Browse Source

PDF.js version 2.7.570 - See https://github.com/mozilla/pdf.js/releases/tag/v2.7.570

pdfjsbot 4 years ago
parent
commit
7bfceb110b
100 changed files with 9021 additions and 10196 deletions
  1. 3 3
      README.md
  2. 1 1
      bower.json
  3. 320 219
      build/pdf.js
  4. 0 0
      build/pdf.js.map
  5. 0 0
      build/pdf.min.js
  6. 438 179
      build/pdf.worker.js
  7. 0 0
      build/pdf.worker.js.map
  8. 0 0
      build/pdf.worker.min.js
  9. 1 0
      es5/build/pdf.d.ts
  10. 319 527
      es5/build/pdf.js
  11. 0 0
      es5/build/pdf.js.map
  12. 0 0
      es5/build/pdf.min.js
  13. 277 450
      es5/build/pdf.worker.js
  14. 0 0
      es5/build/pdf.worker.js.map
  15. 0 0
      es5/build/pdf.worker.min.js
  16. 216 392
      es5/image_decoders/pdf.image_decoders.js
  17. 0 0
      es5/image_decoders/pdf.image_decoders.js.map
  18. 0 0
      es5/image_decoders/pdf.image_decoders.min.js
  19. 18 15
      es5/web/pdf_viewer.css
  20. 340 316
      es5/web/pdf_viewer.js
  21. 0 0
      es5/web/pdf_viewer.js.map
  22. 723 145
      image_decoders/pdf.image_decoders.js
  23. 0 0
      image_decoders/pdf.image_decoders.js.map
  24. 0 0
      image_decoders/pdf.image_decoders.min.js
  25. 491 102
      lib/core/annotation.js
  26. 7 1
      lib/core/cff_parser.js
  27. 1 1
      lib/core/charsets.js
  28. 20 1
      lib/core/cmap.js
  29. 162 3
      lib/core/core_utils.js
  30. 1 1
      lib/core/crypto.js
  31. 132 0
      lib/core/default_appearance.js
  32. 115 9
      lib/core/document.js
  33. 1 1
      lib/core/encodings.js
  34. 168 74
      lib/core/evaluator.js
  35. 97 11
      lib/core/fonts.js
  36. 5 10
      lib/core/function.js
  37. 7 4529
      lib/core/glyphlist.js
  38. 30 1
      lib/core/image_utils.js
  39. 1 1
      lib/core/jpg.js
  40. 5 5
      lib/core/jpx.js
  41. 1 1
      lib/core/murmurhash3.js
  42. 123 39
      lib/core/obj.js
  43. 4 4
      lib/core/pattern.js
  44. 24 5
      lib/core/primitives.js
  45. 1 1
      lib/core/standard_fonts.js
  46. 5 1
      lib/core/stream.js
  47. 12 1380
      lib/core/unicode.js
  48. 58 17
      lib/core/worker.js
  49. 68 4
      lib/core/writer.js
  50. 607 78
      lib/display/annotation_layer.js
  51. 21 4
      lib/display/annotation_storage.js
  52. 88 18
      lib/display/api.js
  53. 272 242
      lib/display/canvas.js
  54. 11 4
      lib/display/display_utils.js
  55. 3 3
      lib/display/fetch_stream.js
  56. 2 3
      lib/display/font_loader.js
  57. 54 20
      lib/display/metadata.js
  58. 1 1
      lib/display/node_stream.js
  59. 1 1
      lib/display/optional_content_config.js
  60. 109 113
      lib/display/pattern_helper.js
  61. 5 5
      lib/display/svg.js
  62. 54 54
      lib/display/text_layer.js
  63. 4 4
      lib/display/transport_stream.js
  64. 65 68
      lib/display/webgl.js
  65. 9 4
      lib/examples/node/domstubs.js
  66. 2 2
      lib/pdf.js
  67. 84 0
      lib/pdf.sandbox.js
  68. 2 2
      lib/pdf.worker.js
  69. 84 0
      lib/shared/scripting_utils.js
  70. 129 14
      lib/shared/util.js
  71. 112 4
      lib/shared/xml_parser.js
  72. 604 61
      lib/test/unit/annotation_spec.js
  73. 28 10
      lib/test/unit/annotation_storage_spec.js
  74. 190 160
      lib/test/unit/api_spec.js
  75. 6 6
      lib/test/unit/bidi_spec.js
  76. 73 73
      lib/test/unit/cff_parser_spec.js
  77. 2 0
      lib/test/unit/clitests_helper.js
  78. 48 74
      lib/test/unit/cmap_spec.js
  79. 34 0
      lib/test/unit/core_utils_spec.js
  80. 162 199
      lib/test/unit/crypto_spec.js
  81. 7 18
      lib/test/unit/custom_spec.js
  82. 54 0
      lib/test/unit/default_appearance_spec.js
  83. 24 19
      lib/test/unit/display_svg_spec.js
  84. 1 1
      lib/test/unit/display_utils_spec.js
  85. 187 20
      lib/test/unit/document_spec.js
  86. 30 30
      lib/test/unit/evaluator_spec.js
  87. 165 165
      lib/test/unit/function_spec.js
  88. 52 53
      lib/test/unit/jasmine-boot.js
  89. 2 2
      lib/test/unit/metadata_spec.js
  90. 29 16
      lib/test/unit/murmurhash3_spec.js
  91. 21 21
      lib/test/unit/network_spec.js
  92. 131 69
      lib/test/unit/pdf_find_controller_spec.js
  93. 10 10
      lib/test/unit/pdf_find_utils_spec.js
  94. 1104 0
      lib/test/unit/scripting_spec.js
  95. 8 8
      lib/test/unit/stream_spec.js
  96. 16 19
      lib/test/unit/test_utils.js
  97. 11 4
      lib/test/unit/testreporter.js
  98. 23 23
      lib/test/unit/type1_parser_spec.js
  99. 78 35
      lib/test/unit/ui_utils_spec.js
  100. 7 7
      lib/test/unit/unicode_spec.js

+ 3 - 3
README.md

@@ -7,8 +7,8 @@ parsing and rendering PDFs.
 This is a pre-built version of the PDF.js source code. It is automatically
 generated by the build scripts.
 
-For usage with older browsers/environments, without support for modern features
-such as e.g. `async`/`await`, `Promise`, and `ReadableStream`,
-please refer to the `es5` folder.
+For usage with older browsers or environments, without support for modern
+features such as e.g. `async`/`await`, `ReadableStream`, optional chaining, and
+nullish coalescing; please see the `es5` folder.
 
 See https://github.com/mozilla/pdf.js for learning and contributing.

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
   "name": "pdfjs-dist",
-  "version": "2.6.347",
+  "version": "2.7.570",
   "main": [
     "build/pdf.js",
     "build/pdf.worker.js"

File diff suppressed because it is too large
+ 320 - 219
build/pdf.js


File diff suppressed because it is too large
+ 0 - 0
build/pdf.js.map


File diff suppressed because it is too large
+ 0 - 0
build/pdf.min.js


File diff suppressed because it is too large
+ 438 - 179
build/pdf.worker.js


File diff suppressed because it is too large
+ 0 - 0
build/pdf.worker.js.map


File diff suppressed because it is too large
+ 0 - 0
build/pdf.worker.min.js


+ 1 - 0
es5/build/pdf.d.ts

@@ -0,0 +1 @@
+export * from "pdfjs-dist";

File diff suppressed because it is too large
+ 319 - 527
es5/build/pdf.js


File diff suppressed because it is too large
+ 0 - 0
es5/build/pdf.js.map


File diff suppressed because it is too large
+ 0 - 0
es5/build/pdf.min.js


File diff suppressed because it is too large
+ 277 - 450
es5/build/pdf.worker.js


File diff suppressed because it is too large
+ 0 - 0
es5/build/pdf.worker.js.map


File diff suppressed because it is too large
+ 0 - 0
es5/build/pdf.worker.min.js


File diff suppressed because it is too large
+ 216 - 392
es5/image_decoders/pdf.image_decoders.js


File diff suppressed because it is too large
+ 0 - 0
es5/image_decoders/pdf.image_decoders.js.map


File diff suppressed because it is too large
+ 0 - 0
es5/image_decoders/pdf.image_decoders.min.js


+ 18 - 15
es5/web/pdf_viewer.css

@@ -40,15 +40,15 @@
 }
 
 .textLayer .highlight.begin {
-  border-radius: 4px 0px 0px 4px;
+  border-radius: 4px 0 0 4px;
 }
 
 .textLayer .highlight.end {
-  border-radius: 0px 4px 4px 0px;
+  border-radius: 0 4px 4px 0;
 }
 
 .textLayer .highlight.middle {
-  border-radius: 0px;
+  border-radius: 0;
 }
 
 .textLayer .highlight.selected {
@@ -66,10 +66,10 @@
 .textLayer .endOfContent {
   display: block;
   position: absolute;
-  left: 0px;
+  left: 0;
   top: 100%;
-  right: 0px;
-  bottom: 0px;
+  right: 0;
+  bottom: 0;
   z-index: -1;
   cursor: default;
   -webkit-user-select: none;
@@ -79,12 +79,13 @@
 }
 
 .textLayer .endOfContent.active {
-  top: 0px;
+  top: 0;
 }
 
 
 .annotationLayer section {
   position: absolute;
+  text-align: initial;
 }
 
 .annotationLayer .linkAnnotation > a,
@@ -101,7 +102,7 @@
 .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
   opacity: 0.2;
   background: rgba(255, 255, 0, 1);
-  box-shadow: 0px 2px 10px rgba(255, 255, 0, 1);
+  box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
 }
 
 .annotationLayer .textAnnotation img {
@@ -230,13 +231,14 @@
   z-index: 200;
   max-width: 20em;
   background-color: rgba(255, 255, 153, 1);
-  box-shadow: 0px 2px 5px rgba(136, 136, 136, 1);
+  box-shadow: 0 2px 5px rgba(136, 136, 136, 1);
   border-radius: 2px;
   padding: 6px;
   margin-left: 5px;
   cursor: pointer;
   font: message-box;
   font-size: 9px;
+  white-space: normal;
   word-wrap: break-word;
 }
 
@@ -284,7 +286,7 @@
   direction: ltr;
   width: 816px;
   height: 1056px;
-  margin: 1px auto -8px auto;
+  margin: 1px auto -8px;
   position: relative;
   overflow: visible;
   border: 9px solid transparent;
@@ -295,7 +297,7 @@
 }
 
 .pdfViewer.removePageBorders .page {
-  margin: 0px auto 10px auto;
+  margin: 0 auto 10px;
   border: none;
 }
 
@@ -386,10 +388,6 @@
   margin-right: auto;
 }
 
-.pdfPresentationMode:-ms-fullscreen .pdfViewer .page {
-  margin-bottom: 100% !important;
-}
-
 .pdfPresentationMode:-webkit-full-screen .pdfViewer .page {
   margin-bottom: 100%;
   border: 0;
@@ -400,6 +398,11 @@
   border: 0;
 }
 
+.pdfPresentationMode:-ms-fullscreen .pdfViewer .page {
+  margin-bottom: 100%;
+  border: 0;
+}
+
 .pdfPresentationMode:fullscreen .pdfViewer .page {
   margin-bottom: 100%;
   border: 0;

File diff suppressed because it is too large
+ 340 - 316
es5/web/pdf_viewer.js


File diff suppressed because it is too large
+ 0 - 0
es5/web/pdf_viewer.js.map


File diff suppressed because it is too large
+ 723 - 145
image_decoders/pdf.image_decoders.js


File diff suppressed because it is too large
+ 0 - 0
image_decoders/pdf.image_decoders.js.map


File diff suppressed because it is too large
+ 0 - 0
image_decoders/pdf.image_decoders.min.js


File diff suppressed because it is too large
+ 491 - 102
lib/core/annotation.js


+ 7 - 1
lib/core/cff_parser.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.CFFFDSelect = exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = void 0;
+exports.CFFTopDict = exports.CFFStrings = exports.CFFStandardStrings = exports.CFFPrivateDict = exports.CFFParser = exports.CFFIndex = exports.CFFHeader = exports.CFFFDSelect = exports.CFFCompiler = exports.CFFCharset = exports.CFF = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -1387,6 +1387,12 @@ class CFFCompiler {
       }
     }
 
+    const xuid = cff.topDict.getByName("XUID");
+
+    if (xuid && xuid.length > 16) {
+      cff.topDict.removeByName("XUID");
+    }
+
     cff.topDict.setByName("charset", 0);
     var compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont);
     output.add(compiled.output);

File diff suppressed because it is too large
+ 1 - 1
lib/core/charsets.js


+ 20 - 1
lib/core/cmap.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.CMapFactory = exports.IdentityCMap = exports.CMap = void 0;
+exports.IdentityCMap = exports.CMapFactory = exports.CMap = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -165,6 +165,25 @@ class CMap {
     out.length = 1;
   }
 
+  getCharCodeLength(charCode) {
+    const codespaceRanges = this.codespaceRanges;
+
+    for (let n = 0, nn = codespaceRanges.length; n < nn; n++) {
+      const codespaceRange = codespaceRanges[n];
+
+      for (let k = 0, kk = codespaceRange.length; k < kk;) {
+        const low = codespaceRange[k++];
+        const high = codespaceRange[k++];
+
+        if (charCode >= low && charCode <= high) {
+          return n + 1;
+        }
+      }
+    }
+
+    return 1;
+  }
+
   get length() {
     return this._map.length;
   }

+ 162 - 3
lib/core/core_utils.js

@@ -24,18 +24,24 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.getLookupTableFactory = getLookupTableFactory;
+exports.collectActions = collectActions;
+exports.escapePDFName = escapePDFName;
+exports.getArrayLookupTableFactory = getArrayLookupTableFactory;
 exports.getInheritableProperty = getInheritableProperty;
-exports.toRomanNumerals = toRomanNumerals;
+exports.getLookupTableFactory = getLookupTableFactory;
+exports.isWhiteSpace = isWhiteSpace;
 exports.log2 = log2;
+exports.parseXFAPath = parseXFAPath;
 exports.readInt8 = readInt8;
 exports.readUint16 = readUint16;
 exports.readUint32 = readUint32;
-exports.isWhiteSpace = isWhiteSpace;
+exports.toRomanNumerals = toRomanNumerals;
 exports.XRefParseException = exports.XRefEntryException = exports.MissingDataException = void 0;
 
 var _util = require("../shared/util.js");
 
+var _primitives = require("./primitives.js");
+
 function getLookupTableFactory(initializer) {
   let lookup;
   return function () {
@@ -49,6 +55,25 @@ function getLookupTableFactory(initializer) {
   };
 }
 
+function getArrayLookupTableFactory(initializer) {
+  let lookup;
+  return function () {
+    if (initializer) {
+      let arr = initializer();
+      initializer = null;
+      lookup = Object.create(null);
+
+      for (let i = 0, ii = arr.length; i < ii; i += 2) {
+        lookup[arr[i]] = arr[i + 1];
+      }
+
+      arr = null;
+    }
+
+    return lookup;
+  };
+}
+
 class MissingDataException extends _util.BaseException {
   constructor(begin, end) {
     super(`Missing data [${begin}, ${end})`);
@@ -149,4 +174,138 @@ function readUint32(data, offset) {
 
 function isWhiteSpace(ch) {
   return ch === 0x20 || ch === 0x09 || ch === 0x0d || ch === 0x0a;
+}
+
+function parseXFAPath(path) {
+  const positionPattern = /(.+)\[([0-9]+)\]$/;
+  return path.split(".").map(component => {
+    const m = component.match(positionPattern);
+
+    if (m) {
+      return {
+        name: m[1],
+        pos: parseInt(m[2], 10)
+      };
+    }
+
+    return {
+      name: component,
+      pos: 0
+    };
+  });
+}
+
+function escapePDFName(str) {
+  const buffer = [];
+  let start = 0;
+
+  for (let i = 0, ii = str.length; i < ii; i++) {
+    const char = str.charCodeAt(i);
+
+    if (char < 0x21 || char > 0x7e || char === 0x23 || char === 0x28 || char === 0x29 || char === 0x3c || char === 0x3e || char === 0x5b || char === 0x5d || char === 0x7b || char === 0x7d || char === 0x2f || char === 0x25) {
+      if (start < i) {
+        buffer.push(str.substring(start, i));
+      }
+
+      buffer.push(`#${char.toString(16)}`);
+      start = i + 1;
+    }
+  }
+
+  if (buffer.length === 0) {
+    return str;
+  }
+
+  if (start < str.length) {
+    buffer.push(str.substring(start, str.length));
+  }
+
+  return buffer.join("");
+}
+
+function _collectJS(entry, xref, list, parents) {
+  if (!entry) {
+    return;
+  }
+
+  let parent = null;
+
+  if ((0, _primitives.isRef)(entry)) {
+    if (parents.has(entry)) {
+      return;
+    }
+
+    parent = entry;
+    parents.put(parent);
+    entry = xref.fetch(entry);
+  }
+
+  if (Array.isArray(entry)) {
+    for (const element of entry) {
+      _collectJS(element, xref, list, parents);
+    }
+  } else if (entry instanceof _primitives.Dict) {
+    if ((0, _primitives.isName)(entry.get("S"), "JavaScript") && entry.has("JS")) {
+      const js = entry.get("JS");
+      let code;
+
+      if ((0, _primitives.isStream)(js)) {
+        code = (0, _util.bytesToString)(js.getBytes());
+      } else {
+        code = js;
+      }
+
+      code = (0, _util.stringToPDFString)(code);
+
+      if (code) {
+        list.push(code);
+      }
+    }
+
+    _collectJS(entry.getRaw("Next"), xref, list, parents);
+  }
+
+  if (parent) {
+    parents.remove(parent);
+  }
+}
+
+function collectActions(xref, dict, eventType) {
+  const actions = Object.create(null);
+
+  if (dict.has("AA")) {
+    const additionalActions = dict.get("AA");
+
+    for (const key of additionalActions.getKeys()) {
+      const action = eventType[key];
+
+      if (!action) {
+        continue;
+      }
+
+      const actionDict = additionalActions.getRaw(key);
+      const parents = new _primitives.RefSet();
+      const list = [];
+
+      _collectJS(actionDict, xref, list, parents);
+
+      if (list.length > 0) {
+        actions[action] = list;
+      }
+    }
+  }
+
+  if (dict.has("A")) {
+    const actionDict = dict.get("A");
+    const parents = new _primitives.RefSet();
+    const list = [];
+
+    _collectJS(actionDict, xref, list, parents);
+
+    if (list.length > 0) {
+      actions.Action = list;
+    }
+  }
+
+  return (0, _util.objectSize)(actions) > 0 ? actions : null;
 }

+ 1 - 1
lib/core/crypto.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.calculateSHA512 = exports.calculateSHA384 = exports.calculateSHA256 = exports.calculateMD5 = exports.PDF20 = exports.PDF17 = exports.CipherTransformFactory = exports.ARCFourCipher = exports.AES256Cipher = exports.AES128Cipher = void 0;
+exports.PDF20 = exports.PDF17 = exports.CipherTransformFactory = exports.calculateSHA512 = exports.calculateSHA384 = exports.calculateSHA256 = exports.calculateMD5 = exports.ARCFourCipher = exports.AES256Cipher = exports.AES128Cipher = void 0;
 
 var _util = require("../shared/util.js");
 

+ 132 - 0
lib/core/default_appearance.js

@@ -0,0 +1,132 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2020 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.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.createDefaultAppearance = createDefaultAppearance;
+exports.parseDefaultAppearance = parseDefaultAppearance;
+
+var _primitives = require("./primitives.js");
+
+var _util = require("../shared/util.js");
+
+var _colorspace = require("./colorspace.js");
+
+var _core_utils = require("./core_utils.js");
+
+var _evaluator = require("./evaluator.js");
+
+var _stream = require("./stream.js");
+
+class DefaultAppearanceEvaluator extends _evaluator.EvaluatorPreprocessor {
+  constructor(str) {
+    super(new _stream.StringStream(str));
+  }
+
+  parse() {
+    const operation = {
+      fn: 0,
+      args: []
+    };
+    const result = {
+      fontSize: 0,
+      fontName: _primitives.Name.get(""),
+      fontColor: new Uint8ClampedArray([0, 0, 0])
+    };
+
+    try {
+      while (true) {
+        operation.args.length = 0;
+
+        if (!this.read(operation)) {
+          break;
+        }
+
+        if (this.savedStatesDepth !== 0) {
+          continue;
+        }
+
+        const {
+          fn,
+          args
+        } = operation;
+
+        switch (fn | 0) {
+          case _util.OPS.setFont:
+            const [fontName, fontSize] = args;
+
+            if ((0, _primitives.isName)(fontName)) {
+              result.fontName = fontName;
+            }
+
+            if (typeof fontSize === "number" && fontSize > 0) {
+              result.fontSize = fontSize;
+            }
+
+            break;
+
+          case _util.OPS.setFillRGBColor:
+            _colorspace.ColorSpace.singletons.rgb.getRgbItem(args, 0, result.fontColor, 0);
+
+            break;
+
+          case _util.OPS.setFillGray:
+            _colorspace.ColorSpace.singletons.gray.getRgbItem(args, 0, result.fontColor, 0);
+
+            break;
+
+          case _util.OPS.setFillColorSpace:
+            _colorspace.ColorSpace.singletons.cmyk.getRgbItem(args, 0, result.fontColor, 0);
+
+            break;
+        }
+      }
+    } catch (reason) {
+      (0, _util.warn)(`parseDefaultAppearance - ignoring errors: "${reason}".`);
+    }
+
+    return result;
+  }
+
+}
+
+function parseDefaultAppearance(str) {
+  return new DefaultAppearanceEvaluator(str).parse();
+}
+
+function createDefaultAppearance({
+  fontSize,
+  fontName,
+  fontColor
+}) {
+  let colorCmd;
+
+  if (fontColor.every(c => c === 0)) {
+    colorCmd = "0 g";
+  } else {
+    colorCmd = Array.from(fontColor).map(c => (c / 255).toFixed(2)).join(" ") + " rg";
+  }
+
+  return `/${(0, _core_utils.escapePDFName)(fontName.name)} ${fontSize} Tf ${colorCmd}`;
+}

+ 115 - 9
lib/core/document.js

@@ -63,7 +63,8 @@ class Page {
     globalIdFactory,
     fontCache,
     builtInCMapCache,
-    globalImageCache
+    globalImageCache,
+    nonBlendModesSet
   }) {
     this.pdfManager = pdfManager;
     this.pageIndex = pageIndex;
@@ -73,6 +74,7 @@ class Page {
     this.fontCache = fontCache;
     this.builtInCMapCache = builtInCMapCache;
     this.globalImageCache = globalImageCache;
+    this.nonBlendModesSet = nonBlendModesSet;
     this.evaluatorOptions = pdfManager.evaluatorOptions;
     this.resourcesPromise = null;
     const idCounters = {
@@ -270,7 +272,7 @@ class Page {
     const pageListPromise = dataPromises.then(([contentStream]) => {
       const opList = new _operator_list.OperatorList(intent, sink);
       handler.send("StartRenderPage", {
-        transparency: partialEvaluator.hasBlendModes(this.resources),
+        transparency: partialEvaluator.hasBlendModes(this.resources, this.nonBlendModesSet),
         pageIndex: this.pageIndex,
         intent
       });
@@ -294,7 +296,7 @@ class Page {
       const opListPromises = [];
 
       for (const annotation of annotations) {
-        if (isAnnotationRenderable(annotation, intent)) {
+        if (isAnnotationRenderable(annotation, intent) && !annotation.isHidden(annotationStorage)) {
           opListPromises.push(annotation.getOperatorList(partialEvaluator, task, renderInteractiveForms, annotationStorage).catch(function (reason) {
             (0, _util.warn)("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
             return null;
@@ -365,7 +367,9 @@ class Page {
   }
 
   get annotations() {
-    return (0, _util.shadow)(this, "annotations", this._getInheritableProperty("Annots") || []);
+    const annots = this._getInheritableProperty("Annots");
+
+    return (0, _util.shadow)(this, "annotations", Array.isArray(annots) ? annots : []);
   }
 
   get _parsedAnnotations() {
@@ -386,6 +390,11 @@ class Page {
     return (0, _util.shadow)(this, "_parsedAnnotations", parsedAnnotations);
   }
 
+  get jsActions() {
+    const actions = (0, _core_utils.collectActions)(this.xref, this.pageDict, _util.PageActionEventType);
+    return (0, _util.shadow)(this, "jsActions", actions);
+  }
+
 }
 
 exports.Page = Page;
@@ -601,9 +610,18 @@ class PDFDocument {
 
   _hasOnlyDocumentSignatures(fields, recursionDepth = 0) {
     const RECURSION_LIMIT = 10;
+
+    if (!Array.isArray(fields)) {
+      return false;
+    }
+
     return fields.every(field => {
       field = this.xref.fetchIfRef(field);
 
+      if (!(field instanceof _primitives.Dict)) {
+        return false;
+      }
+
       if (field.has("Kids")) {
         if (++recursionDepth > RECURSION_LIMIT) {
           (0, _util.warn)("_hasOnlyDocumentSignatures: maximum recursion depth reached");
@@ -622,6 +640,7 @@ class PDFDocument {
 
   get formInfo() {
     const formInfo = {
+      hasFields: false,
       hasAcroForm: false,
       hasXfa: false
     };
@@ -632,11 +651,11 @@ class PDFDocument {
     }
 
     try {
-      const xfa = acroForm.get("XFA");
-      const hasXfa = Array.isArray(xfa) && xfa.length > 0 || (0, _primitives.isStream)(xfa) && !xfa.isEmpty;
-      formInfo.hasXfa = hasXfa;
       const fields = acroForm.get("Fields");
       const hasFields = Array.isArray(fields) && fields.length > 0;
+      formInfo.hasFields = hasFields;
+      const xfa = acroForm.get("XFA");
+      formInfo.hasXfa = Array.isArray(xfa) && xfa.length > 0 || (0, _primitives.isStream)(xfa) && !xfa.isEmpty;
       const sigFlags = acroForm.get("SigFlags");
 
       const hasOnlyDocumentSignatures = !!(sigFlags & 0x1) && this._hasOnlyDocumentSignatures(fields);
@@ -647,7 +666,7 @@ class PDFDocument {
         throw ex;
       }
 
-      (0, _util.info)("Cannot fetch form information.");
+      (0, _util.warn)(`Cannot fetch form information: "${ex}".`);
     }
 
     return (0, _util.shadow)(this, "formInfo", formInfo);
@@ -789,7 +808,8 @@ class PDFDocument {
         globalIdFactory: this._globalIdFactory,
         fontCache: catalog.fontCache,
         builtInCMapCache: catalog.builtInCMapCache,
-        globalImageCache: catalog.globalImageCache
+        globalImageCache: catalog.globalImageCache,
+        nonBlendModesSet: catalog.nonBlendModesSet
       });
     });
   }
@@ -812,6 +832,92 @@ class PDFDocument {
     return this.catalog ? this.catalog.cleanup(manuallyTriggered) : (0, _primitives.clearPrimitiveCaches)();
   }
 
+  _collectFieldObjects(name, fieldRef, promises) {
+    const field = this.xref.fetchIfRef(fieldRef);
+
+    if (field.has("T")) {
+      const partName = (0, _util.stringToPDFString)(field.get("T"));
+
+      if (name === "") {
+        name = partName;
+      } else {
+        name = `${name}.${partName}`;
+      }
+    }
+
+    if (!promises.has(name)) {
+      promises.set(name, []);
+    }
+
+    promises.get(name).push(_annotation.AnnotationFactory.create(this.xref, fieldRef, this.pdfManager, this._localIdFactory).then(annotation => annotation && annotation.getFieldObject()).catch(function (reason) {
+      (0, _util.warn)(`_collectFieldObjects: "${reason}".`);
+      return null;
+    }));
+
+    if (field.has("Kids")) {
+      const kids = field.get("Kids");
+
+      for (const kid of kids) {
+        this._collectFieldObjects(name, kid, promises);
+      }
+    }
+  }
+
+  get fieldObjects() {
+    if (!this.formInfo.hasFields) {
+      return (0, _util.shadow)(this, "fieldObjects", Promise.resolve(null));
+    }
+
+    const allFields = Object.create(null);
+    const fieldPromises = new Map();
+
+    for (const fieldRef of this.catalog.acroForm.get("Fields")) {
+      this._collectFieldObjects("", fieldRef, fieldPromises);
+    }
+
+    const allPromises = [];
+
+    for (const [name, promises] of fieldPromises) {
+      allPromises.push(Promise.all(promises).then(fields => {
+        fields = fields.filter(field => !!field);
+
+        if (fields.length > 0) {
+          allFields[name] = fields;
+        }
+      }));
+    }
+
+    return (0, _util.shadow)(this, "fieldObjects", Promise.all(allPromises).then(() => allFields));
+  }
+
+  get hasJSActions() {
+    return (0, _util.shadow)(this, "hasJSActions", this.fieldObjects.then(fieldObjects => {
+      return fieldObjects !== null && Object.values(fieldObjects).some(fieldObject => fieldObject.some(object => object.actions !== null)) || !!this.catalog.jsActions;
+    }));
+  }
+
+  get calculationOrderIds() {
+    const acroForm = this.catalog.acroForm;
+
+    if (!acroForm || !acroForm.has("CO")) {
+      return (0, _util.shadow)(this, "calculationOrderIds", null);
+    }
+
+    const calculationOrder = acroForm.get("CO");
+
+    if (!Array.isArray(calculationOrder) || calculationOrder.length === 0) {
+      return (0, _util.shadow)(this, "calculationOrderIds", null);
+    }
+
+    const ids = calculationOrder.filter(_primitives.isRef).map(ref => ref.toString());
+
+    if (ids.length === 0) {
+      return (0, _util.shadow)(this, "calculationOrderIds", null);
+    }
+
+    return (0, _util.shadow)(this, "calculationOrderIds", ids);
+  }
+
 }
 
 exports.PDFDocument = PDFDocument;

File diff suppressed because it is too large
+ 1 - 1
lib/core/encodings.js


+ 168 - 74
lib/core/evaluator.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.PartialEvaluator = void 0;
+exports.PartialEvaluator = exports.EvaluatorPreprocessor = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -226,12 +226,16 @@ class PartialEvaluator {
     return newEvaluator;
   }
 
-  hasBlendModes(resources) {
+  hasBlendModes(resources, nonBlendModesSet) {
     if (!(resources instanceof _primitives.Dict)) {
       return false;
     }
 
-    const processed = new _primitives.RefSet();
+    if (resources.objId && nonBlendModesSet.has(resources.objId)) {
+      return false;
+    }
+
+    const processed = new _primitives.RefSet(nonBlendModesSet);
 
     if (resources.objId) {
       processed.put(resources.objId);
@@ -343,6 +347,9 @@ class PartialEvaluator {
       }
     }
 
+    processed.forEach(ref => {
+      nonBlendModesSet.put(ref);
+    });
     return false;
   }
 
@@ -680,7 +687,7 @@ class PartialEvaluator {
     return transferMaps;
   }
 
-  handleTilingType(fn, args, resources, pattern, patternDict, operatorList, task) {
+  handleTilingType(fn, color, resources, pattern, patternDict, operatorList, task, cacheKey, localTilingPatternCache) {
     const tilingOpList = new _operator_list.OperatorList();
 
     const patternResources = _primitives.Dict.merge({
@@ -694,14 +701,18 @@ class PartialEvaluator {
       resources: patternResources,
       operatorList: tilingOpList
     }).then(function () {
-      return (0, _pattern.getTilingPatternIR)({
-        fnArray: tilingOpList.fnArray,
-        argsArray: tilingOpList.argsArray
-      }, patternDict, args);
-    }).then(function (tilingPatternIR) {
+      const operatorListIR = tilingOpList.getIR();
+      const tilingPatternIR = (0, _pattern.getTilingPatternIR)(operatorListIR, patternDict, color);
       operatorList.addDependencies(tilingOpList.dependencies);
       operatorList.addOp(fn, tilingPatternIR);
-    }, reason => {
+
+      if (cacheKey) {
+        localTilingPatternCache.set(cacheKey, patternDict.objId, {
+          operatorListIR,
+          dict: patternDict
+        });
+      }
+    }).catch(reason => {
       if (reason instanceof _util.AbortException) {
         return;
       }
@@ -718,17 +729,15 @@ class PartialEvaluator {
     });
   }
 
-  handleSetFont(resources, fontArgs, fontRef, operatorList, task, state) {
-    var fontName,
-        fontSize = 0;
+  handleSetFont(resources, fontArgs, fontRef, operatorList, task, state, fallbackFontDict = null) {
+    var fontName;
 
     if (fontArgs) {
       fontArgs = fontArgs.slice();
       fontName = fontArgs[0].name;
-      fontSize = fontArgs[1];
     }
 
-    return this.loadFont(fontName, fontRef, resources).then(translated => {
+    return this.loadFont(fontName, fontRef, resources, fallbackFontDict).then(translated => {
       if (!translated.font.isType3Font) {
         return translated;
       }
@@ -749,8 +758,6 @@ class PartialEvaluator {
       });
     }).then(translated => {
       state.font = translated.font;
-      state.fontSize = fontSize;
-      state.fontName = fontName;
       translated.send(this.handler);
       return translated.loadedName;
     });
@@ -895,14 +902,14 @@ class PartialEvaluator {
     });
   }
 
-  loadFont(fontName, font, resources) {
-    const errorFont = () => {
-      return Promise.resolve(new TranslatedFont({
+  loadFont(fontName, font, resources, fallbackFontDict = null) {
+    const errorFont = async () => {
+      return new TranslatedFont({
         loadedName: "g_font_error",
         font: new _fonts.ErrorFont(`Font "${fontName}" is not available.`),
         dict: font,
         extraProperties: this.options.fontExtraProperties
-      }));
+      });
     };
 
     var fontRef,
@@ -934,7 +941,12 @@ class PartialEvaluator {
         featureId: _util.UNSUPPORTED_FEATURES.errorFontMissing
       });
       (0, _util.warn)(`${partialMsg} -- attempting to fallback to a default font.`);
-      fontRef = PartialEvaluator.fallbackFontDict;
+
+      if (fallbackFontDict) {
+        fontRef = fallbackFontDict;
+      } else {
+        fontRef = PartialEvaluator.fallbackFontDict;
+      }
     }
 
     if (this.fontCache.has(fontRef)) {
@@ -947,12 +959,20 @@ class PartialEvaluator {
       return errorFont();
     }
 
-    if (font.translated) {
-      return font.translated;
+    if (font.cacheKey && this.fontCache.has(font.cacheKey)) {
+      return this.fontCache.get(font.cacheKey);
     }
 
     var fontCapability = (0, _util.createPromiseCapability)();
-    var preEvaluatedFont = this.preEvaluateFont(font);
+    let preEvaluatedFont;
+
+    try {
+      preEvaluatedFont = this.preEvaluateFont(font);
+    } catch (reason) {
+      (0, _util.warn)(`loadFont - ignoring preEvaluateFont errors: "${reason}".`);
+      return errorFont();
+    }
+
     const {
       descriptor,
       hash
@@ -998,21 +1018,13 @@ class PartialEvaluator {
         fontID = this.idFactory.createFontId();
       }
 
-      this.fontCache.put(`id_${fontID}`, fontCapability.promise);
+      font.cacheKey = `cacheKey_${fontID}`;
+      this.fontCache.put(font.cacheKey, fontCapability.promise);
     }
 
     (0, _util.assert)(fontID && fontID.startsWith("f"), 'The "fontID" must be (correctly) defined.');
     font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
-    font.translated = fontCapability.promise;
-    var translatedPromise;
-
-    try {
-      translatedPromise = this.translateFont(preEvaluatedFont);
-    } catch (e) {
-      translatedPromise = Promise.reject(e);
-    }
-
-    translatedPromise.then(translatedFont => {
+    this.translateFont(preEvaluatedFont).then(translatedFont => {
       if (translatedFont.fontType !== undefined) {
         var xrefFontStats = xref.stats.fontTypes;
         xrefFontStats[translatedFont.fontType] = true;
@@ -1100,26 +1112,45 @@ class PartialEvaluator {
     });
   }
 
-  async handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache) {
-    var patternName = args[args.length - 1];
-    var pattern;
+  handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache) {
+    const patternName = args.pop();
 
-    if ((0, _primitives.isName)(patternName) && (pattern = patterns.get(patternName.name))) {
-      var dict = (0, _primitives.isStream)(pattern) ? pattern.dict : pattern;
-      var typeNum = dict.get("PatternType");
+    if (patternName instanceof _primitives.Name) {
+      const name = patternName.name;
+      const localTilingPattern = localTilingPatternCache.getByName(name);
 
-      if (typeNum === PatternType.TILING) {
-        var color = cs.base ? cs.base.getRgb(args, 0) : null;
-        return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task);
-      } else if (typeNum === PatternType.SHADING) {
-        var shading = dict.get("Shading");
-        var matrix = dict.getArray("Matrix");
-        pattern = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this._pdfFunctionFactory, localColorSpaceCache);
-        operatorList.addOp(fn, pattern.getIR());
-        return undefined;
+      if (localTilingPattern) {
+        try {
+          const color = cs.base ? cs.base.getRgb(args, 0) : null;
+          const tilingPatternIR = (0, _pattern.getTilingPatternIR)(localTilingPattern.operatorListIR, localTilingPattern.dict, color);
+          operatorList.addOp(fn, tilingPatternIR);
+          return undefined;
+        } catch (ex) {
+          if (ex instanceof _core_utils.MissingDataException) {
+            throw ex;
+          }
+        }
       }
 
-      throw new _util.FormatError(`Unknown PatternType: ${typeNum}`);
+      let pattern = patterns.get(name);
+
+      if (pattern) {
+        var dict = (0, _primitives.isStream)(pattern) ? pattern.dict : pattern;
+        var typeNum = dict.get("PatternType");
+
+        if (typeNum === PatternType.TILING) {
+          const color = cs.base ? cs.base.getRgb(args, 0) : null;
+          return this.handleTilingType(fn, color, resources, pattern, dict, operatorList, task, name, localTilingPatternCache);
+        } else if (typeNum === PatternType.SHADING) {
+          var shading = dict.get("Shading");
+          var matrix = dict.getArray("Matrix");
+          pattern = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this._pdfFunctionFactory, localColorSpaceCache);
+          operatorList.addOp(fn, pattern.getIR());
+          return undefined;
+        }
+
+        throw new _util.FormatError(`Unknown PatternType: ${typeNum}`);
+      }
     }
 
     throw new _util.FormatError(`Unknown PatternName: ${patternName}`);
@@ -1186,7 +1217,8 @@ class PartialEvaluator {
     task,
     resources,
     operatorList,
-    initialState = null
+    initialState = null,
+    fallbackFontDict = null
   }) {
     resources = resources || _primitives.Dict.empty;
     initialState = initialState || new EvalState();
@@ -1201,6 +1233,7 @@ class PartialEvaluator {
     const localImageCache = new _image_utils.LocalImageCache();
     const localColorSpaceCache = new _image_utils.LocalColorSpaceCache();
     const localGStateCache = new _image_utils.LocalGStateCache();
+    const localTilingPatternCache = new _image_utils.LocalTilingPatternCache();
 
     var xobjs = resources.get("XObject") || _primitives.Dict.empty;
 
@@ -1341,7 +1374,7 @@ class PartialEvaluator {
 
           case _util.OPS.setFont:
             var fontSize = args[1];
-            next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state).then(function (loadedName) {
+            next(self.handleSetFont(resources, args, null, operatorList, task, stateManager.state, fallbackFontDict).then(function (loadedName) {
               operatorList.addDependency(loadedName);
               operatorList.addOp(_util.OPS.setFont, [loadedName, fontSize]);
             }));
@@ -1533,7 +1566,7 @@ class PartialEvaluator {
             cs = stateManager.state.fillColorSpace;
 
             if (cs.name === "Pattern") {
-              next(self.handleColorN(operatorList, _util.OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache));
+              next(self.handleColorN(operatorList, _util.OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache));
               return;
             }
 
@@ -1545,7 +1578,7 @@ class PartialEvaluator {
             cs = stateManager.state.strokeColorSpace;
 
             if (cs.name === "Pattern") {
-              next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache));
+              next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache));
               return;
             }
 
@@ -2875,6 +2908,11 @@ class PartialEvaluator {
       }
 
       dict = Array.isArray(df) ? this.xref.fetchIfRef(df[0]) : df;
+
+      if (!(dict instanceof _primitives.Dict)) {
+        throw new _util.FormatError("Descendant font is not a dictionary.");
+      }
+
       type = dict.get("Subtype");
 
       if (!(0, _primitives.isName)(type)) {
@@ -2950,7 +2988,7 @@ class PartialEvaluator {
     };
   }
 
-  translateFont(preEvaluatedFont) {
+  async translateFont(preEvaluatedFont) {
     var baseDict = preEvaluatedFont.baseDict;
     var dict = preEvaluatedFont.dict;
     var composite = preEvaluatedFont.composite;
@@ -3022,7 +3060,7 @@ class PartialEvaluator {
       var baseFontStr = baseFont && baseFont.name;
 
       if (fontNameStr !== baseFontStr) {
-        (0, _util.info)(`The FontDescriptor\'s FontName is "${fontNameStr}" but ` + `should be the same as the Font\'s BaseFont "${baseFontStr}".`);
+        (0, _util.info)(`The FontDescriptor's FontName is "${fontNameStr}" but ` + `should be the same as the Font's BaseFont "${baseFontStr}".`);
 
         if (fontNameStr && baseFontStr && baseFontStr.startsWith(fontNameStr)) {
           fontName = baseFont;
@@ -3075,30 +3113,24 @@ class PartialEvaluator {
       italicAngle: descriptor.get("ItalicAngle"),
       isType3Font: false
     };
-    var cMapPromise;
 
     if (composite) {
-      var cidEncoding = baseDict.get("Encoding");
+      const cidEncoding = baseDict.get("Encoding");
 
       if ((0, _primitives.isName)(cidEncoding)) {
         properties.cidEncoding = cidEncoding.name;
       }
 
-      cMapPromise = _cmap.CMapFactory.create({
+      const cMap = await _cmap.CMapFactory.create({
         encoding: cidEncoding,
         fetchBuiltInCMap: this._fetchBuiltInCMapBound,
         useCMap: null
-      }).then(function (cMap) {
-        properties.cMap = cMap;
-        properties.vertical = properties.cMap.vertical;
       });
-    } else {
-      cMapPromise = Promise.resolve(undefined);
+      properties.cMap = cMap;
+      properties.vertical = properties.cMap.vertical;
     }
 
-    return cMapPromise.then(() => {
-      return this.extractDataStructures(dict, baseDict, properties);
-    }).then(newProperties => {
+    return this.extractDataStructures(dict, baseDict, properties).then(newProperties => {
       this.extractWidths(dict, descriptor, newProperties);
 
       if (type === "Type3") {
@@ -3197,7 +3229,7 @@ class TranslatedFont {
     var charProcOperatorList = Object.create(null);
 
     for (const key of charProcs.getKeys()) {
-      loadCharProcsPromise = loadCharProcsPromise.then(function () {
+      loadCharProcsPromise = loadCharProcsPromise.then(() => {
         var glyphStream = charProcs.get(key);
         var operatorList = new _operator_list.OperatorList();
         return type3Evaluator.getOperatorList({
@@ -3205,7 +3237,11 @@ class TranslatedFont {
           task,
           resources: fontResources,
           operatorList
-        }).then(function () {
+        }).then(() => {
+          if (operatorList.fnArray[0] === _util.OPS.setCharWidthAndBounds) {
+            this._removeType3ColorOperators(operatorList);
+          }
+
           charProcOperatorList[key] = operatorList.getIR();
 
           for (const dependency of operatorList.dependencies) {
@@ -3225,10 +3261,66 @@ class TranslatedFont {
     return this.type3Loaded;
   }
 
+  _removeType3ColorOperators(operatorList) {
+    let i = 1,
+        ii = operatorList.length;
+
+    while (i < ii) {
+      switch (operatorList.fnArray[i]) {
+        case _util.OPS.setStrokeColorSpace:
+        case _util.OPS.setFillColorSpace:
+        case _util.OPS.setStrokeColor:
+        case _util.OPS.setStrokeColorN:
+        case _util.OPS.setFillColor:
+        case _util.OPS.setFillColorN:
+        case _util.OPS.setStrokeGray:
+        case _util.OPS.setFillGray:
+        case _util.OPS.setStrokeRGBColor:
+        case _util.OPS.setFillRGBColor:
+        case _util.OPS.setStrokeCMYKColor:
+        case _util.OPS.setFillCMYKColor:
+        case _util.OPS.shadingFill:
+        case _util.OPS.setRenderingIntent:
+          operatorList.fnArray.splice(i, 1);
+          operatorList.argsArray.splice(i, 1);
+          ii--;
+          continue;
+
+        case _util.OPS.setGState:
+          const [gStateObj] = operatorList.argsArray[i];
+          let j = 0,
+              jj = gStateObj.length;
+
+          while (j < jj) {
+            const [gStateKey] = gStateObj[j];
+
+            switch (gStateKey) {
+              case "TR":
+              case "TR2":
+              case "HT":
+              case "BG":
+              case "BG2":
+              case "UCR":
+              case "UCR2":
+                gStateObj.splice(j, 1);
+                jj--;
+                continue;
+            }
+
+            j++;
+          }
+
+          break;
+      }
+
+      i++;
+    }
+  }
+
 }
 
 class StateManager {
-  constructor(initialState) {
+  constructor(initialState = new EvalState()) {
     this.state = initialState;
     this.stateStack = [];
   }
@@ -3761,7 +3853,7 @@ class EvaluatorPreprocessor {
     return (0, _util.shadow)(this, "MAX_INVALID_PATH_OPS", 20);
   }
 
-  constructor(stream, xref, stateManager) {
+  constructor(stream, xref, stateManager = new StateManager()) {
     this.parser = new _parser.Parser({
       lexer: new _parser.Lexer(stream, EvaluatorPreprocessor.opMap),
       xref
@@ -3872,4 +3964,6 @@ class EvaluatorPreprocessor {
     }
   }
 
-}
+}
+
+exports.EvaluatorPreprocessor = EvaluatorPreprocessor;

+ 97 - 11
lib/core/fonts.js

@@ -25,7 +25,7 @@ Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.getFontType = getFontType;
-exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.SEAC_ANALYSIS_ENABLED = void 0;
+exports.ToUnicodeMap = exports.SEAC_ANALYSIS_ENABLED = exports.IdentityToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -435,6 +435,7 @@ var Font = function FontClosure() {
     this.defaultWidth = properties.defaultWidth;
     this.composite = properties.composite;
     this.cMap = properties.cMap;
+    this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
     this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
     this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
     this.fontMatrix = properties.fontMatrix;
@@ -466,7 +467,7 @@ var Font = function FontClosure() {
         (0, _util.warn)('Font file is empty in "' + name + '" (' + this.loadedName + ")");
       }
 
-      this.fallbackToSystemFont();
+      this.fallbackToSystemFont(properties);
       return;
     }
 
@@ -509,7 +510,7 @@ var Font = function FontClosure() {
       }
     } catch (e) {
       (0, _util.warn)(e);
-      this.fallbackToSystemFont();
+      this.fallbackToSystemFont(properties);
       return;
     }
 
@@ -1020,7 +1021,7 @@ var Font = function FontClosure() {
       return data;
     },
 
-    fallbackToSystemFont: function Font_fallbackToSystemFont() {
+    fallbackToSystemFont(properties) {
       this.missingFile = true;
       var name = this.name;
       var type = this.type;
@@ -1028,15 +1029,18 @@ var Font = function FontClosure() {
       let fontName = name.replace(/[,_]/g, "-").replace(/\s/g, "");
       var stdFontMap = (0, _standard_fonts.getStdFontMap)(),
           nonStdFontMap = (0, _standard_fonts.getNonStdFontMap)();
-      var isStandardFont = !!stdFontMap[fontName] || !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
+      const isStandardFont = !!stdFontMap[fontName];
+      const isMappedToStandardFont = !!(nonStdFontMap[fontName] && stdFontMap[nonStdFontMap[fontName]]);
       fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
       this.bold = fontName.search(/bold/gi) !== -1;
       this.italic = fontName.search(/oblique/gi) !== -1 || fontName.search(/italic/gi) !== -1;
       this.black = name.search(/Black/g) !== -1;
-      this.remeasure = Object.keys(this.widths).length > 0;
+      const isNarrow = name.search(/Narrow/g) !== -1;
+      this.remeasure = (!isStandardFont || isNarrow) && Object.keys(this.widths).length > 0;
 
-      if (isStandardFont && type === "CIDFontType2" && this.cidEncoding.startsWith("Identity-")) {
-        const GlyphMapForStandardFonts = (0, _standard_fonts.getGlyphMapForStandardFonts)();
+      if ((isStandardFont || isMappedToStandardFont) && type === "CIDFontType2" && this.cidEncoding.startsWith("Identity-")) {
+        const GlyphMapForStandardFonts = (0, _standard_fonts.getGlyphMapForStandardFonts)(),
+              cidToGidMap = properties.cidToGidMap;
         const map = [];
 
         for (const charCode in GlyphMapForStandardFonts) {
@@ -1057,6 +1061,16 @@ var Font = function FontClosure() {
           }
         }
 
+        if (cidToGidMap) {
+          for (const charCode in map) {
+            const cid = map[charCode];
+
+            if (cidToGidMap[cid] !== undefined) {
+              map[+charCode] = cidToGidMap[cid];
+            }
+          }
+        }
+
         var isIdentityUnicode = this.toUnicode instanceof IdentityToUnicodeMap;
 
         if (!isIdentityUnicode) {
@@ -1109,6 +1123,7 @@ var Font = function FontClosure() {
       this.loadedName = fontName.split("-")[0];
       this.fontType = getFontType(type, subtype);
     },
+
     checkAndRepair: function Font_checkAndRepair(name, font, properties) {
       const VALID_TABLES = ["OS/2", "cmap", "head", "hhea", "hmtx", "maxp", "name", "post", "loca", "glyf", "fpgm", "prep", "cvt ", "CFF "];
 
@@ -2641,7 +2656,7 @@ var Font = function FontClosure() {
       return (0, _util.shadow)(this, "spaceWidth", width);
     },
 
-    charToGlyph: function Font_charToGlyph(charcode, isSpace) {
+    _charToGlyph(charcode, isSpace = false) {
       var fontCharCode, width, operatorListId;
       var widthCode = charcode;
 
@@ -2706,6 +2721,7 @@ var Font = function FontClosure() {
 
       return glyph;
     },
+
     charsToGlyphs: function Font_charsToGlyphs(chars) {
       var charsCache = this.charsCache;
       var glyphs, glyph, charcode;
@@ -2736,13 +2752,13 @@ var Font = function FontClosure() {
           var length = c.length;
           i += length;
           var isSpace = length === 1 && chars.charCodeAt(i - 1) === 0x20;
-          glyph = this.charToGlyph(charcode, isSpace);
+          glyph = this._charToGlyph(charcode, isSpace);
           glyphs.push(glyph);
         }
       } else {
         for (i = 0, ii = chars.length; i < ii; ++i) {
           charcode = chars.charCodeAt(i);
-          glyph = this.charToGlyph(charcode, charcode === 0x20);
+          glyph = this._charToGlyph(charcode, charcode === 0x20);
           glyphs.push(glyph);
         }
       }
@@ -2750,8 +2766,75 @@ var Font = function FontClosure() {
       return charsCache[charsCacheKey] = glyphs;
     },
 
+    getCharPositions(chars) {
+      const positions = [];
+
+      if (this.cMap) {
+        const c = Object.create(null);
+        let i = 0;
+
+        while (i < chars.length) {
+          this.cMap.readCharCode(chars, i, c);
+          const length = c.length;
+          positions.push([i, i + length]);
+          i += length;
+        }
+      } else {
+        for (let i = 0, ii = chars.length; i < ii; ++i) {
+          positions.push([i, i + 1]);
+        }
+      }
+
+      return positions;
+    },
+
     get glyphCacheValues() {
       return Object.values(this.glyphCache);
+    },
+
+    encodeString(str) {
+      const buffers = [];
+      const currentBuf = [];
+
+      const hasCurrentBufErrors = () => buffers.length % 2 === 1;
+
+      for (let i = 0, ii = str.length; i < ii; i++) {
+        const unicode = str.codePointAt(i);
+
+        if (unicode > 0xd7ff && (unicode < 0xe000 || unicode > 0xfffd)) {
+          i++;
+        }
+
+        if (this.toUnicode) {
+          const char = String.fromCodePoint(unicode);
+          const charCode = this.toUnicode.charCodeOf(char);
+
+          if (charCode !== -1) {
+            if (hasCurrentBufErrors()) {
+              buffers.push(currentBuf.join(""));
+              currentBuf.length = 0;
+            }
+
+            const charCodeLength = this.cMap ? this.cMap.getCharCodeLength(charCode) : 1;
+
+            for (let j = charCodeLength - 1; j >= 0; j--) {
+              currentBuf.push(String.fromCharCode(charCode >> 8 * j & 0xff));
+            }
+
+            continue;
+          }
+        }
+
+        if (!hasCurrentBufErrors()) {
+          buffers.push(currentBuf.join(""));
+          currentBuf.length = 0;
+        }
+
+        currentBuf.push(String.fromCodePoint(unicode));
+      }
+
+      buffers.push(currentBuf.join(""));
+      return buffers;
     }
 
   };
@@ -2771,6 +2854,9 @@ var ErrorFont = function ErrorFontClosure() {
     charsToGlyphs: function ErrorFont_charsToGlyphs() {
       return [];
     },
+    encodeString: function ErrorFont_encodeString(chars) {
+      return [chars];
+    },
 
     exportData(extraProperties = false) {
       return {

+ 5 - 10
lib/core/function.js

@@ -25,7 +25,7 @@ Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.isPDFFunction = isPDFFunction;
-exports.PostScriptCompiler = exports.PostScriptEvaluator = exports.PDFFunctionFactory = void 0;
+exports.PostScriptEvaluator = exports.PostScriptCompiler = exports.PDFFunctionFactory = void 0;
 
 var _primitives = require("./primitives.js");
 
@@ -42,7 +42,6 @@ class PDFFunctionFactory {
   }) {
     this.xref = xref;
     this.isEvalSupported = isEvalSupported !== false;
-    this._localFunctionCache = null;
   }
 
   create(fn) {
@@ -93,10 +92,6 @@ class PDFFunctionFactory {
     }
 
     if (fnRef) {
-      if (!this._localFunctionCache) {
-        this._localFunctionCache = new _image_utils.LocalFunctionCache();
-      }
-
       const localFunction = this._localFunctionCache.getByRef(fnRef);
 
       if (localFunction) {
@@ -123,14 +118,14 @@ class PDFFunctionFactory {
     }
 
     if (fnRef) {
-      if (!this._localFunctionCache) {
-        this._localFunctionCache = new _image_utils.LocalFunctionCache();
-      }
-
       this._localFunctionCache.set(null, fnRef, parsedFunction);
     }
   }
 
+  get _localFunctionCache() {
+    return (0, _util.shadow)(this, "_localFunctionCache", new _image_utils.LocalFunctionCache());
+  }
+
 }
 
 exports.PDFFunctionFactory = PDFFunctionFactory;

File diff suppressed because it is too large
+ 7 - 4529
lib/core/glyphlist.js


+ 30 - 1
lib/core/image_utils.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.GlobalImageCache = exports.LocalGStateCache = exports.LocalFunctionCache = exports.LocalColorSpaceCache = exports.LocalImageCache = void 0;
+exports.LocalTilingPatternCache = exports.LocalImageCache = exports.LocalGStateCache = exports.LocalFunctionCache = exports.LocalColorSpaceCache = exports.GlobalImageCache = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -180,6 +180,35 @@ class LocalGStateCache extends BaseLocalCache {
 
 exports.LocalGStateCache = LocalGStateCache;
 
+class LocalTilingPatternCache extends BaseLocalCache {
+  set(name, ref = null, data) {
+    if (!name) {
+      throw new Error('LocalTilingPatternCache.set - expected "name" argument.');
+    }
+
+    if (ref) {
+      if (this._imageCache.has(ref)) {
+        return;
+      }
+
+      this._nameRefMap.set(name, ref);
+
+      this._imageCache.put(ref, data);
+
+      return;
+    }
+
+    if (this._imageMap.has(name)) {
+      return;
+    }
+
+    this._imageMap.set(name, data);
+  }
+
+}
+
+exports.LocalTilingPatternCache = LocalTilingPatternCache;
+
 class GlobalImageCache {
   static get NUM_PAGES_THRESHOLD() {
     return (0, _util.shadow)(this, "NUM_PAGES_THRESHOLD", 2);

+ 1 - 1
lib/core/jpg.js

@@ -1029,7 +1029,7 @@ var JpegImage = function JpegImageClosure() {
               break;
             }
 
-            if (offset >= data.length - 1) {
+            if (!nextFileMarker || offset >= data.length - 1) {
               (0, _util.warn)("JpegImage.parse - reached the end of the image data " + "without finding an EOI marker (0xFFD9).");
               break markerLoop;
             }

+ 5 - 5
lib/core/jpx.js

@@ -413,7 +413,7 @@ var JpxImage = function JpxImageClosure() {
 
               if (unsupported.length > 0) {
                 doNotRecover = true;
-                throw new Error("Unsupported COD options (" + unsupported.join(", ") + ")");
+                (0, _util.warn)(`JPX: Unsupported COD options (${unsupported.join(", ")}).`);
               }
 
               if (context.mainHeader) {
@@ -457,6 +457,9 @@ var JpxImage = function JpxImageClosure() {
               parseTilePackets(context, data, position, length);
               break;
 
+            case 0xff53:
+              (0, _util.warn)("JPX: Codestream code 0xFF53 (COC) is not implemented.");
+
             case 0xff55:
             case 0xff57:
             case 0xff58:
@@ -464,9 +467,6 @@ var JpxImage = function JpxImageClosure() {
               length = (0, _core_utils.readUint16)(data, position);
               break;
 
-            case 0xff53:
-              throw new Error("Codestream code 0xFF53 (COC) is not implemented");
-
             default:
               throw new Error("Unknown codestream code: " + code.toString(16));
           }
@@ -477,7 +477,7 @@ var JpxImage = function JpxImageClosure() {
         if (doNotRecover || this.failOnCorruptedImage) {
           throw new JpxError(e.message);
         } else {
-          (0, _util.warn)("JPX: Trying to recover from: " + e.message);
+          (0, _util.warn)(`JPX: Trying to recover from: "${e.message}".`);
         }
       }
 

+ 1 - 1
lib/core/murmurhash3.js

@@ -56,7 +56,7 @@ class MurmurHash3_64 {
         }
       }
     } else if ((0, _util.isArrayBuffer)(input)) {
-      data = input;
+      data = input.slice();
       length = data.byteLength;
     } else {
       throw new Error("Wrong data format in MurmurHash3_64_update. " + "Input must be a string or array.");

+ 123 - 39
lib/core/obj.js

@@ -24,16 +24,16 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.FileSpec = exports.XRef = exports.ObjectLoader = exports.Catalog = void 0;
+exports.XRef = exports.ObjectLoader = exports.FileSpec = exports.Catalog = void 0;
 
 var _util = require("../shared/util.js");
 
 var _primitives = require("./primitives.js");
 
-var _parser = require("./parser.js");
-
 var _core_utils = require("./core_utils.js");
 
+var _parser = require("./parser.js");
+
 var _crypto = require("./crypto.js");
 
 var _colorspace = require("./colorspace.js");
@@ -58,6 +58,7 @@ class Catalog {
     this.builtInCMapCache = new Map();
     this.globalImageCache = new _image_utils.GlobalImageCache();
     this.pageKidsCountCache = new _primitives.RefSetCache();
+    this.nonBlendModesSet = new _primitives.RefSet();
   }
 
   get version() {
@@ -141,6 +142,52 @@ class Catalog {
     return (0, _util.shadow)(this, "metadata", metadata);
   }
 
+  get markInfo() {
+    let markInfo = null;
+
+    try {
+      markInfo = this._readMarkInfo();
+    } catch (ex) {
+      if (ex instanceof _core_utils.MissingDataException) {
+        throw ex;
+      }
+
+      (0, _util.warn)("Unable to read mark info.");
+    }
+
+    return (0, _util.shadow)(this, "markInfo", markInfo);
+  }
+
+  _readMarkInfo() {
+    const obj = this._catDict.get("MarkInfo");
+
+    if (!(0, _primitives.isDict)(obj)) {
+      return null;
+    }
+
+    const markInfo = Object.assign(Object.create(null), {
+      Marked: false,
+      UserProperties: false,
+      Suspects: false
+    });
+
+    for (const key in markInfo) {
+      if (!obj.has(key)) {
+        continue;
+      }
+
+      const value = obj.get(key);
+
+      if (!(0, _util.isBool)(value)) {
+        continue;
+      }
+
+      markInfo[key] = value;
+    }
+
+    return markInfo;
+  }
+
   get toplevelPagesDict() {
     const pagesObj = this._catDict.get("Pages");
 
@@ -843,7 +890,7 @@ class Catalog {
   get openAction() {
     const obj = this._catDict.get("OpenAction");
 
-    let openAction = null;
+    const openAction = Object.create(null);
 
     if ((0, _primitives.isDict)(obj)) {
       const destDict = new _primitives.Dict(this.xref);
@@ -859,27 +906,15 @@ class Catalog {
       });
 
       if (Array.isArray(resultObj.dest)) {
-        if (!openAction) {
-          openAction = Object.create(null);
-        }
-
         openAction.dest = resultObj.dest;
       } else if (resultObj.action) {
-        if (!openAction) {
-          openAction = Object.create(null);
-        }
-
         openAction.action = resultObj.action;
       }
     } else if (Array.isArray(obj)) {
-      if (!openAction) {
-        openAction = Object.create(null);
-      }
-
       openAction.dest = obj;
     }
 
-    return (0, _util.shadow)(this, "openAction", openAction);
+    return (0, _util.shadow)(this, "openAction", (0, _util.objectSize)(openAction) > 0 ? openAction : null);
   }
 
   get attachments() {
@@ -905,12 +940,12 @@ class Catalog {
     return (0, _util.shadow)(this, "attachments", attachments);
   }
 
-  get javaScript() {
+  _collectJavaScript() {
     const obj = this._catDict.get("Names");
 
     let javaScript = null;
 
-    function appendIfJavaScriptDict(jsDict) {
+    function appendIfJavaScriptDict(name, jsDict) {
       const type = jsDict.get("S");
 
       if (!(0, _primitives.isName)(type, "JavaScript")) {
@@ -925,11 +960,11 @@ class Catalog {
         return;
       }
 
-      if (!javaScript) {
-        javaScript = [];
+      if (javaScript === null) {
+        javaScript = Object.create(null);
       }
 
-      javaScript.push((0, _util.stringToPDFString)(js));
+      javaScript[name] = (0, _util.stringToPDFString)(js);
     }
 
     if (obj && obj.has("JavaScript")) {
@@ -940,7 +975,7 @@ class Catalog {
         const jsDict = names[name];
 
         if ((0, _primitives.isDict)(jsDict)) {
-          appendIfJavaScriptDict(jsDict);
+          appendIfJavaScriptDict(name, jsDict);
         }
       }
     }
@@ -948,10 +983,38 @@ class Catalog {
     const openAction = this._catDict.get("OpenAction");
 
     if ((0, _primitives.isDict)(openAction) && (0, _primitives.isName)(openAction.get("S"), "JavaScript")) {
-      appendIfJavaScriptDict(openAction);
+      appendIfJavaScriptDict("OpenAction", openAction);
     }
 
-    return (0, _util.shadow)(this, "javaScript", javaScript);
+    return javaScript;
+  }
+
+  get javaScript() {
+    const javaScript = this._collectJavaScript();
+
+    return (0, _util.shadow)(this, "javaScript", javaScript ? Object.values(javaScript) : null);
+  }
+
+  get jsActions() {
+    const js = this._collectJavaScript();
+
+    let actions = (0, _core_utils.collectActions)(this.xref, this._catDict, _util.DocumentActionEventType);
+
+    if (!actions && js) {
+      actions = Object.create(null);
+    }
+
+    if (actions && js) {
+      for (const [key, val] of Object.entries(js)) {
+        if (key in actions) {
+          actions[key].push(val);
+        } else {
+          actions[key] = [val];
+        }
+      }
+    }
+
+    return (0, _util.shadow)(this, "jsActions", actions);
   }
 
   fontFallback(id, handler) {
@@ -973,6 +1036,7 @@ class Catalog {
     (0, _primitives.clearPrimitiveCaches)();
     this.globalImageCache.clear(manuallyTriggered);
     this.pageKidsCountCache.clear();
+    this.nonBlendModesSet.clear();
     const promises = [];
     this.fontCache.forEach(function (promise) {
       promises.push(promise);
@@ -981,7 +1045,7 @@ class Catalog {
       for (const {
         dict
       } of translatedFonts) {
-        delete dict.translated;
+        delete dict.cacheKey;
       }
 
       this.fontCache.clear();
@@ -1210,8 +1274,20 @@ class Catalog {
         url,
         dest;
 
-    if (!(0, _primitives.isDict)(action) && destDict.has("Dest")) {
-      action = destDict.get("Dest");
+    if (!(0, _primitives.isDict)(action)) {
+      if (destDict.has("Dest")) {
+        action = destDict.get("Dest");
+      } else {
+        action = destDict.get("AA");
+
+        if ((0, _primitives.isDict)(action)) {
+          if (action.has("D")) {
+            action = action.get("D");
+          } else if (action.has("U")) {
+            action = action.get("U");
+          }
+        }
+      }
     }
 
     if ((0, _primitives.isDict)(action)) {
@@ -1772,16 +1848,14 @@ var XRef = function XRefClosure() {
         }
       }
 
-      var i, ii;
-
-      for (i = 0, ii = xrefStms.length; i < ii; ++i) {
+      for (let i = 0, ii = xrefStms.length; i < ii; ++i) {
         this.startXRefQueue.push(xrefStms[i]);
         this.readXRef(true);
       }
 
       let trailerDict;
 
-      for (i = 0, ii = trailers.length; i < ii; ++i) {
+      for (let i = 0, ii = trailers.length; i < ii; ++i) {
         stream.pos = trailers[i];
         const parser = new _parser.Parser({
           lexer: new _parser.Lexer(stream),
@@ -1801,10 +1875,24 @@ var XRef = function XRefClosure() {
           continue;
         }
 
-        let rootDict;
-
         try {
-          rootDict = dict.get("Root");
+          const rootDict = dict.get("Root");
+
+          if (!(rootDict instanceof _primitives.Dict)) {
+            continue;
+          }
+
+          const pagesDict = rootDict.get("Pages");
+
+          if (!(pagesDict instanceof _primitives.Dict)) {
+            continue;
+          }
+
+          const pagesCount = pagesDict.get("Count");
+
+          if (!Number.isInteger(pagesCount)) {
+            continue;
+          }
         } catch (ex) {
           if (ex instanceof _core_utils.MissingDataException) {
             throw ex;
@@ -1813,10 +1901,6 @@ var XRef = function XRefClosure() {
           continue;
         }
 
-        if (!(0, _primitives.isDict)(rootDict) || !rootDict.has("Pages")) {
-          continue;
-        }
-
         if (dict.has("ID")) {
           return dict;
         }

+ 4 - 4
lib/core/pattern.js

@@ -173,7 +173,7 @@ Shadings.RadialAxial = function RadialAxialClosure() {
       fn(ratio, 0, color, 0);
       rgbColor = cs.getRgb(color, 0);
 
-      var cssColor = _util.Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
+      var cssColor = _util.Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);
 
       colorStops.push([i / NUMBER_OF_SAMPLES, cssColor]);
     }
@@ -182,7 +182,7 @@ Shadings.RadialAxial = function RadialAxialClosure() {
 
     if (dict.has("Background")) {
       rgbColor = cs.getRgb(dict.get("Background"), 0);
-      background = _util.Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
+      background = _util.Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);
     }
 
     if (!extendStart) {
@@ -949,7 +949,7 @@ Shadings.Dummy = function DummyClosure() {
   return Dummy;
 }();
 
-function getTilingPatternIR(operatorList, dict, args) {
+function getTilingPatternIR(operatorList, dict, color) {
   const matrix = dict.getArray("Matrix");
 
   const bbox = _util.Util.normalizeRect(dict.getArray("BBox"));
@@ -963,5 +963,5 @@ function getTilingPatternIR(operatorList, dict, args) {
     throw new _util.FormatError(`Invalid getTilingPatternIR /BBox array: [${bbox}].`);
   }
 
-  return ["TilingPattern", args, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType];
+  return ["TilingPattern", color, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType];
 }

+ 24 - 5
lib/core/primitives.js

@@ -25,14 +25,14 @@ Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.clearPrimitiveCaches = clearPrimitiveCaches;
-exports.isEOF = isEOF;
 exports.isCmd = isCmd;
 exports.isDict = isDict;
+exports.isEOF = isEOF;
 exports.isName = isName;
 exports.isRef = isRef;
 exports.isRefsEqual = isRefsEqual;
 exports.isStream = isStream;
-exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.Dict = exports.Cmd = exports.EOF = void 0;
+exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF = exports.Dict = exports.Cmd = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -184,7 +184,16 @@ var Dict = function DictClosure() {
       }
     }
   };
-  Dict.empty = new Dict(null);
+
+  Dict.empty = function () {
+    const emptyDict = new Dict(null);
+
+    emptyDict.set = (key, value) => {
+      (0, _util.unreachable)("Should not call `set` on the empty dictionary.");
+    };
+
+    return emptyDict;
+  }();
 
   Dict.merge = function ({
     xref,
@@ -296,8 +305,8 @@ var Ref = function RefClosure() {
 exports.Ref = Ref;
 
 class RefSet {
-  constructor() {
-    this._set = new Set();
+  constructor(parent = null) {
+    this._set = new Set(parent && parent._set);
   }
 
   has(ref) {
@@ -312,6 +321,16 @@ class RefSet {
     this._set.delete(ref.toString());
   }
 
+  forEach(callback) {
+    for (const ref of this._set.values()) {
+      callback(ref);
+    }
+  }
+
+  clear() {
+    this._set.clear();
+  }
+
 }
 
 exports.RefSet = RefSet;

+ 1 - 1
lib/core/standard_fonts.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForArialBlack = exports.getGlyphMapForStandardFonts = exports.getSymbolsFonts = exports.getSerifFonts = exports.getNonStdFontMap = exports.getStdFontMap = void 0;
+exports.getSymbolsFonts = exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForArialBlack = exports.getStdFontMap = exports.getSerifFonts = exports.getNonStdFontMap = exports.getGlyphMapForStandardFonts = void 0;
 
 var _core_utils = require("./core_utils.js");
 

+ 5 - 1
lib/core/stream.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.LZWStream = exports.StringStream = exports.StreamsSequenceStream = exports.Stream = exports.RunLengthStream = exports.PredictorStream = exports.NullStream = exports.FlateStream = exports.DecodeStream = exports.DecryptStream = exports.AsciiHexStream = exports.Ascii85Stream = void 0;
+exports.StringStream = exports.StreamsSequenceStream = exports.Stream = exports.RunLengthStream = exports.PredictorStream = exports.NullStream = exports.LZWStream = exports.FlateStream = exports.DecryptStream = exports.DecodeStream = exports.AsciiHexStream = exports.Ascii85Stream = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -177,6 +177,10 @@ var DecodeStream = function DecodeStreamClosure() {
   }
 
   DecodeStream.prototype = {
+    get length() {
+      (0, _util.unreachable)("Should not access DecodeStream.length");
+    },
+
     get isEmpty() {
       while (!this.eof && this.bufferLength === 0) {
         this.readBlock();

File diff suppressed because it is too large
+ 12 - 1380
lib/core/unicode.js


+ 58 - 17
lib/core/worker.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.WorkerMessageHandler = exports.WorkerTask = void 0;
+exports.WorkerTask = exports.WorkerMessageHandler = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -107,7 +107,7 @@ class WorkerMessageHandler {
     var WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     const apiVersion = docParams.apiVersion;
-    const workerVersion = '2.6.347';
+    const workerVersion = '2.7.570';
 
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
@@ -123,8 +123,8 @@ class WorkerMessageHandler {
       throw new Error("The `Array.prototype` contains unexpected enumerable properties: " + enumerableProperties.join(", ") + "; thus breaking e.g. `for...in` iteration of `Array`s.");
     }
 
-    if (typeof ReadableStream === "undefined" || typeof Promise.allSettled === "undefined") {
-      throw new Error("The browser/environment lacks native support for critical " + "functionality used by the PDF.js library (e.g. " + "`ReadableStream` and/or `Promise.allSettled`); " + "please use an ES5-compatible build instead.");
+    if (typeof ReadableStream === "undefined") {
+      throw new Error("The browser/environment lacks native support for critical " + "functionality used by the PDF.js library (e.g. `ReadableStream`); " + "please use an `es5`-build instead.");
     }
 
     var docId = docParams.docId;
@@ -406,6 +406,16 @@ class WorkerMessageHandler {
     handler.on("GetJavaScript", function wphSetupGetJavaScript(data) {
       return pdfManager.ensureCatalog("javaScript");
     });
+    handler.on("GetDocJSActions", function wphSetupGetDocJSActions(data) {
+      return pdfManager.ensureCatalog("jsActions");
+    });
+    handler.on("GetPageJSActions", function ({
+      pageIndex
+    }) {
+      return pdfManager.getPage(pageIndex).then(function (page) {
+        return page.jsActions;
+      });
+    });
     handler.on("GetOutline", function wphSetupGetOutline(data) {
       return pdfManager.ensureCatalog("documentOutline");
     });
@@ -418,6 +428,9 @@ class WorkerMessageHandler {
     handler.on("GetMetadata", function wphSetupGetMetadata(data) {
       return Promise.all([pdfManager.ensureDoc("documentInfo"), pdfManager.ensureCatalog("metadata")]);
     });
+    handler.on("GetMarkInfo", function wphSetupGetMarkInfo(data) {
+      return pdfManager.ensureCatalog("markInfo");
+    });
     handler.on("GetData", function wphSetupGetData(data) {
       pdfManager.requestLoadedStream();
       return pdfManager.onLoadedStream().then(function (stream) {
@@ -435,23 +448,34 @@ class WorkerMessageHandler {
         return page.getAnnotationsData(intent);
       });
     });
+    handler.on("GetFieldObjects", function (data) {
+      return pdfManager.ensureDoc("fieldObjects");
+    });
+    handler.on("HasJSActions", function (data) {
+      return pdfManager.ensureDoc("hasJSActions");
+    });
+    handler.on("GetCalculationOrderIds", function (data) {
+      return pdfManager.ensureDoc("calculationOrderIds");
+    });
     handler.on("SaveDocument", function ({
       numPages,
       annotationStorage,
       filename
     }) {
       pdfManager.requestLoadedStream();
-      const promises = [pdfManager.onLoadedStream()];
-      const document = pdfManager.pdfDocument;
+      const promises = [pdfManager.onLoadedStream(), pdfManager.ensureCatalog("acroForm"), pdfManager.ensureDoc("xref"), pdfManager.ensureDoc("startXRef")];
 
       for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {
         promises.push(pdfManager.getPage(pageIndex).then(function (page) {
           const task = new WorkerTask(`Save: page ${pageIndex}`);
-          return page.save(handler, task, annotationStorage);
+          startWorkerTask(task);
+          return page.save(handler, task, annotationStorage).finally(function () {
+            finishWorkerTask(task);
+          });
         }));
       }
 
-      return Promise.all(promises).then(([stream, ...refs]) => {
+      return Promise.all(promises).then(function ([stream, acroForm, xref, startXRef, ...refs]) {
         let newRefs = [];
 
         for (const ref of refs) {
@@ -462,18 +486,29 @@ class WorkerMessageHandler {
           return stream.bytes;
         }
 
-        const xref = document.xref;
+        const xfa = acroForm instanceof _primitives.Dict && acroForm.get("XFA") || [];
+        let xfaDatasets = null;
+
+        if (Array.isArray(xfa)) {
+          for (let i = 0, ii = xfa.length; i < ii; i += 2) {
+            if (xfa[i] === "datasets") {
+              xfaDatasets = xfa[i + 1];
+            }
+          }
+        } else {
+          (0, _util.warn)("Unsupported XFA type.");
+        }
+
         let newXrefInfo = Object.create(null);
 
         if (xref.trailer) {
-          const _info = Object.create(null);
-
+          const infoObj = Object.create(null);
           const xrefInfo = xref.trailer.get("Info") || null;
 
-          if (xrefInfo) {
+          if (xrefInfo instanceof _primitives.Dict) {
             xrefInfo.forEach((key, value) => {
               if ((0, _util.isString)(key) && (0, _util.isString)(value)) {
-                _info[key] = (0, _util.stringToPDFString)(value);
+                infoObj[key] = (0, _util.stringToPDFString)(value);
               }
             });
           }
@@ -483,15 +518,21 @@ class WorkerMessageHandler {
             encrypt: xref.trailer.getRaw("Encrypt") || null,
             newRef: xref.getNewRef(),
             infoRef: xref.trailer.getRaw("Info") || null,
-            info: _info,
+            info: infoObj,
             fileIds: xref.trailer.getRaw("ID") || null,
-            startXRef: document.startXRef,
+            startXRef,
             filename
           };
         }
 
         xref.resetNewRef();
-        return (0, _writer.incrementalUpdate)(stream.bytes, newXrefInfo, newRefs);
+        return (0, _writer.incrementalUpdate)({
+          originalData: stream.bytes,
+          xrefInfo: newXrefInfo,
+          newRefs,
+          xref,
+          datasetsRef: xfaDatasets
+        });
       });
     });
     handler.on("GetOperatorList", function wphSetupRenderPage(data, sink) {
@@ -528,7 +569,7 @@ class WorkerMessageHandler {
           sink.error(reason);
         });
       });
-    }, this);
+    });
     handler.on("GetTextContent", function wphExtractText(data, sink) {
       var pageIndex = data.pageIndex;
 

+ 68 - 4
lib/core/writer.js

@@ -24,20 +24,24 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.writeDict = writeDict;
 exports.incrementalUpdate = incrementalUpdate;
+exports.writeDict = writeDict;
 
 var _util = require("../shared/util.js");
 
 var _primitives = require("./primitives.js");
 
+var _core_utils = require("./core_utils.js");
+
+var _xml_parser = require("../shared/xml_parser.js");
+
 var _crypto = require("./crypto.js");
 
 function writeDict(dict, buffer, transform) {
   buffer.push("<<");
 
   for (const key of dict.getKeys()) {
-    buffer.push(` /${key} `);
+    buffer.push(` /${(0, _core_utils.escapePDFName)(key)} `);
     writeValue(dict.getRaw(key), buffer, transform);
   }
 
@@ -94,7 +98,7 @@ function numberToString(value) {
 
 function writeValue(value, buffer, transform) {
   if ((0, _primitives.isName)(value)) {
-    buffer.push(`/${value.name}`);
+    buffer.push(`/${(0, _core_utils.escapePDFName)(value.name)}`);
   } else if ((0, _primitives.isRef)(value)) {
     buffer.push(`${value.num} ${value.gen} R`);
   } else if (Array.isArray(value)) {
@@ -151,7 +155,67 @@ function computeMD5(filesize, xrefInfo) {
   return (0, _util.bytesToString)((0, _crypto.calculateMD5)(array));
 }
 
-function incrementalUpdate(originalData, xrefInfo, newRefs) {
+function updateXFA(datasetsRef, newRefs, xref) {
+  if (datasetsRef === null || xref === null) {
+    return;
+  }
+
+  const datasets = xref.fetchIfRef(datasetsRef);
+  const str = (0, _util.bytesToString)(datasets.getBytes());
+  const xml = new _xml_parser.SimpleXMLParser({
+    hasAttributes: true
+  }).parseFromString(str);
+
+  for (const {
+    xfa
+  } of newRefs) {
+    if (!xfa) {
+      continue;
+    }
+
+    const {
+      path,
+      value
+    } = xfa;
+
+    if (!path) {
+      continue;
+    }
+
+    const node = xml.documentElement.searchNode((0, _core_utils.parseXFAPath)(path), 0);
+
+    if (node) {
+      node.childNodes = [new _xml_parser.SimpleDOMNode("#text", value)];
+    } else {
+      (0, _util.warn)(`Node not found for path: ${path}`);
+    }
+  }
+
+  const buffer = [];
+  xml.documentElement.dump(buffer);
+  let updatedXml = buffer.join("");
+  const encrypt = xref.encrypt;
+
+  if (encrypt) {
+    const transform = encrypt.createCipherTransform(datasetsRef.num, datasetsRef.gen);
+    updatedXml = transform.encryptString(updatedXml);
+  }
+
+  const data = `${datasetsRef.num} ${datasetsRef.gen} obj\n` + `<< /Type /EmbeddedFile /Length ${updatedXml.length}>>\nstream\n` + updatedXml + "\nendstream\nendobj\n";
+  newRefs.push({
+    ref: datasetsRef,
+    data
+  });
+}
+
+function incrementalUpdate({
+  originalData,
+  xrefInfo,
+  newRefs,
+  xref = null,
+  datasetsRef = null
+}) {
+  updateXFA(datasetsRef, newRefs, xref);
   const newXref = new _primitives.Dict(null);
   const refForXrefTable = xrefInfo.newRef;
   let buffer, baseOffset;

File diff suppressed because it is too large
+ 607 - 78
lib/display/annotation_layer.js


+ 21 - 4
lib/display/annotation_storage.js

@@ -26,6 +26,8 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.AnnotationStorage = void 0;
 
+var _util = require("../shared/util.js");
+
 class AnnotationStorage {
   constructor() {
     this._storage = new Map();
@@ -45,11 +47,26 @@ class AnnotationStorage {
   }
 
   setValue(key, value) {
-    if (this._storage.get(key) !== value) {
-      this._setModified();
+    const obj = this._storage.get(key);
+
+    let modified = false;
+
+    if (obj !== undefined) {
+      for (const [entry, val] of Object.entries(value)) {
+        if (obj[entry] !== val) {
+          modified = true;
+          obj[entry] = val;
+        }
+      }
+    } else {
+      this._storage.set(key, value);
+
+      modified = true;
     }
 
-    this._storage.set(key, value);
+    if (modified) {
+      this._setModified();
+    }
   }
 
   getAll() {
@@ -57,7 +74,7 @@ class AnnotationStorage {
       return null;
     }
 
-    return Object.fromEntries(this._storage);
+    return (0, _util.objectFromEntries)(this._storage);
   }
 
   get size() {

+ 88 - 18
lib/display/api.js

@@ -26,7 +26,7 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.getDocument = getDocument;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
-exports.build = exports.version = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.LoopbackPort = void 0;
+exports.version = exports.PDFWorker = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.DefaultCMapReaderFactory = exports.DefaultCanvasFactory = exports.build = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -59,7 +59,9 @@ var _webgl = require("./webgl.js");
 const DEFAULT_RANGE_CHUNK_SIZE = 65536;
 const RENDERING_CANCELLED_TIMEOUT = 100;
 const DefaultCanvasFactory = _is_node.isNodeJS ? _node_utils.NodeCanvasFactory : _display_utils.DOMCanvasFactory;
+exports.DefaultCanvasFactory = DefaultCanvasFactory;
 const DefaultCMapReaderFactory = _is_node.isNodeJS ? _node_utils.NodeCMapReaderFactory : _display_utils.DOMCMapReaderFactory;
+exports.DefaultCMapReaderFactory = DefaultCMapReaderFactory;
 let createPDFNetworkStream;
 
 function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) {
@@ -233,7 +235,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
 
   return worker.messageHandler.sendWithPromise("GetDocRequest", {
     docId,
-    apiVersion: '2.6.347',
+    apiVersion: '2.7.570',
     source: {
       data: source.data,
       url: source.url,
@@ -430,6 +432,10 @@ class PDFDocumentProxy {
     return this._transport.getJavaScript();
   }
 
+  getJSActions() {
+    return this._transport.getDocJSActions();
+  }
+
   getOutline() {
     return this._transport.getOutline();
   }
@@ -446,6 +452,10 @@ class PDFDocumentProxy {
     return this._transport.getMetadata();
   }
 
+  getMarkInfo() {
+    return this._transport.getMarkInfo();
+  }
+
   getData() {
     return this._transport.getData();
   }
@@ -478,6 +488,18 @@ class PDFDocumentProxy {
     return this._transport.saveDocument(annotationStorage);
   }
 
+  getFieldObjects() {
+    return this._transport.getFieldObjects();
+  }
+
+  hasJSActions() {
+    return this._transport.hasJSActions();
+  }
+
+  getCalculationOrderIds() {
+    return this._transport.getCalculationOrderIds();
+  }
+
 }
 
 exports.PDFDocumentProxy = PDFDocumentProxy;
@@ -546,6 +568,10 @@ class PDFPageProxy {
     return this.annotationsPromise;
   }
 
+  getJSActions() {
+    return this._jsActionsPromise || (this._jsActionsPromise = this._transport.getPageJSActions(this._pageIndex));
+  }
+
   render({
     canvasContext,
     viewport,
@@ -606,7 +632,7 @@ class PDFPageProxy {
         pageIndex: this._pageIndex,
         intent: renderingIntent,
         renderInteractiveForms: renderInteractiveForms === true,
-        annotationStorage: annotationStorage && annotationStorage.getAll() || null
+        annotationStorage: annotationStorage?.getAll() || null
       });
     }
 
@@ -805,6 +831,7 @@ class PDFPageProxy {
 
     this.objs.clear();
     this.annotationsPromise = null;
+    this._jsActionsPromise = null;
     this.pendingCleanup = false;
     return Promise.all(waitOn);
   }
@@ -832,6 +859,7 @@ class PDFPageProxy {
 
     this.objs.clear();
     this.annotationsPromise = null;
+    this._jsActionsPromise = null;
 
     if (resetStats && this._stats) {
       this._stats = new _display_utils.StatTimer();
@@ -962,7 +990,7 @@ class PDFPageProxy {
       }
     }
 
-    intentState.streamReader.cancel(new _util.AbortException(reason && reason.message));
+    intentState.streamReader.cancel(new _util.AbortException(reason?.message));
     intentState.streamReader = null;
 
     if (this._transport.destroyed) {
@@ -1008,9 +1036,7 @@ class LoopbackPort {
       let buffer, result;
 
       if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
-        const transferable = transfers && transfers.includes(buffer);
-
-        if (transferable) {
+        if (transfers?.includes(buffer)) {
           result = new value.constructor(buffer, value.byteOffset, value.byteLength);
         } else {
           result = new value.constructor(value);
@@ -1100,7 +1126,7 @@ const PDFWorker = function PDFWorkerClosure() {
     isWorkerDisabled = true;
     fallbackWorkerSrc = "../pdf.worker.js";
   } else if (typeof document === "object" && "currentScript" in document) {
-    const pdfjsFilePath = document.currentScript && document.currentScript.src;
+    const pdfjsFilePath = document.currentScript?.src;
 
     if (pdfjsFilePath) {
       fallbackWorkerSrc = pdfjsFilePath.replace(/(\.(?:min\.)?js)(\?.*)?$/i, ".worker$1$2");
@@ -1127,7 +1153,7 @@ const PDFWorker = function PDFWorkerClosure() {
     let mainWorkerMessageHandler;
 
     try {
-      mainWorkerMessageHandler = globalThis.pdfjsWorker && globalThis.pdfjsWorker.WorkerMessageHandler;
+      mainWorkerMessageHandler = globalThis.pdfjsWorker?.WorkerMessageHandler;
     } catch (ex) {}
 
     return mainWorkerMessageHandler || null;
@@ -1413,6 +1439,10 @@ class WorkerTransport {
     this.setupMessageHandler();
   }
 
+  get loadingTaskSettled() {
+    return this.loadingTask._capability.settled;
+  }
+
   destroy() {
     if (this.destroyCapability) {
       return this.destroyCapability.promise;
@@ -1435,8 +1465,20 @@ class WorkerTransport {
     this.pagePromises.length = 0;
     const terminated = this.messageHandler.sendWithPromise("Terminate", null);
     waitOn.push(terminated);
+
+    if (this.loadingTaskSettled) {
+      const annotationStorageResetModified = this.loadingTask.promise.then(pdfDocument => {
+        if (pdfDocument.hasOwnProperty("annotationStorage")) {
+          pdfDocument.annotationStorage.resetModified();
+        }
+      }).catch(() => {});
+      waitOn.push(annotationStorageResetModified);
+    }
+
     Promise.all(waitOn).then(() => {
+      this.commonObjs.clear();
       this.fontLoader.clear();
+      this._hasJSActionsPromise = null;
 
       if (this._networkStream) {
         this._networkStream.cancelAllRequests(new _util.AbortException("Worker was terminated."));
@@ -1665,7 +1707,7 @@ class WorkerTransport {
 
           let fontRegistry = null;
 
-          if (params.pdfBug && globalThis.FontInspector && globalThis.FontInspector.enabled) {
+          if (params.pdfBug && globalThis.FontInspector?.enabled) {
             fontRegistry = {
               registerFont(font, url) {
                 globalThis.FontInspector.fontAdded(font, url);
@@ -1720,7 +1762,7 @@ class WorkerTransport {
           pageProxy.objs.resolve(id, imageData);
           const MAX_IMAGE_SIZE_TO_STORE = 8000000;
 
-          if (imageData && "data" in imageData && imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) {
+          if (imageData?.data?.length > MAX_IMAGE_SIZE_TO_STORE) {
             pageProxy.cleanupAfterRender = true;
           }
 
@@ -1829,8 +1871,8 @@ class WorkerTransport {
   saveDocument(annotationStorage) {
     return this.messageHandler.sendWithPromise("SaveDocument", {
       numPages: this._numPages,
-      annotationStorage: annotationStorage && annotationStorage.getAll() || null,
-      filename: this._fullReader ? this._fullReader.filename : null
+      annotationStorage: annotationStorage?.getAll() || null,
+      filename: this._fullReader?.filename ?? null
     }).finally(() => {
       if (annotationStorage) {
         annotationStorage.resetModified();
@@ -1838,6 +1880,18 @@ class WorkerTransport {
     });
   }
 
+  getFieldObjects() {
+    return this.messageHandler.sendWithPromise("GetFieldObjects", null);
+  }
+
+  hasJSActions() {
+    return this._hasJSActionsPromise || (this._hasJSActionsPromise = this.messageHandler.sendWithPromise("HasJSActions", null));
+  }
+
+  getCalculationOrderIds() {
+    return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null);
+  }
+
   getDestinations() {
     return this.messageHandler.sendWithPromise("GetDestinations", null);
   }
@@ -1880,6 +1934,16 @@ class WorkerTransport {
     return this.messageHandler.sendWithPromise("GetJavaScript", null);
   }
 
+  getDocJSActions() {
+    return this.messageHandler.sendWithPromise("GetDocJSActions", null);
+  }
+
+  getPageJSActions(pageIndex) {
+    return this.messageHandler.sendWithPromise("GetPageJSActions", {
+      pageIndex
+    });
+  }
+
   getOutline() {
     return this.messageHandler.sendWithPromise("GetOutline", null);
   }
@@ -1899,11 +1963,16 @@ class WorkerTransport {
       return {
         info: results[0],
         metadata: results[1] ? new _metadata.Metadata(results[1]) : null,
-        contentDispositionFilename: this._fullReader ? this._fullReader.filename : null
+        contentDispositionFilename: this._fullReader?.filename ?? null,
+        contentLength: this._fullReader?.contentLength ?? null
       };
     });
   }
 
+  getMarkInfo() {
+    return this.messageHandler.sendWithPromise("GetMarkInfo", null);
+  }
+
   getStats() {
     return this.messageHandler.sendWithPromise("GetStats", null);
   }
@@ -1924,6 +1993,7 @@ class WorkerTransport {
 
       this.commonObjs.clear();
       this.fontLoader.clear();
+      this._hasJSActionsPromise = null;
     });
   }
 
@@ -1972,7 +2042,7 @@ class PDFObjects {
 
   has(objId) {
     const obj = this._objs[objId];
-    return obj ? obj.resolved : false;
+    return obj?.resolved || false;
   }
 
   resolve(objId, data) {
@@ -2064,7 +2134,7 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
         canvasInRendering.add(this._canvas);
       }
 
-      if (this._pdfBug && globalThis.StepperManager && globalThis.StepperManager.enabled) {
+      if (this._pdfBug && globalThis.StepperManager?.enabled) {
         this.stepper = globalThis.StepperManager.create(this._pageIndex);
         this.stepper.init(this.operatorList);
         this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
@@ -2178,7 +2248,7 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
   return InternalRenderTask;
 }();
 
-const version = '2.6.347';
+const version = '2.7.570';
 exports.version = version;
-const build = '3be9c65f';
+const build = 'f2c7338b0';
 exports.build = build;

File diff suppressed because it is too large
+ 272 - 242
lib/display/canvas.js


+ 11 - 4
lib/display/display_utils.js

@@ -25,12 +25,12 @@ Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.addLinkAttributes = addLinkAttributes;
+exports.deprecated = deprecated;
 exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.isFetchSupported = isFetchSupported;
 exports.isValidFetchUrl = isValidFetchUrl;
 exports.loadScript = loadScript;
-exports.deprecated = deprecated;
-exports.PDFDateString = exports.StatTimer = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.BaseCMapReaderFactory = exports.DOMCanvasFactory = exports.BaseCanvasFactory = exports.DEFAULT_LINK_REL = exports.LinkTarget = exports.RenderingCancelledException = exports.PageViewport = void 0;
+exports.StatTimer = exports.RenderingCancelledException = exports.PDFDateString = exports.PageViewport = exports.LinkTarget = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.BaseCMapReaderFactory = exports.BaseCanvasFactory = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -483,11 +483,18 @@ function isValidFetchUrl(url, baseUrl) {
   }
 }
 
-function loadScript(src) {
+function loadScript(src, removeScriptElement = false) {
   return new Promise((resolve, reject) => {
     const script = document.createElement("script");
     script.src = src;
-    script.onload = resolve;
+
+    script.onload = function (evt) {
+      if (removeScriptElement) {
+        script.remove();
+      }
+
+      resolve(evt);
+    };
 
     script.onerror = function () {
       reject(new Error(`Cannot load script at: ${script.src}`));

+ 3 - 3
lib/display/fetch_stream.js

@@ -36,7 +36,7 @@ function createFetchOptions(headers, withCredentials, abortController) {
   return {
     method: "GET",
     headers,
-    signal: abortController && abortController.signal,
+    signal: abortController?.signal,
     mode: "cors",
     credentials: withCredentials ? "include" : "same-origin",
     redirect: "follow"
@@ -69,7 +69,7 @@ class PDFFetchStream {
   }
 
   get _progressiveDataLength() {
-    return this._fullRequestReader ? this._fullRequestReader._loaded : 0;
+    return this._fullRequestReader?._loaded ?? 0;
   }
 
   getFullReader() {
@@ -254,7 +254,7 @@ class PDFFetchStreamRangeReader {
 
       this._reader = response.body.getReader();
     }).catch(reason => {
-      if (reason && reason.name === "AbortError") {
+      if (reason?.name === "AbortError") {
         return;
       }
 

+ 2 - 3
lib/display/font_loader.js

@@ -128,8 +128,7 @@ class BaseFontLoader {
   }
 
   get isFontLoadingAPISupported() {
-    const supported = typeof this._document !== "undefined" && !!this._document.fonts;
-    return (0, _util.shadow)(this, "isFontLoadingAPISupported", supported);
+    return (0, _util.shadow)(this, "isFontLoadingAPISupported", !!this._document?.fonts);
   }
 
   get isSyncFontLoadingSupported() {
@@ -167,7 +166,7 @@ exports.FontLoader = FontLoader;
       } else {
         const m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(navigator.userAgent);
 
-        if (m && m[1] >= 14) {
+        if (m?.[1] >= 14) {
           supported = true;
         }
       }

+ 54 - 20
lib/display/metadata.js

@@ -28,19 +28,23 @@ exports.Metadata = void 0;
 
 var _util = require("../shared/util.js");
 
-var _xml_parser = require("./xml_parser.js");
+var _xml_parser = require("../shared/xml_parser.js");
 
 class Metadata {
   constructor(data) {
     (0, _util.assert)(typeof data === "string", "Metadata: input is not a string");
     data = this._repair(data);
-    const parser = new _xml_parser.SimpleXMLParser();
+    const parser = new _xml_parser.SimpleXMLParser({
+      lowerCaseName: true
+    });
     const xmlDocument = parser.parseFromString(data);
     this._metadataMap = new Map();
 
     if (xmlDocument) {
       this._parse(xmlDocument);
     }
+
+    this._data = data;
   }
 
   _repair(data) {
@@ -83,49 +87,79 @@ class Metadata {
     });
   }
 
+  _getSequence(entry) {
+    const name = entry.nodeName;
+
+    if (name !== "rdf:bag" && name !== "rdf:seq" && name !== "rdf:alt") {
+      return null;
+    }
+
+    return entry.childNodes.filter(node => node.nodeName === "rdf:li");
+  }
+
+  _getCreators(entry) {
+    if (entry.nodeName !== "dc:creator") {
+      return false;
+    }
+
+    if (!entry.hasChildNodes()) {
+      return true;
+    }
+
+    const seqNode = entry.childNodes[0];
+    const authors = this._getSequence(seqNode) || [];
+
+    this._metadataMap.set(entry.nodeName, authors.map(node => node.textContent.trim()));
+
+    return true;
+  }
+
   _parse(xmlDocument) {
     let rdf = xmlDocument.documentElement;
 
-    if (rdf.nodeName.toLowerCase() !== "rdf:rdf") {
+    if (rdf.nodeName !== "rdf:rdf") {
       rdf = rdf.firstChild;
 
-      while (rdf && rdf.nodeName.toLowerCase() !== "rdf:rdf") {
+      while (rdf && rdf.nodeName !== "rdf:rdf") {
         rdf = rdf.nextSibling;
       }
     }
 
-    const nodeName = rdf ? rdf.nodeName.toLowerCase() : null;
-
-    if (!rdf || nodeName !== "rdf:rdf" || !rdf.hasChildNodes()) {
+    if (!rdf || rdf.nodeName !== "rdf:rdf" || !rdf.hasChildNodes()) {
       return;
     }
 
-    const children = rdf.childNodes;
-
-    for (let i = 0, ii = children.length; i < ii; i++) {
-      const desc = children[i];
-
-      if (desc.nodeName.toLowerCase() !== "rdf:description") {
+    for (const desc of rdf.childNodes) {
+      if (desc.nodeName !== "rdf:description") {
         continue;
       }
 
-      for (let j = 0, jj = desc.childNodes.length; j < jj; j++) {
-        if (desc.childNodes[j].nodeName.toLowerCase() !== "#text") {
-          const entry = desc.childNodes[j];
-          const name = entry.nodeName.toLowerCase();
+      for (const entry of desc.childNodes) {
+        const name = entry.nodeName;
 
-          this._metadataMap.set(name, entry.textContent.trim());
+        if (name === "#text") {
+          continue;
         }
+
+        if (this._getCreators(entry)) {
+          continue;
+        }
+
+        this._metadataMap.set(name, entry.textContent.trim());
       }
     }
   }
 
+  getRaw() {
+    return this._data;
+  }
+
   get(name) {
-    return this._metadataMap.has(name) ? this._metadataMap.get(name) : null;
+    return this._metadataMap.get(name) ?? null;
   }
 
   getAll() {
-    return Object.fromEntries(this._metadataMap);
+    return (0, _util.objectFromEntries)(this._metadataMap);
   }
 
   has(name) {

+ 1 - 1
lib/display/node_stream.js

@@ -72,7 +72,7 @@ class PDFNodeStream {
   }
 
   get _progressiveDataLength() {
-    return this._fullRequestReader ? this._fullRequestReader._loaded : 0;
+    return this._fullRequestReader?._loaded ?? 0;
   }
 
   getFullReader() {

+ 1 - 1
lib/display/optional_content_config.js

@@ -172,7 +172,7 @@ class OptionalContentConfig {
       return null;
     }
 
-    return Object.fromEntries(this._groups);
+    return (0, _util.objectFromEntries)(this._groups);
   }
 
   getGroup(id) {

+ 109 - 113
lib/display/pattern_helper.js

@@ -29,7 +29,7 @@ exports.TilingPattern = void 0;
 
 var _util = require("../shared/util.js");
 
-var ShadingIRs = {};
+const ShadingIRs = {};
 
 function applyBoundingBox(ctx, bbox) {
   if (!bbox || typeof Path2D === "undefined") {
@@ -45,18 +45,17 @@ function applyBoundingBox(ctx, bbox) {
 
 ShadingIRs.RadialAxial = {
   fromIR: function RadialAxial_fromIR(raw) {
-    var type = raw[1];
-    var bbox = raw[2];
-    var colorStops = raw[3];
-    var p0 = raw[4];
-    var p1 = raw[5];
-    var r0 = raw[6];
-    var r1 = raw[7];
+    const type = raw[1];
+    const bbox = raw[2];
+    const colorStops = raw[3];
+    const p0 = raw[4];
+    const p1 = raw[5];
+    const r0 = raw[6];
+    const r1 = raw[7];
     return {
-      type: "Pattern",
       getPattern: function RadialAxial_getPattern(ctx) {
         applyBoundingBox(ctx, bbox);
-        var grad;
+        let grad;
 
         if (type === "axial") {
           grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
@@ -64,8 +63,8 @@ ShadingIRs.RadialAxial = {
           grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
         }
 
-        for (var i = 0, ii = colorStops.length; i < ii; ++i) {
-          var c = colorStops[i];
+        for (let i = 0, ii = colorStops.length; i < ii; ++i) {
+          const c = colorStops[i];
           grad.addColorStop(c[0], c[1]);
         }
 
@@ -75,13 +74,13 @@ ShadingIRs.RadialAxial = {
   }
 };
 
-var createMeshCanvas = function createMeshCanvasClosure() {
+const createMeshCanvas = function createMeshCanvasClosure() {
   function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
-    var coords = context.coords,
-        colors = context.colors;
-    var bytes = data.data,
-        rowSize = data.width * 4;
-    var tmp;
+    const coords = context.coords,
+          colors = context.colors;
+    const bytes = data.data,
+          rowSize = data.width * 4;
+    let tmp;
 
     if (coords[p1 + 1] > coords[p2 + 1]) {
       tmp = p1;
@@ -110,32 +109,32 @@ var createMeshCanvas = function createMeshCanvasClosure() {
       c2 = tmp;
     }
 
-    var x1 = (coords[p1] + context.offsetX) * context.scaleX;
-    var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
-    var x2 = (coords[p2] + context.offsetX) * context.scaleX;
-    var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
-    var x3 = (coords[p3] + context.offsetX) * context.scaleX;
-    var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
+    const x1 = (coords[p1] + context.offsetX) * context.scaleX;
+    const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
+    const x2 = (coords[p2] + context.offsetX) * context.scaleX;
+    const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
+    const x3 = (coords[p3] + context.offsetX) * context.scaleX;
+    const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
 
     if (y1 >= y3) {
       return;
     }
 
-    var c1r = colors[c1],
-        c1g = colors[c1 + 1],
-        c1b = colors[c1 + 2];
-    var c2r = colors[c2],
-        c2g = colors[c2 + 1],
-        c2b = colors[c2 + 2];
-    var c3r = colors[c3],
-        c3g = colors[c3 + 1],
-        c3b = colors[c3 + 2];
-    var minY = Math.round(y1),
-        maxY = Math.round(y3);
-    var xa, car, cag, cab;
-    var xb, cbr, cbg, cbb;
-
-    for (var y = minY; y <= maxY; y++) {
+    const c1r = colors[c1],
+          c1g = colors[c1 + 1],
+          c1b = colors[c1 + 2];
+    const c2r = colors[c2],
+          c2g = colors[c2 + 1],
+          c2b = colors[c2 + 2];
+    const c3r = colors[c3],
+          c3g = colors[c3 + 1],
+          c3b = colors[c3 + 2];
+    const minY = Math.round(y1),
+          maxY = Math.round(y3);
+    let xa, car, cag, cab;
+    let xb, cbr, cbg, cbb;
+
+    for (let y = minY; y <= maxY; y++) {
       if (y < y2) {
         let k;
 
@@ -182,11 +181,11 @@ var createMeshCanvas = function createMeshCanvasClosure() {
       cbr = c1r - (c1r - c3r) * k;
       cbg = c1g - (c1g - c3g) * k;
       cbb = c1b - (c1b - c3b) * k;
-      var x1_ = Math.round(Math.min(xa, xb));
-      var x2_ = Math.round(Math.max(xa, xb));
-      var j = rowSize * y + x1_ * 4;
+      const x1_ = Math.round(Math.min(xa, xb));
+      const x2_ = Math.round(Math.max(xa, xb));
+      let j = rowSize * y + x1_ * 4;
 
-      for (var x = x1_; x <= x2_; x++) {
+      for (let x = x1_; x <= x2_; x++) {
         k = (xa - x) / (xa - xb);
 
         if (k < 0) {
@@ -204,20 +203,20 @@ var createMeshCanvas = function createMeshCanvasClosure() {
   }
 
   function drawFigure(data, figure, context) {
-    var ps = figure.coords;
-    var cs = figure.colors;
-    var i, ii;
+    const ps = figure.coords;
+    const cs = figure.colors;
+    let i, ii;
 
     switch (figure.type) {
       case "lattice":
-        var verticesPerRow = figure.verticesPerRow;
-        var rows = Math.floor(ps.length / verticesPerRow) - 1;
-        var cols = verticesPerRow - 1;
+        const verticesPerRow = figure.verticesPerRow;
+        const rows = Math.floor(ps.length / verticesPerRow) - 1;
+        const cols = verticesPerRow - 1;
 
         for (i = 0; i < rows; i++) {
-          var q = i * verticesPerRow;
+          let q = i * verticesPerRow;
 
-          for (var j = 0; j < cols; j++, q++) {
+          for (let j = 0; j < cols; j++, q++) {
             drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);
             drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
           }
@@ -238,18 +237,18 @@ var createMeshCanvas = function createMeshCanvasClosure() {
   }
 
   function createMeshCanvas(bounds, combinesScale, coords, colors, figures, backgroundColor, cachedCanvases, webGLContext) {
-    var EXPECTED_SCALE = 1.1;
-    var MAX_PATTERN_SIZE = 3000;
-    var BORDER_SIZE = 2;
-    var offsetX = Math.floor(bounds[0]);
-    var offsetY = Math.floor(bounds[1]);
-    var boundsWidth = Math.ceil(bounds[2]) - offsetX;
-    var boundsHeight = Math.ceil(bounds[3]) - offsetY;
-    var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
-    var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
-    var scaleX = boundsWidth / width;
-    var scaleY = boundsHeight / height;
-    var context = {
+    const EXPECTED_SCALE = 1.1;
+    const MAX_PATTERN_SIZE = 3000;
+    const BORDER_SIZE = 2;
+    const offsetX = Math.floor(bounds[0]);
+    const offsetY = Math.floor(bounds[1]);
+    const boundsWidth = Math.ceil(bounds[2]) - offsetX;
+    const boundsHeight = Math.ceil(bounds[3]) - offsetY;
+    const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
+    const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
+    const scaleX = boundsWidth / width;
+    const scaleY = boundsHeight / height;
+    const context = {
       coords,
       colors,
       offsetX: -offsetX,
@@ -257,9 +256,9 @@ var createMeshCanvas = function createMeshCanvasClosure() {
       scaleX: 1 / scaleX,
       scaleY: 1 / scaleY
     };
-    var paddedWidth = width + BORDER_SIZE * 2;
-    var paddedHeight = height + BORDER_SIZE * 2;
-    var canvas, tmpCanvas, i, ii;
+    const paddedWidth = width + BORDER_SIZE * 2;
+    const paddedHeight = height + BORDER_SIZE * 2;
+    let canvas, tmpCanvas, i, ii;
 
     if (webGLContext.isEnabled) {
       canvas = webGLContext.drawFigures({
@@ -274,11 +273,11 @@ var createMeshCanvas = function createMeshCanvasClosure() {
       canvas = tmpCanvas.canvas;
     } else {
       tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight, false);
-      var tmpCtx = tmpCanvas.context;
-      var data = tmpCtx.createImageData(width, height);
+      const tmpCtx = tmpCanvas.context;
+      const data = tmpCtx.createImageData(width, height);
 
       if (backgroundColor) {
-        var bytes = data.data;
+        const bytes = data.data;
 
         for (i = 0, ii = bytes.length; i < ii; i += 4) {
           bytes[i] = backgroundColor[0];
@@ -310,18 +309,17 @@ var createMeshCanvas = function createMeshCanvasClosure() {
 
 ShadingIRs.Mesh = {
   fromIR: function Mesh_fromIR(raw) {
-    var coords = raw[2];
-    var colors = raw[3];
-    var figures = raw[4];
-    var bounds = raw[5];
-    var matrix = raw[6];
-    var bbox = raw[7];
-    var background = raw[8];
+    const coords = raw[2];
+    const colors = raw[3];
+    const figures = raw[4];
+    const bounds = raw[5];
+    const matrix = raw[6];
+    const bbox = raw[7];
+    const background = raw[8];
     return {
-      type: "Pattern",
       getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
         applyBoundingBox(ctx, bbox);
-        var scale;
+        let scale;
 
         if (shadingFill) {
           scale = _util.Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
@@ -329,13 +327,13 @@ ShadingIRs.Mesh = {
           scale = _util.Util.singularValueDecompose2dScale(owner.baseTransform);
 
           if (matrix) {
-            var matrixScale = _util.Util.singularValueDecompose2dScale(matrix);
+            const matrixScale = _util.Util.singularValueDecompose2dScale(matrix);
 
             scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];
           }
         }
 
-        var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords, colors, figures, shadingFill ? null : background, owner.cachedCanvases, owner.webGLContext);
+        const temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords, colors, figures, shadingFill ? null : background, owner.cachedCanvases, owner.webGLContext);
 
         if (!shadingFill) {
           ctx.setTransform.apply(ctx, owner.baseTransform);
@@ -355,7 +353,6 @@ ShadingIRs.Mesh = {
 ShadingIRs.Dummy = {
   fromIR: function Dummy_fromIR() {
     return {
-      type: "Pattern",
       getPattern: function Dummy_fromIR_getPattern() {
         return "hotpink";
       }
@@ -364,7 +361,7 @@ ShadingIRs.Dummy = {
 };
 
 function getShadingPatternFromIR(raw) {
-  var shadingIR = ShadingIRs[raw[0]];
+  const shadingIR = ShadingIRs[raw[0]];
 
   if (!shadingIR) {
     throw new Error(`Unknown IR type: ${raw[0]}`);
@@ -373,12 +370,12 @@ function getShadingPatternFromIR(raw) {
   return shadingIR.fromIR(raw);
 }
 
-var TilingPattern = function TilingPatternClosure() {
-  var PaintType = {
+const TilingPattern = function TilingPatternClosure() {
+  const PaintType = {
     COLORED: 1,
     UNCOLORED: 2
   };
-  var MAX_PATTERN_SIZE = 3000;
+  const MAX_PATTERN_SIZE = 3000;
 
   function TilingPattern(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
     this.operatorList = IR[2];
@@ -391,36 +388,35 @@ var TilingPattern = function TilingPatternClosure() {
     this.color = color;
     this.canvasGraphicsFactory = canvasGraphicsFactory;
     this.baseTransform = baseTransform;
-    this.type = "Pattern";
     this.ctx = ctx;
   }
 
   TilingPattern.prototype = {
     createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
-      var operatorList = this.operatorList;
-      var bbox = this.bbox;
-      var xstep = this.xstep;
-      var ystep = this.ystep;
-      var paintType = this.paintType;
-      var tilingType = this.tilingType;
-      var color = this.color;
-      var canvasGraphicsFactory = this.canvasGraphicsFactory;
+      const operatorList = this.operatorList;
+      const bbox = this.bbox;
+      const xstep = this.xstep;
+      const ystep = this.ystep;
+      const paintType = this.paintType;
+      const tilingType = this.tilingType;
+      const color = this.color;
+      const canvasGraphicsFactory = this.canvasGraphicsFactory;
       (0, _util.info)("TilingType: " + tilingType);
-      var x0 = bbox[0],
-          y0 = bbox[1],
-          x1 = bbox[2],
-          y1 = bbox[3];
+      const x0 = bbox[0],
+            y0 = bbox[1],
+            x1 = bbox[2],
+            y1 = bbox[3];
 
-      var matrixScale = _util.Util.singularValueDecompose2dScale(this.matrix);
+      const matrixScale = _util.Util.singularValueDecompose2dScale(this.matrix);
 
-      var curMatrixScale = _util.Util.singularValueDecompose2dScale(this.baseTransform);
+      const curMatrixScale = _util.Util.singularValueDecompose2dScale(this.baseTransform);
 
-      var combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]];
-      var dimx = this.getSizeAndScale(xstep, this.ctx.canvas.width, combinedScale[0]);
-      var dimy = this.getSizeAndScale(ystep, this.ctx.canvas.height, combinedScale[1]);
-      var tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size, true);
-      var tmpCtx = tmpCanvas.context;
-      var graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
+      const combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]];
+      const dimx = this.getSizeAndScale(xstep, this.ctx.canvas.width, combinedScale[0]);
+      const dimy = this.getSizeAndScale(ystep, this.ctx.canvas.height, combinedScale[1]);
+      const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size, true);
+      const tmpCtx = tmpCanvas.context;
+      const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
       graphics.groupLevel = owner.groupLevel;
       this.setFillAndStrokeStyleToContext(graphics, paintType, color);
       graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
@@ -433,8 +429,8 @@ var TilingPattern = function TilingPatternClosure() {
     },
     getSizeAndScale: function TilingPattern_getSizeAndScale(step, realOutputSize, scale) {
       step = Math.abs(step);
-      var maxSize = Math.max(MAX_PATTERN_SIZE, realOutputSize);
-      var size = Math.ceil(step * scale);
+      const maxSize = Math.max(MAX_PATTERN_SIZE, realOutputSize);
+      let size = Math.ceil(step * scale);
 
       if (size >= maxSize) {
         size = maxSize;
@@ -449,8 +445,8 @@ var TilingPattern = function TilingPatternClosure() {
     },
     clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
       if (Array.isArray(bbox) && bbox.length === 4) {
-        var bboxWidth = x1 - x0;
-        var bboxHeight = y1 - y0;
+        const bboxWidth = x1 - x0;
+        const bboxHeight = y1 - y0;
         graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
         graphics.clip();
         graphics.endPath();
@@ -462,7 +458,7 @@ var TilingPattern = function TilingPatternClosure() {
 
       switch (paintType) {
         case PaintType.COLORED:
-          var ctx = this.ctx;
+          const ctx = this.ctx;
           context.fillStyle = ctx.fillStyle;
           context.strokeStyle = ctx.strokeStyle;
           current.fillColor = ctx.fillStyle;
@@ -470,7 +466,7 @@ var TilingPattern = function TilingPatternClosure() {
           break;
 
         case PaintType.UNCOLORED:
-          var cssColor = _util.Util.makeCssRgb(color[0], color[1], color[2]);
+          const cssColor = _util.Util.makeHexColor(color[0], color[1], color[2]);
 
           context.fillStyle = cssColor;
           context.strokeStyle = cssColor;
@@ -486,7 +482,7 @@ var TilingPattern = function TilingPatternClosure() {
       ctx = this.ctx;
       ctx.setTransform.apply(ctx, this.baseTransform);
       ctx.transform.apply(ctx, this.matrix);
-      var temporaryPatternCanvas = this.createPatternCanvas(owner);
+      const temporaryPatternCanvas = this.createPatternCanvas(owner);
       return ctx.createPattern(temporaryPatternCanvas, "repeat");
     }
   };

+ 5 - 5
lib/display/svg.js

@@ -893,7 +893,7 @@ exports.SVGGraphics = SVGGraphics;
         this.embeddedFonts[fontObj.loadedName] = fontObj;
       }
 
-      current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix : _util.FONT_IDENTITY_MATRIX;
+      current.fontMatrix = fontObj.fontMatrix || _util.FONT_IDENTITY_MATRIX;
       let bold = "normal";
 
       if (fontObj.black) {
@@ -924,7 +924,7 @@ exports.SVGGraphics = SVGGraphics;
     endText() {
       const current = this.current;
 
-      if (current.textRenderingMode & _util.TextRenderingMode.ADD_TO_PATH_FLAG && current.txtElement && current.txtElement.hasChildNodes()) {
+      if (current.textRenderingMode & _util.TextRenderingMode.ADD_TO_PATH_FLAG && current.txtElement?.hasChildNodes()) {
         current.element = current.txtElement;
         this.clip("nonzero");
         this.endPath();
@@ -954,7 +954,7 @@ exports.SVGGraphics = SVGGraphics;
     }
 
     setStrokeRGBColor(r, g, b) {
-      this.current.strokeColor = _util.Util.makeCssRgb(r, g, b);
+      this.current.strokeColor = _util.Util.makeHexColor(r, g, b);
     }
 
     setFillAlpha(fillAlpha) {
@@ -962,7 +962,7 @@ exports.SVGGraphics = SVGGraphics;
     }
 
     setFillRGBColor(r, g, b) {
-      this.current.fillColor = _util.Util.makeCssRgb(r, g, b);
+      this.current.fillColor = _util.Util.makeHexColor(r, g, b);
       this.current.tspan = this.svgFactory.createElement("svg:tspan");
       this.current.xcoords = [];
       this.current.ycoords = [];
@@ -1050,7 +1050,7 @@ exports.SVGGraphics = SVGGraphics;
       this.transformMatrix = matrix;
 
       if (paintType === 2) {
-        const cssColor = _util.Util.makeCssRgb(...color);
+        const cssColor = _util.Util.makeHexColor(...color);
 
         this.current.fillColor = cssColor;
         this.current.strokeColor = cssColor;

+ 54 - 54
lib/display/text_layer.js

@@ -28,17 +28,17 @@ exports.renderTextLayer = void 0;
 
 var _util = require("../shared/util.js");
 
-var renderTextLayer = function renderTextLayerClosure() {
-  var MAX_TEXT_DIVS_TO_RENDER = 100000;
-  var NonWhitespaceRegexp = /\S/;
+const renderTextLayer = function renderTextLayerClosure() {
+  const MAX_TEXT_DIVS_TO_RENDER = 100000;
+  const NonWhitespaceRegexp = /\S/;
 
   function isAllWhitespace(str) {
     return !NonWhitespaceRegexp.test(str);
   }
 
   function appendText(task, geom, styles) {
-    var textDiv = document.createElement("span");
-    var textDivProperties = {
+    const textDiv = document.createElement("span");
+    const textDivProperties = {
       angle: 0,
       canvasWidth: 0,
       isWhitespace: false,
@@ -60,17 +60,17 @@ var renderTextLayer = function renderTextLayerClosure() {
       return;
     }
 
-    var tx = _util.Util.transform(task._viewport.transform, geom.transform);
+    const tx = _util.Util.transform(task._viewport.transform, geom.transform);
 
-    var angle = Math.atan2(tx[1], tx[0]);
-    var style = styles[geom.fontName];
+    let angle = Math.atan2(tx[1], tx[0]);
+    const style = styles[geom.fontName];
 
     if (style.vertical) {
       angle += Math.PI / 2;
     }
 
-    var fontHeight = Math.sqrt(tx[2] * tx[2] + tx[3] * tx[3]);
-    var fontAscent = fontHeight;
+    const fontHeight = Math.sqrt(tx[2] * tx[2] + tx[3] * tx[3]);
+    let fontAscent = fontHeight;
 
     if (style.ascent) {
       fontAscent = style.ascent * fontAscent;
@@ -130,7 +130,7 @@ var renderTextLayer = function renderTextLayerClosure() {
     }
 
     if (task._enhanceTextSelection) {
-      var angleCos = 1,
+      let angleCos = 1,
           angleSin = 0;
 
       if (angle !== 0) {
@@ -138,9 +138,9 @@ var renderTextLayer = function renderTextLayerClosure() {
         angleSin = Math.sin(angle);
       }
 
-      var divWidth = (style.vertical ? geom.height : geom.width) * task._viewport.scale;
-      var divHeight = fontHeight;
-      var m, b;
+      const divWidth = (style.vertical ? geom.height : geom.width) * task._viewport.scale;
+      const divHeight = fontHeight;
+      let m, b;
 
       if (angle !== 0) {
         m = [angleCos, angleSin, -angleSin, angleCos, left, top];
@@ -166,9 +166,9 @@ var renderTextLayer = function renderTextLayerClosure() {
       return;
     }
 
-    var textDivs = task._textDivs;
-    var capability = task._capability;
-    var textDivsLength = textDivs.length;
+    const textDivs = task._textDivs;
+    const capability = task._capability;
+    const textDivsLength = textDivs.length;
 
     if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) {
       task._renderingDone = true;
@@ -177,7 +177,7 @@ var renderTextLayer = function renderTextLayerClosure() {
     }
 
     if (!task._textContentStream) {
-      for (var i = 0; i < textDivsLength; i++) {
+      for (let i = 0; i < textDivsLength; i++) {
         task._layoutText(textDivs[i]);
       }
     }
@@ -201,14 +201,14 @@ var renderTextLayer = function renderTextLayerClosure() {
   }
 
   function expand(task) {
-    var bounds = task._bounds;
-    var viewport = task._viewport;
-    var expanded = expandBounds(viewport.width, viewport.height, bounds);
+    const bounds = task._bounds;
+    const viewport = task._viewport;
+    const expanded = expandBounds(viewport.width, viewport.height, bounds);
 
-    for (var i = 0; i < expanded.length; i++) {
-      var div = bounds[i].div;
+    for (let i = 0; i < expanded.length; i++) {
+      const div = bounds[i].div;
 
-      var divProperties = task._textDivProperties.get(div);
+      const divProperties = task._textDivProperties.get(div);
 
       if (divProperties.angle === 0) {
         divProperties.paddingLeft = bounds[i].left - expanded[i].left;
@@ -221,15 +221,15 @@ var renderTextLayer = function renderTextLayerClosure() {
         continue;
       }
 
-      var e = expanded[i],
-          b = bounds[i];
-      var m = b.m,
-          c = m[0],
-          s = m[1];
-      var points = [[0, 0], [0, b.size[1]], [b.size[0], 0], b.size];
-      var ts = new Float64Array(64);
+      const e = expanded[i],
+            b = bounds[i];
+      const m = b.m,
+            c = m[0],
+            s = m[1];
+      const points = [[0, 0], [0, b.size[1]], [b.size[0], 0], b.size];
+      const ts = new Float64Array(64);
       points.forEach(function (p, j) {
-        var t = _util.Util.applyTransform(p, m);
+        const t = _util.Util.applyTransform(p, m);
 
         ts[j + 0] = c && (e.left - t[0]) / c;
         ts[j + 4] = s && (e.top - t[1]) / s;
@@ -248,7 +248,7 @@ var renderTextLayer = function renderTextLayerClosure() {
         ts[j + 56] = s && (e.right - t[0]) / s;
         ts[j + 60] = c && (e.bottom - t[1]) / -c;
       });
-      var boxScale = 1 + Math.min(Math.abs(c), Math.abs(s));
+      const boxScale = 1 + Math.min(Math.abs(c), Math.abs(s));
       divProperties.paddingLeft = findPositiveMin(ts, 32, 16) / boxScale;
       divProperties.paddingTop = findPositiveMin(ts, 48, 16) / boxScale;
       divProperties.paddingRight = findPositiveMin(ts, 0, 16) / boxScale;
@@ -259,7 +259,7 @@ var renderTextLayer = function renderTextLayerClosure() {
   }
 
   function expandBounds(width, height, boxes) {
-    var bounds = boxes.map(function (box, i) {
+    const bounds = boxes.map(function (box, i) {
       return {
         x1: box.left,
         y1: box.top,
@@ -271,9 +271,9 @@ var renderTextLayer = function renderTextLayerClosure() {
       };
     });
     expandBoundsLTR(width, bounds);
-    var expanded = new Array(boxes.length);
+    const expanded = new Array(boxes.length);
     bounds.forEach(function (b) {
-      var i = b.index;
+      const i = b.index;
       expanded[i] = {
         left: b.x1New,
         top: 0,
@@ -282,8 +282,8 @@ var renderTextLayer = function renderTextLayerClosure() {
       };
     });
     boxes.map(function (box, i) {
-      var e = expanded[i],
-          b = bounds[i];
+      const e = expanded[i],
+            b = bounds[i];
       b.x1 = box.top;
       b.y1 = width - e.right;
       b.x2 = box.bottom;
@@ -294,7 +294,7 @@ var renderTextLayer = function renderTextLayerClosure() {
     });
     expandBoundsLTR(height, bounds);
     bounds.forEach(function (b) {
-      var i = b.index;
+      const i = b.index;
       expanded[i].top = b.x1New;
       expanded[i].bottom = b.x2New;
     });
@@ -305,7 +305,7 @@ var renderTextLayer = function renderTextLayerClosure() {
     bounds.sort(function (a, b) {
       return a.x1 - b.x1 || a.index - b.index;
     });
-    var fakeBoundary = {
+    const fakeBoundary = {
       x1: -Infinity,
       y1: -Infinity,
       x2: 0,
@@ -314,33 +314,33 @@ var renderTextLayer = function renderTextLayerClosure() {
       x1New: 0,
       x2New: 0
     };
-    var horizon = [{
+    const horizon = [{
       start: -Infinity,
       end: Infinity,
       boundary: fakeBoundary
     }];
     bounds.forEach(function (boundary) {
-      var i = 0;
+      let i = 0;
 
       while (i < horizon.length && horizon[i].end <= boundary.y1) {
         i++;
       }
 
-      var j = horizon.length - 1;
+      let j = horizon.length - 1;
 
       while (j >= 0 && horizon[j].start >= boundary.y2) {
         j--;
       }
 
-      var horizonPart, affectedBoundary;
-      var q,
+      let horizonPart, affectedBoundary;
+      let q,
           k,
           maxXNew = -Infinity;
 
       for (q = i; q <= j; q++) {
         horizonPart = horizon[q];
         affectedBoundary = horizonPart.boundary;
-        var xNew;
+        let xNew;
 
         if (affectedBoundary.x2 > boundary.x1) {
           xNew = affectedBoundary.index > boundary.index ? affectedBoundary.x1New : boundary.x1;
@@ -374,13 +374,13 @@ var renderTextLayer = function renderTextLayerClosure() {
         }
       }
 
-      var changedHorizon = [],
-          lastBoundary = null;
+      const changedHorizon = [];
+      let lastBoundary = null;
 
       for (q = i; q <= j; q++) {
         horizonPart = horizon[q];
         affectedBoundary = horizonPart.boundary;
-        var useBoundary = affectedBoundary.x2 > boundary.x2 ? affectedBoundary : boundary;
+        const useBoundary = affectedBoundary.x2 > boundary.x2 ? affectedBoundary : boundary;
 
         if (lastBoundary === useBoundary) {
           changedHorizon[changedHorizon.length - 1].end = horizonPart.end;
@@ -420,7 +420,7 @@ var renderTextLayer = function renderTextLayerClosure() {
           continue;
         }
 
-        var used = false;
+        let used = false;
 
         for (k = i - 1; !used && k >= 0 && horizon[k].start >= affectedBoundary.y1; k--) {
           used = horizon[k].boundary === affectedBoundary;
@@ -442,7 +442,7 @@ var renderTextLayer = function renderTextLayerClosure() {
       Array.prototype.splice.apply(horizon, [i, j - i + 1].concat(changedHorizon));
     });
     horizon.forEach(function (horizonPart) {
-      var affectedBoundary = horizonPart.boundary;
+      const affectedBoundary = horizonPart.boundary;
 
       if (affectedBoundary.x2New === undefined) {
         affectedBoundary.x2New = Math.max(width, affectedBoundary.x2);
@@ -467,7 +467,7 @@ var renderTextLayer = function renderTextLayerClosure() {
     this._textDivs = textDivs || [];
     this._textContentItemsStr = textContentItemsStr || [];
     this._enhanceTextSelection = !!enhanceTextSelection;
-    this._fontInspectorEnabled = !!(globalThis.FontInspector && globalThis.FontInspector.enabled);
+    this._fontInspectorEnabled = !!globalThis.FontInspector?.enabled;
     this._reader = null;
     this._layoutTextLastFontSize = null;
     this._layoutTextLastFontFamily = null;
@@ -635,7 +635,7 @@ var renderTextLayer = function renderTextLayerClosure() {
       const transformBuf = [],
             paddingBuf = [];
 
-      for (var i = 0, ii = this._textDivs.length; i < ii; i++) {
+      for (let i = 0, ii = this._textDivs.length; i < ii; i++) {
         const div = this._textDivs[i];
 
         const divProps = this._textDivProperties.get(div);
@@ -692,7 +692,7 @@ var renderTextLayer = function renderTextLayerClosure() {
   };
 
   function renderTextLayer(renderParameters) {
-    var task = new TextLayerRenderTask({
+    const task = new TextLayerRenderTask({
       textContent: renderParameters.textContent,
       textContentStream: renderParameters.textContentStream,
       container: renderParameters.container,

+ 4 - 4
lib/display/transport_stream.js

@@ -35,7 +35,7 @@ class PDFDataTransportStream {
     this._progressiveDone = params.progressiveDone || false;
     const initialData = params.initialData;
 
-    if (initialData && initialData.length > 0) {
+    if (initialData?.length > 0) {
       const buffer = new Uint8Array(initialData).buffer;
 
       this._queuedChunks.push(buffer);
@@ -100,14 +100,14 @@ class PDFDataTransportStream {
   }
 
   get _progressiveDataLength() {
-    return this._fullRequestReader ? this._fullRequestReader._loaded : 0;
+    return this._fullRequestReader?._loaded ?? 0;
   }
 
   _onProgress(evt) {
     if (evt.total === undefined) {
       const firstReader = this._rangeReaders[0];
 
-      if (firstReader && firstReader.onProgress) {
+      if (firstReader?.onProgress) {
         firstReader.onProgress({
           loaded: evt.loaded
         });
@@ -115,7 +115,7 @@ class PDFDataTransportStream {
     } else {
       const fullReader = this._fullRequestReader;
 
-      if (fullReader && fullReader.onProgress) {
+      if (fullReader?.onProgress) {
         fullReader.onProgress({
           loaded: evt.loaded,
           total: evt.total

+ 65 - 68
lib/display/webgl.js

@@ -71,15 +71,15 @@ class WebGLContext {
 
 exports.WebGLContext = WebGLContext;
 
-var WebGLUtils = function WebGLUtilsClosure() {
+const WebGLUtils = function WebGLUtilsClosure() {
   function loadShader(gl, code, shaderType) {
-    var shader = gl.createShader(shaderType);
+    const shader = gl.createShader(shaderType);
     gl.shaderSource(shader, code);
     gl.compileShader(shader);
-    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+    const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
 
     if (!compiled) {
-      var errorMsg = gl.getShaderInfoLog(shader);
+      const errorMsg = gl.getShaderInfoLog(shader);
       throw new Error("Error during shader compilation: " + errorMsg);
     }
 
@@ -95,17 +95,17 @@ var WebGLUtils = function WebGLUtilsClosure() {
   }
 
   function createProgram(gl, shaders) {
-    var program = gl.createProgram();
+    const program = gl.createProgram();
 
-    for (var i = 0, ii = shaders.length; i < ii; ++i) {
+    for (let i = 0, ii = shaders.length; i < ii; ++i) {
       gl.attachShader(program, shaders[i]);
     }
 
     gl.linkProgram(program);
-    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
+    const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
 
     if (!linked) {
-      var errorMsg = gl.getProgramInfoLog(program);
+      const errorMsg = gl.getProgramInfoLog(program);
       throw new Error("Error during program linking: " + errorMsg);
     }
 
@@ -114,7 +114,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
 
   function createTexture(gl, image, textureId) {
     gl.activeTexture(textureId);
-    var texture = gl.createTexture();
+    const texture = gl.createTexture();
     gl.bindTexture(gl.TEXTURE_2D, texture);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
@@ -124,7 +124,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
     return texture;
   }
 
-  var currentGL, currentCanvas;
+  let currentGL, currentCanvas;
 
   function generateGL() {
     if (currentGL) {
@@ -137,7 +137,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
     });
   }
 
-  var smaskVertexShaderCode = "\
+  const smaskVertexShaderCode = "\
   attribute vec2 a_position;                                    \
   attribute vec2 a_texCoord;                                    \
                                                                 \
@@ -151,7 +151,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
                                                                 \
     v_texCoord = a_texCoord;                                    \
   }                                                             ";
-  var smaskFragmentShaderCode = "\
+  const smaskFragmentShaderCode = "\
   precision mediump float;                                      \
                                                                 \
   uniform vec4 u_backdrop;                                      \
@@ -179,30 +179,29 @@ var WebGLUtils = function WebGLUtilsClosure() {
     imageColor.rgb *= imageColor.a;                             \
     gl_FragColor = imageColor;                                  \
   }                                                             ";
-  var smaskCache = null;
+  let smaskCache = null;
 
   function initSmaskGL() {
-    var canvas, gl;
     generateGL();
-    canvas = currentCanvas;
+    const canvas = currentCanvas;
     currentCanvas = null;
-    gl = currentGL;
+    const gl = currentGL;
     currentGL = null;
-    var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
-    var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
-    var program = createProgram(gl, [vertexShader, fragmentShader]);
+    const vertexShader = createVertexShader(gl, smaskVertexShaderCode);
+    const fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
+    const program = createProgram(gl, [vertexShader, fragmentShader]);
     gl.useProgram(program);
-    var cache = {};
+    const cache = {};
     cache.gl = gl;
     cache.canvas = canvas;
     cache.resolutionLocation = gl.getUniformLocation(program, "u_resolution");
     cache.positionLocation = gl.getAttribLocation(program, "a_position");
     cache.backdropLocation = gl.getUniformLocation(program, "u_backdrop");
     cache.subtypeLocation = gl.getUniformLocation(program, "u_subtype");
-    var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
-    var texLayerLocation = gl.getUniformLocation(program, "u_image");
-    var texMaskLocation = gl.getUniformLocation(program, "u_mask");
-    var texCoordBuffer = gl.createBuffer();
+    const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
+    const texLayerLocation = gl.getUniformLocation(program, "u_image");
+    const texMaskLocation = gl.getUniformLocation(program, "u_mask");
+    const texCoordBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), gl.STATIC_DRAW);
     gl.enableVertexAttribArray(texCoordLocation);
@@ -213,16 +212,16 @@ var WebGLUtils = function WebGLUtilsClosure() {
   }
 
   function composeSMask(layer, mask, properties) {
-    var width = layer.width,
-        height = layer.height;
+    const width = layer.width,
+          height = layer.height;
 
     if (!smaskCache) {
       initSmaskGL();
     }
 
-    var cache = smaskCache,
-        canvas = cache.canvas,
-        gl = cache.gl;
+    const cache = smaskCache,
+          canvas = cache.canvas,
+          gl = cache.gl;
     canvas.width = width;
     canvas.height = height;
     gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
@@ -235,9 +234,9 @@ var WebGLUtils = function WebGLUtilsClosure() {
     }
 
     gl.uniform1i(cache.subtypeLocation, properties.subtype === "Luminosity" ? 1 : 0);
-    var texture = createTexture(gl, layer, gl.TEXTURE0);
-    var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
-    var buffer = gl.createBuffer();
+    const texture = createTexture(gl, layer, gl.TEXTURE0);
+    const maskTexture = createTexture(gl, mask, gl.TEXTURE1);
+    const buffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
     gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, width, 0, 0, height, 0, height, width, 0, width, height]), gl.STATIC_DRAW);
     gl.enableVertexAttribArray(cache.positionLocation);
@@ -254,7 +253,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
     return canvas;
   }
 
-  var figuresVertexShaderCode = "\
+  const figuresVertexShaderCode = "\
   attribute vec2 a_position;                                    \
   attribute vec3 a_color;                                       \
                                                                 \
@@ -271,7 +270,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
                                                                 \
     v_color = vec4(a_color / 255.0, 1.0);                       \
   }                                                             ";
-  var figuresFragmentShaderCode = "\
+  const figuresFragmentShaderCode = "\
   precision mediump float;                                      \
                                                                 \
   varying vec4 v_color;                                         \
@@ -279,20 +278,19 @@ var WebGLUtils = function WebGLUtilsClosure() {
   void main() {                                                 \
     gl_FragColor = v_color;                                     \
   }                                                             ";
-  var figuresCache = null;
+  let figuresCache = null;
 
   function initFiguresGL() {
-    var canvas, gl;
     generateGL();
-    canvas = currentCanvas;
+    const canvas = currentCanvas;
     currentCanvas = null;
-    gl = currentGL;
+    const gl = currentGL;
     currentGL = null;
-    var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
-    var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
-    var program = createProgram(gl, [vertexShader, fragmentShader]);
+    const vertexShader = createVertexShader(gl, figuresVertexShaderCode);
+    const fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
+    const program = createProgram(gl, [vertexShader, fragmentShader]);
     gl.useProgram(program);
-    var cache = {};
+    const cache = {};
     cache.gl = gl;
     cache.canvas = canvas;
     cache.resolutionLocation = gl.getUniformLocation(program, "u_resolution");
@@ -308,20 +306,19 @@ var WebGLUtils = function WebGLUtilsClosure() {
       initFiguresGL();
     }
 
-    var cache = figuresCache,
-        canvas = cache.canvas,
-        gl = cache.gl;
+    const cache = figuresCache,
+          canvas = cache.canvas,
+          gl = cache.gl;
     canvas.width = width;
     canvas.height = height;
     gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
     gl.uniform2f(cache.resolutionLocation, width, height);
-    var count = 0;
-    var i, ii, rows;
+    let count = 0;
 
-    for (i = 0, ii = figures.length; i < ii; i++) {
+    for (let i = 0, ii = figures.length; i < ii; i++) {
       switch (figures[i].type) {
         case "lattice":
-          rows = figures[i].coords.length / figures[i].verticesPerRow | 0;
+          const rows = figures[i].coords.length / figures[i].verticesPerRow | 0;
           count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
           break;
 
@@ -331,27 +328,27 @@ var WebGLUtils = function WebGLUtilsClosure() {
       }
     }
 
-    var coords = new Float32Array(count * 2);
-    var colors = new Uint8Array(count * 3);
-    var coordsMap = context.coords,
-        colorsMap = context.colors;
-    var pIndex = 0,
+    const coords = new Float32Array(count * 2);
+    const colors = new Uint8Array(count * 3);
+    const coordsMap = context.coords,
+          colorsMap = context.colors;
+    let pIndex = 0,
         cIndex = 0;
 
-    for (i = 0, ii = figures.length; i < ii; i++) {
-      var figure = figures[i],
-          ps = figure.coords,
-          cs = figure.colors;
+    for (let i = 0, ii = figures.length; i < ii; i++) {
+      const figure = figures[i],
+            ps = figure.coords,
+            cs = figure.colors;
 
       switch (figure.type) {
         case "lattice":
-          var cols = figure.verticesPerRow;
-          rows = ps.length / cols | 0;
+          const cols = figure.verticesPerRow;
+          const rows = ps.length / cols | 0;
 
-          for (var row = 1; row < rows; row++) {
-            var offset = row * cols + 1;
+          for (let row = 1; row < rows; row++) {
+            let offset = row * cols + 1;
 
-            for (var col = 1; col < cols; col++, offset++) {
+            for (let col = 1; col < cols; col++, offset++) {
               coords[pIndex] = coordsMap[ps[offset - cols - 1]];
               coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
               coords[pIndex + 2] = coordsMap[ps[offset - cols]];
@@ -390,7 +387,7 @@ var WebGLUtils = function WebGLUtilsClosure() {
           break;
 
         case "triangles":
-          for (var j = 0, jj = ps.length; j < jj; j++) {
+          for (let j = 0, jj = ps.length; j < jj; j++) {
             coords[pIndex] = coordsMap[ps[j]];
             coords[pIndex + 1] = coordsMap[ps[j] + 1];
             colors[cIndex] = colorsMap[cs[j]];
@@ -411,12 +408,12 @@ var WebGLUtils = function WebGLUtilsClosure() {
     }
 
     gl.clear(gl.COLOR_BUFFER_BIT);
-    var coordsBuffer = gl.createBuffer();
+    const coordsBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
     gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
     gl.enableVertexAttribArray(cache.positionLocation);
     gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
-    var colorsBuffer = gl.createBuffer();
+    const colorsBuffer = gl.createBuffer();
     gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
     gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
     gl.enableVertexAttribArray(cache.colorLocation);
@@ -444,12 +441,12 @@ var WebGLUtils = function WebGLUtilsClosure() {
     drawFigures,
 
     cleanup() {
-      if (smaskCache && smaskCache.canvas) {
+      if (smaskCache?.canvas) {
         smaskCache.canvas.width = 0;
         smaskCache.canvas.height = 0;
       }
 
-      if (figuresCache && figuresCache.canvas) {
+      if (figuresCache?.canvas) {
         figuresCache.canvas.width = 0;
         figuresCache.canvas.height = 0;
       }

+ 9 - 4
lib/examples/node/domstubs.js

@@ -82,9 +82,11 @@ function DOMElement(name) {
   if (name === "style") {
     this.sheet = {
       cssRules: [],
-      insertRule: function (rule) {
+
+      insertRule(rule) {
         this.cssRules.push(rule);
       }
+
     };
   }
 }
@@ -248,20 +250,23 @@ const document = {
     return this;
   },
 
-  createElementNS: function (NS, element) {
+  createElementNS(NS, element) {
     var elObject = new DOMElement(element);
     return elObject;
   },
-  createElement: function (element) {
+
+  createElement(element) {
     return this.createElementNS("", element);
   },
-  getElementsByTagName: function (element) {
+
+  getElementsByTagName(element) {
     if (element === "head") {
       return [this.head || (this.head = new DOMElement("head"))];
     }
 
     return [];
   }
+
 };
 
 function Image() {

+ 2 - 2
lib/pdf.js

@@ -233,8 +233,8 @@ var _text_layer = require("./display/text_layer.js");
 
 var _svg = require("./display/svg.js");
 
-const pdfjsVersion = '2.6.347';
-const pdfjsBuild = '3be9c65f';
+const pdfjsVersion = '2.7.570';
+const pdfjsBuild = 'f2c7338b0';
 {
   const {
     isNodeJS

File diff suppressed because it is too large
+ 84 - 0
lib/pdf.sandbox.js


+ 2 - 2
lib/pdf.worker.js

@@ -33,5 +33,5 @@ Object.defineProperty(exports, "WorkerMessageHandler", {
 
 var _worker = require("./core/worker.js");
 
-const pdfjsVersion = '2.6.347';
-const pdfjsBuild = '3be9c65f';
+const pdfjsVersion = '2.7.570';
+const pdfjsBuild = 'f2c7338b0';

+ 84 - 0
lib/shared/scripting_utils.js

@@ -0,0 +1,84 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2020 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.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.ColorConverters = void 0;
+
+function makeColorComp(n) {
+  return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0");
+}
+
+class ColorConverters {
+  static CMYK_G([c, y, m, k]) {
+    return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];
+  }
+
+  static G_CMYK([g]) {
+    return ["CMYK", 0, 0, 0, 1 - g];
+  }
+
+  static G_RGB([g]) {
+    return ["RGB", g, g, g];
+  }
+
+  static G_HTML([g]) {
+    const G = makeColorComp(g);
+    return `#${G}${G}${G}`;
+  }
+
+  static RGB_G([r, g, b]) {
+    return ["G", 0.3 * r + 0.59 * g + 0.11 * b];
+  }
+
+  static RGB_HTML([r, g, b]) {
+    const R = makeColorComp(r);
+    const G = makeColorComp(g);
+    const B = makeColorComp(b);
+    return `#${R}${G}${B}`;
+  }
+
+  static T_HTML() {
+    return "#00000000";
+  }
+
+  static CMYK_RGB([c, y, m, k]) {
+    return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];
+  }
+
+  static CMYK_HTML(components) {
+    return this.RGB_HTML(this.CMYK_RGB(components));
+  }
+
+  static RGB_CMYK([r, g, b]) {
+    const c = 1 - r;
+    const m = 1 - g;
+    const y = 1 - b;
+    const k = Math.min(c, m, y);
+    return ["CMYK", c, m, y, k];
+  }
+
+}
+
+exports.ColorConverters = ColorConverters;

+ 129 - 14
lib/shared/util.js

@@ -29,28 +29,33 @@ exports.arraysToBytes = arraysToBytes;
 exports.assert = assert;
 exports.bytesToString = bytesToString;
 exports.createPromiseCapability = createPromiseCapability;
+exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
+exports.encodeToXmlString = encodeToXmlString;
 exports.escapeString = escapeString;
 exports.getModificationDate = getModificationDate;
 exports.getVerbosityLevel = getVerbosityLevel;
 exports.info = info;
 exports.isArrayBuffer = isArrayBuffer;
 exports.isArrayEqual = isArrayEqual;
+exports.isAscii = isAscii;
 exports.isBool = isBool;
 exports.isNum = isNum;
-exports.isString = isString;
 exports.isSameOrigin = isSameOrigin;
-exports.createValidAbsoluteUrl = createValidAbsoluteUrl;
+exports.isString = isString;
+exports.objectFromEntries = objectFromEntries;
+exports.objectSize = objectSize;
 exports.removeNullCharacters = removeNullCharacters;
 exports.setVerbosityLevel = setVerbosityLevel;
 exports.shadow = shadow;
 exports.string32 = string32;
 exports.stringToBytes = stringToBytes;
 exports.stringToPDFString = stringToPDFString;
+exports.stringToUTF16BEString = stringToUTF16BEString;
 exports.stringToUTF8String = stringToUTF8String;
+exports.unreachable = unreachable;
 exports.utf8StringToString = utf8StringToString;
 exports.warn = warn;
-exports.unreachable = unreachable;
-exports.IsEvalSupportedCached = exports.IsLittleEndianCached = exports.createObjectURL = exports.FormatError = exports.Util = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.MissingPDFException = exports.InvalidPDFException = exports.AbortException = exports.CMapCompressionType = exports.ImageKind = exports.FontType = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.UNSUPPORTED_FEATURES = exports.VerbosityLevel = exports.OPS = exports.IDENTITY_MATRIX = exports.FONT_IDENTITY_MATRIX = exports.BaseException = void 0;
+exports.VerbosityLevel = exports.Util = exports.UNSUPPORTED_FEATURES = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.PageActionEventType = exports.OPS = exports.MissingPDFException = exports.IsLittleEndianCached = exports.IsEvalSupportedCached = exports.InvalidPDFException = exports.ImageKind = exports.IDENTITY_MATRIX = exports.FormatError = exports.FontType = exports.FONT_IDENTITY_MATRIX = exports.DocumentActionEventType = exports.createObjectURL = exports.CMapCompressionType = exports.BaseException = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.AnnotationActionEventType = exports.AbortException = void 0;
 
 require("./compatibility.js");
 
@@ -183,6 +188,36 @@ const AnnotationBorderStyleType = {
   UNDERLINE: 5
 };
 exports.AnnotationBorderStyleType = AnnotationBorderStyleType;
+const AnnotationActionEventType = {
+  E: "Mouse Enter",
+  X: "Mouse Exit",
+  D: "Mouse Down",
+  U: "Mouse Up",
+  Fo: "Focus",
+  Bl: "Blur",
+  PO: "PageOpen",
+  PC: "PageClose",
+  PV: "PageVisible",
+  PI: "PageInvisible",
+  K: "Keystroke",
+  F: "Format",
+  V: "Validate",
+  C: "Calculate"
+};
+exports.AnnotationActionEventType = AnnotationActionEventType;
+const DocumentActionEventType = {
+  WC: "WillClose",
+  WS: "WillSave",
+  DS: "DidSave",
+  WP: "WillPrint",
+  DP: "DidPrint"
+};
+exports.DocumentActionEventType = DocumentActionEventType;
+const PageActionEventType = {
+  O: "PageOpen",
+  C: "PageClose"
+};
+exports.PageActionEventType = PageActionEventType;
 const StreamType = {
   UNKNOWN: "UNKNOWN",
   FLATE: "FLATE",
@@ -591,6 +626,14 @@ function string32(value) {
   return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
 }
 
+function objectSize(obj) {
+  return Object.keys(obj).length;
+}
+
+function objectFromEntries(iterable) {
+  return Object.assign(Object.create(null), Object.fromEntries(iterable));
+}
+
 function isLittleEndian() {
   const buffer8 = new Uint8Array(4);
   buffer8[0] = 1;
@@ -622,14 +665,11 @@ const IsEvalSupportedCached = {
 
 };
 exports.IsEvalSupportedCached = IsEvalSupportedCached;
-const rgbBuf = ["rgb(", 0, ",", 0, ",", 0, ")"];
+const hexNumbers = [...Array(256).keys()].map(n => n.toString(16).padStart(2, "0"));
 
 class Util {
-  static makeCssRgb(r, g, b) {
-    rgbBuf[1] = r;
-    rgbBuf[3] = g;
-    rgbBuf[5] = b;
-    return rgbBuf.join("");
+  static makeHexColor(r, g, b) {
+    return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
   }
 
   static transform(m1, m2) {
@@ -751,7 +791,31 @@ function stringToPDFString(str) {
 }
 
 function escapeString(str) {
-  return str.replace(/([\(\)\\])/g, "\\$1");
+  return str.replace(/([()\\\n\r])/g, match => {
+    if (match === "\n") {
+      return "\\n";
+    } else if (match === "\r") {
+      return "\\r";
+    }
+
+    return `\\${match}`;
+  });
+}
+
+function isAscii(str) {
+  return /^[\x00-\x7F]*$/.test(str);
+}
+
+function stringToUTF16BEString(str) {
+  const buf = ["\xFE\xFF"];
+
+  for (let i = 0, ii = str.length; i < ii; i++) {
+    const char = str.charCodeAt(i);
+    buf.push(String.fromCharCode(char >> 8 & 0xff));
+    buf.push(String.fromCharCode(char & 0xff));
+  }
+
+  return buf.join("");
 }
 
 function stringToUTF8String(str) {
@@ -788,8 +852,8 @@ function isArrayEqual(arr1, arr2) {
   });
 }
 
-function getModificationDate(date = new Date(Date.now())) {
-  const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), (date.getUTCDate() + 1).toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")];
+function getModificationDate(date = new Date()) {
+  const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")];
   return buffer.join("");
 }
 
@@ -843,4 +907,55 @@ const createObjectURL = function createObjectURLClosure() {
   };
 }();
 
-exports.createObjectURL = createObjectURL;
+exports.createObjectURL = createObjectURL;
+const XMLEntities = {
+  0x3c: "&lt;",
+  0x3e: "&gt;",
+  0x26: "&amp;",
+  0x22: "&quot;",
+  0x27: "&apos;"
+};
+
+function encodeToXmlString(str) {
+  const buffer = [];
+  let start = 0;
+
+  for (let i = 0, ii = str.length; i < ii; i++) {
+    const char = str.codePointAt(i);
+
+    if (0x20 <= char && char <= 0x7e) {
+      const entity = XMLEntities[char];
+
+      if (entity) {
+        if (start < i) {
+          buffer.push(str.substring(start, i));
+        }
+
+        buffer.push(entity);
+        start = i + 1;
+      }
+    } else {
+      if (start < i) {
+        buffer.push(str.substring(start, i));
+      }
+
+      buffer.push(`&#x${char.toString(16).toUpperCase()};`);
+
+      if (char > 0xd7ff && (char < 0xe000 || char > 0xfffd)) {
+        i++;
+      }
+
+      start = i + 1;
+    }
+  }
+
+  if (buffer.length === 0) {
+    return str;
+  }
+
+  if (start < str.length) {
+    buffer.push(str.substring(start, str.length));
+  }
+
+  return buffer.join("");
+}

+ 112 - 4
lib/display/xml_parser.js → lib/shared/xml_parser.js

@@ -24,7 +24,10 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.SimpleXMLParser = void 0;
+exports.SimpleXMLParser = exports.SimpleDOMNode = void 0;
+
+var _util = require("./util.js");
+
 const XMLParserErrorCode = {
   NoError: 0,
   EndOfDocument: -1,
@@ -58,9 +61,9 @@ class XMLParserBase {
   _resolveEntities(s) {
     return s.replace(/&([^;]+);/g, (all, entity) => {
       if (entity.substring(0, 2) === "#x") {
-        return String.fromCharCode(parseInt(entity.substring(2), 16));
+        return String.fromCodePoint(parseInt(entity.substring(2), 16));
       } else if (entity.substring(0, 1) === "#") {
-        return String.fromCharCode(parseInt(entity.substring(1), 10));
+        return String.fromCodePoint(parseInt(entity.substring(1), 10));
       }
 
       switch (entity) {
@@ -365,14 +368,111 @@ class SimpleDOMNode {
     return this.childNodes && this.childNodes.length > 0;
   }
 
+  searchNode(paths, pos) {
+    if (pos >= paths.length) {
+      return this;
+    }
+
+    const component = paths[pos];
+    const stack = [];
+    let node = this;
+
+    while (true) {
+      if (component.name === node.nodeName) {
+        if (component.pos === 0) {
+          const res = node.searchNode(paths, pos + 1);
+
+          if (res !== null) {
+            return res;
+          }
+        } else if (stack.length === 0) {
+          return null;
+        } else {
+          const [parent] = stack.pop();
+          let siblingPos = 0;
+
+          for (const child of parent.childNodes) {
+            if (component.name === child.nodeName) {
+              if (siblingPos === component.pos) {
+                return child.searchNode(paths, pos + 1);
+              }
+
+              siblingPos++;
+            }
+          }
+
+          return node.searchNode(paths, pos + 1);
+        }
+      }
+
+      if (node.childNodes && node.childNodes.length !== 0) {
+        stack.push([node, 0]);
+        node = node.childNodes[0];
+      } else if (stack.length === 0) {
+        return null;
+      } else {
+        while (stack.length !== 0) {
+          const [parent, currentPos] = stack.pop();
+          const newPos = currentPos + 1;
+
+          if (newPos < parent.childNodes.length) {
+            stack.push([parent, newPos]);
+            node = parent.childNodes[newPos];
+            break;
+          }
+        }
+
+        if (stack.length === 0) {
+          return null;
+        }
+      }
+    }
+  }
+
+  dump(buffer) {
+    if (this.nodeName === "#text") {
+      buffer.push((0, _util.encodeToXmlString)(this.nodeValue));
+      return;
+    }
+
+    buffer.push(`<${this.nodeName}`);
+
+    if (this.attributes) {
+      for (const attribute of this.attributes) {
+        buffer.push(` ${attribute.name}="${(0, _util.encodeToXmlString)(attribute.value)}"`);
+      }
+    }
+
+    if (this.hasChildNodes()) {
+      buffer.push(">");
+
+      for (const child of this.childNodes) {
+        child.dump(buffer);
+      }
+
+      buffer.push(`</${this.nodeName}>`);
+    } else if (this.nodeValue) {
+      buffer.push(`>${(0, _util.encodeToXmlString)(this.nodeValue)}</${this.nodeName}>`);
+    } else {
+      buffer.push("/>");
+    }
+  }
+
 }
 
+exports.SimpleDOMNode = SimpleDOMNode;
+
 class SimpleXMLParser extends XMLParserBase {
-  constructor() {
+  constructor({
+    hasAttributes = false,
+    lowerCaseName = false
+  }) {
     super();
     this._currentFragment = null;
     this._stack = null;
     this._errorCode = XMLParserErrorCode.NoError;
+    this._hasAttributes = hasAttributes;
+    this._lowerCaseName = lowerCaseName;
   }
 
   parseFromString(data) {
@@ -422,9 +522,17 @@ class SimpleXMLParser extends XMLParserBase {
   }
 
   onBeginElement(name, attributes, isEmpty) {
+    if (this._lowerCaseName) {
+      name = name.toLowerCase();
+    }
+
     const node = new SimpleDOMNode(name);
     node.childNodes = [];
 
+    if (this._hasAttributes) {
+      node.attributes = attributes;
+    }
+
     this._currentFragment.push(node);
 
     if (isEmpty) {

File diff suppressed because it is too large
+ 604 - 61
lib/test/unit/annotation_spec.js


+ 28 - 10
lib/test/unit/annotation_storage_spec.js

@@ -27,9 +27,13 @@ describe("AnnotationStorage", function () {
   describe("GetOrCreateValue", function () {
     it("should get and set a new value in the annotation storage", function (done) {
       const annotationStorage = new _annotation_storage.AnnotationStorage();
-      let value = annotationStorage.getOrCreateValue("123A", "hello world");
+      let value = annotationStorage.getOrCreateValue("123A", {
+        value: "hello world"
+      }).value;
       expect(value).toEqual("hello world");
-      value = annotationStorage.getOrCreateValue("123A", "an other string");
+      value = annotationStorage.getOrCreateValue("123A", {
+        value: "an other string"
+      }).value;
       expect(value).toEqual("hello world");
       done();
     });
@@ -37,8 +41,10 @@ describe("AnnotationStorage", function () {
   describe("SetValue", function () {
     it("should set a new value in the annotation storage", function (done) {
       const annotationStorage = new _annotation_storage.AnnotationStorage();
-      annotationStorage.setValue("123A", "an other string");
-      const value = annotationStorage.getAll()["123A"];
+      annotationStorage.setValue("123A", {
+        value: "an other string"
+      });
+      const value = annotationStorage.getAll()["123A"].value;
       expect(value).toEqual("an other string");
       done();
     });
@@ -51,11 +57,17 @@ describe("AnnotationStorage", function () {
       };
 
       annotationStorage.onSetModified = callback;
-      annotationStorage.getOrCreateValue("asdf", "original");
+      annotationStorage.getOrCreateValue("asdf", {
+        value: "original"
+      });
       expect(called).toBe(false);
-      annotationStorage.setValue("asdf", "original");
+      annotationStorage.setValue("asdf", {
+        value: "original"
+      });
       expect(called).toBe(false);
-      annotationStorage.setValue("asdf", "modified");
+      annotationStorage.setValue("asdf", {
+        value: "modified"
+      });
       expect(called).toBe(true);
       done();
     });
@@ -70,11 +82,17 @@ describe("AnnotationStorage", function () {
       };
 
       annotationStorage.onResetModified = callback;
-      annotationStorage.getOrCreateValue("asdf", "original");
-      annotationStorage.setValue("asdf", "original");
+      annotationStorage.getOrCreateValue("asdf", {
+        value: "original"
+      });
+      annotationStorage.setValue("asdf", {
+        value: "original"
+      });
       annotationStorage.resetModified();
       expect(called).toBe(false);
-      annotationStorage.setValue("asdf", "modified");
+      annotationStorage.setValue("asdf", {
+        value: "modified"
+      });
       annotationStorage.resetModified();
       expect(called).toBe(true);
       done();

+ 190 - 160
lib/test/unit/api_spec.js

@@ -25,10 +25,10 @@ var _test_utils = require("./test_utils.js");
 
 var _util = require("../../shared/util.js");
 
-var _display_utils = require("../../display/display_utils.js");
-
 var _api = require("../../display/api.js");
 
+var _display_utils = require("../../display/display_utils.js");
+
 var _ui_utils = require("../../web/ui_utils.js");
 
 var _image_utils = require("../../core/image_utils.js");
@@ -39,20 +39,13 @@ var _is_node = require("../../shared/is_node.js");
 
 var _metadata = require("../../display/metadata.js");
 
-var _node_utils = require("../../display/node_utils.js");
-
 describe("api", function () {
   const basicApiFileName = "basicapi.pdf";
   const basicApiFileLength = 105779;
   const basicApiGetDocumentParams = (0, _test_utils.buildGetDocumentParams)(basicApiFileName);
   let CanvasFactory;
   beforeAll(function (done) {
-    if (_is_node.isNodeJS) {
-      CanvasFactory = new _node_utils.NodeCanvasFactory();
-    } else {
-      CanvasFactory = new _display_utils.DOMCanvasFactory();
-    }
-
+    CanvasFactory = new _api.DefaultCanvasFactory();
     done();
   });
   afterAll(function (done) {
@@ -61,7 +54,7 @@ describe("api", function () {
   });
 
   function waitSome(callback) {
-    var WAIT_TIMEOUT = 10;
+    const WAIT_TIMEOUT = 10;
     setTimeout(function () {
       callback();
     }, WAIT_TIMEOUT);
@@ -69,8 +62,8 @@ describe("api", function () {
 
   describe("getDocument", function () {
     it("creates pdf doc from URL", function (done) {
-      var loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
-      var progressReportedCapability = (0, _util.createPromiseCapability)();
+      const loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
+      const progressReportedCapability = (0, _util.createPromiseCapability)();
 
       loadingTask.onProgress = function (progressData) {
         if (!progressReportedCapability.settled) {
@@ -78,7 +71,7 @@ describe("api", function () {
         }
       };
 
-      var promises = [progressReportedCapability.promise, loadingTask.promise];
+      const promises = [progressReportedCapability.promise, loadingTask.promise];
       Promise.all(promises).then(function (data) {
         expect(data[0].loaded / data[0].total >= 0).toEqual(true);
         expect(data[1] instanceof _api.PDFDocumentProxy).toEqual(true);
@@ -87,7 +80,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("creates pdf doc from URL and aborts before worker initialized", function (done) {
-      var loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
+      const loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
       const destroyed = loadingTask.destroy();
       loadingTask.promise.then(function (reason) {
         done.fail("shall fail loading");
@@ -97,9 +90,9 @@ describe("api", function () {
       });
     });
     it("creates pdf doc from URL and aborts loading after worker initialized", function (done) {
-      var loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
+      const loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
 
-      var destroyed = loadingTask._worker.promise.then(function () {
+      const destroyed = loadingTask._worker.promise.then(function () {
         return loadingTask.destroy();
       });
 
@@ -109,17 +102,9 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("creates pdf doc from typed array", function (done) {
-      let typedArrayPdfPromise;
-
-      if (_is_node.isNodeJS) {
-        typedArrayPdfPromise = _test_utils.NodeFileReaderFactory.fetch({
-          path: _test_utils.TEST_PDFS_PATH.node + basicApiFileName
-        });
-      } else {
-        typedArrayPdfPromise = _test_utils.DOMFileReaderFactory.fetch({
-          path: _test_utils.TEST_PDFS_PATH.dom + basicApiFileName
-        });
-      }
+      const typedArrayPdfPromise = _test_utils.DefaultFileReaderFactory.fetch({
+        path: _test_utils.TEST_PDFS_PATH + basicApiFileName
+      });
 
       typedArrayPdfPromise.then(typedArrayPdf => {
         expect(typedArrayPdf.length).toEqual(basicApiFileLength);
@@ -138,7 +123,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("creates pdf doc from invalid PDF file", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug1020226.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug1020226.pdf"));
       loadingTask.promise.then(function () {
         done.fail("shall fail loading");
       }).catch(function (reason) {
@@ -148,7 +133,7 @@ describe("api", function () {
       });
     });
     it("creates pdf doc from non-existent URL", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("non-existent.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("non-existent.pdf"));
       loadingTask.promise.then(function (error) {
         done.fail("shall fail loading");
       }).catch(function (error) {
@@ -157,9 +142,9 @@ describe("api", function () {
       });
     });
     it("creates pdf doc from PDF file protected with user and owner password", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("pr6531_1.pdf"));
-      var passwordNeededCapability = (0, _util.createPromiseCapability)();
-      var passwordIncorrectCapability = (0, _util.createPromiseCapability)();
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("pr6531_1.pdf"));
+      const passwordNeededCapability = (0, _util.createPromiseCapability)();
+      const passwordIncorrectCapability = (0, _util.createPromiseCapability)();
 
       loadingTask.onPassword = function (updatePassword, reason) {
         if (reason === _util.PasswordResponses.NEED_PASSWORD && !passwordNeededCapability.settled) {
@@ -177,18 +162,18 @@ describe("api", function () {
         expect(false).toEqual(true);
       };
 
-      var promises = [passwordNeededCapability.promise, passwordIncorrectCapability.promise, loadingTask.promise];
+      const promises = [passwordNeededCapability.promise, passwordIncorrectCapability.promise, loadingTask.promise];
       Promise.all(promises).then(function (data) {
         expect(data[2] instanceof _api.PDFDocumentProxy).toEqual(true);
         loadingTask.destroy().then(done);
       }).catch(done.fail);
     });
     it("creates pdf doc from PDF file protected with only a user password", function (done) {
-      var filename = "pr6531_2.pdf";
-      var passwordNeededLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const filename = "pr6531_2.pdf";
+      const passwordNeededLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         password: ""
       }));
-      var result1 = passwordNeededLoadingTask.promise.then(function () {
+      const result1 = passwordNeededLoadingTask.promise.then(function () {
         done.fail("shall fail with no password");
         return Promise.reject(new Error("loadingTask should be rejected"));
       }, function (data) {
@@ -196,10 +181,10 @@ describe("api", function () {
         expect(data.code).toEqual(_util.PasswordResponses.NEED_PASSWORD);
         return passwordNeededLoadingTask.destroy();
       });
-      var passwordIncorrectLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const passwordIncorrectLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         password: "qwerty"
       }));
-      var result2 = passwordIncorrectLoadingTask.promise.then(function () {
+      const result2 = passwordIncorrectLoadingTask.promise.then(function () {
         done.fail("shall fail with wrong password");
         return Promise.reject(new Error("loadingTask should be rejected"));
       }, function (data) {
@@ -207,10 +192,10 @@ describe("api", function () {
         expect(data.code).toEqual(_util.PasswordResponses.INCORRECT_PASSWORD);
         return passwordIncorrectLoadingTask.destroy();
       });
-      var passwordAcceptedLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const passwordAcceptedLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         password: "asdfasdf"
       }));
-      var result3 = passwordAcceptedLoadingTask.promise.then(function (data) {
+      const result3 = passwordAcceptedLoadingTask.promise.then(function (data) {
         expect(data instanceof _api.PDFDocumentProxy).toEqual(true);
         return passwordAcceptedLoadingTask.destroy();
       });
@@ -219,9 +204,9 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("creates pdf doc from password protected PDF file and aborts/throws " + "in the onPassword callback (issue 7806)", function (done) {
-      var filename = "issue3371.pdf";
-      var passwordNeededLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename));
-      var passwordIncorrectLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const filename = "issue3371.pdf";
+      const passwordNeededLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename));
+      const passwordIncorrectLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         password: "qwerty"
       }));
       let passwordNeededDestroyed;
@@ -235,7 +220,7 @@ describe("api", function () {
         expect(false).toEqual(true);
       };
 
-      var result1 = passwordNeededLoadingTask.promise.then(function () {
+      const result1 = passwordNeededLoadingTask.promise.then(function () {
         done.fail("shall fail since the loadingTask should be destroyed");
         return Promise.reject(new Error("loadingTask should be rejected"));
       }, function (reason) {
@@ -252,7 +237,7 @@ describe("api", function () {
         expect(false).toEqual(true);
       };
 
-      var result2 = passwordIncorrectLoadingTask.promise.then(function () {
+      const result2 = passwordIncorrectLoadingTask.promise.then(function () {
         done.fail("shall fail since the onPassword callback should throw");
         return Promise.reject(new Error("loadingTask should be rejected"));
       }, function (reason) {
@@ -281,7 +266,7 @@ describe("api", function () {
         pending("Worker is not supported in Node.js.");
       }
 
-      var worker = new _api.PDFWorker({
+      const worker = new _api.PDFWorker({
         name: "test1"
       });
       worker.promise.then(function () {
@@ -301,17 +286,17 @@ describe("api", function () {
         pending("Worker is not supported in Node.js.");
       }
 
-      var loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
-      var worker;
+      const loadingTask = (0, _api.getDocument)(basicApiGetDocumentParams);
+      let worker;
       loadingTask.promise.then(function () {
         worker = loadingTask._worker;
         expect(!!worker).toEqual(true);
       });
-      var destroyPromise = loadingTask.promise.then(function () {
+      const destroyPromise = loadingTask.promise.then(function () {
         return loadingTask.destroy();
       });
       destroyPromise.then(function () {
-        var destroyedWorker = loadingTask._worker;
+        const destroyedWorker = loadingTask._worker;
         expect(!!destroyedWorker).toEqual(false);
         expect(worker.destroyed).toEqual(true);
         done();
@@ -322,19 +307,19 @@ describe("api", function () {
         pending("Worker is not supported in Node.js.");
       }
 
-      var worker = new _api.PDFWorker({
+      const worker = new _api.PDFWorker({
         name: "test1"
       });
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(basicApiFileName, {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(basicApiFileName, {
         worker
       }));
       loadingTask.promise.then(function () {
-        var docWorker = loadingTask._worker;
+        const docWorker = loadingTask._worker;
         expect(!!docWorker).toEqual(false);
-        var messageHandlerPort = loadingTask._transport.messageHandler.comObj;
+        const messageHandlerPort = loadingTask._transport.messageHandler.comObj;
         expect(messageHandlerPort === worker.port).toEqual(true);
       });
-      var destroyPromise = loadingTask.promise.then(function () {
+      const destroyPromise = loadingTask.promise.then(function () {
         return loadingTask.destroy();
       });
       destroyPromise.then(function () {
@@ -348,16 +333,16 @@ describe("api", function () {
         pending("Worker is not supported in Node.js.");
       }
 
-      var worker1 = new _api.PDFWorker({
+      const worker1 = new _api.PDFWorker({
         name: "test1"
       });
-      var worker2 = new _api.PDFWorker({
+      const worker2 = new _api.PDFWorker({
         name: "test2"
       });
-      var worker3 = new _api.PDFWorker({
+      const worker3 = new _api.PDFWorker({
         name: "test3"
       });
-      var ready = Promise.all([worker1.promise, worker2.promise, worker3.promise]);
+      const ready = Promise.all([worker1.promise, worker2.promise, worker3.promise]);
       ready.then(function () {
         expect(worker1.port !== worker2.port && worker1.port !== worker3.port && worker2.port !== worker3.port).toEqual(true);
         worker1.destroy();
@@ -396,7 +381,7 @@ describe("api", function () {
       expect(pdfDocument.fingerprint).toEqual("ea8b35919d6279a369e835bde778611b");
     });
     it("gets page", function (done) {
-      var promise = pdfDocument.getPage(1);
+      const promise = pdfDocument.getPage(1);
       promise.then(function (data) {
         expect(data instanceof _api.PDFPageProxy).toEqual(true);
         expect(data.pageNumber).toEqual(1);
@@ -404,9 +389,9 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets non-existent page", function (done) {
-      var outOfRangePromise = pdfDocument.getPage(100);
-      var nonIntegerPromise = pdfDocument.getPage(2.5);
-      var nonNumberPromise = pdfDocument.getPage("1");
+      let outOfRangePromise = pdfDocument.getPage(100);
+      let nonIntegerPromise = pdfDocument.getPage(2.5);
+      let nonNumberPromise = pdfDocument.getPage("1");
       outOfRangePromise = outOfRangePromise.then(function () {
         throw new Error("shall fail for out-of-range pageNumber parameter");
       }, function (reason) {
@@ -452,22 +437,22 @@ describe("api", function () {
       }, done.fail);
     });
     it("gets page index", function (done) {
-      var ref = {
+      const ref = {
         num: 17,
         gen: 0
       };
-      var promise = pdfDocument.getPageIndex(ref);
+      const promise = pdfDocument.getPageIndex(ref);
       promise.then(function (pageIndex) {
         expect(pageIndex).toEqual(1);
         done();
       }).catch(done.fail);
     });
     it("gets invalid page index", function (done) {
-      var ref = {
+      const ref = {
         num: 3,
         gen: 0
       };
-      var promise = pdfDocument.getPageIndex(ref);
+      const promise = pdfDocument.getPageIndex(ref);
       promise.then(function () {
         done.fail("shall fail for invalid page reference.");
       }).catch(function (reason) {
@@ -476,7 +461,7 @@ describe("api", function () {
       });
     });
     it("gets destinations, from /Dests dictionary", function (done) {
-      var promise = pdfDocument.getDestinations();
+      const promise = pdfDocument.getDestinations();
       promise.then(function (data) {
         expect(data).toEqual({
           chapter1: [{
@@ -490,7 +475,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets a destination, from /Dests dictionary", function (done) {
-      var promise = pdfDocument.getDestination("chapter1");
+      const promise = pdfDocument.getDestination("chapter1");
       promise.then(function (data) {
         expect(data).toEqual([{
           gen: 0,
@@ -502,15 +487,15 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets a non-existent destination, from /Dests dictionary", function (done) {
-      var promise = pdfDocument.getDestination("non-existent-named-destination");
+      const promise = pdfDocument.getDestination("non-existent-named-destination");
       promise.then(function (data) {
         expect(data).toEqual(null);
         done();
       }).catch(done.fail);
     });
     it("gets destinations, from /Names (NameTree) dictionary", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getDestinations();
       });
       promise.then(function (destinations) {
@@ -532,8 +517,8 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets a destination, from /Names (NameTree) dictionary", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getDestination("Page.1");
       });
       promise.then(function (destination) {
@@ -547,8 +532,8 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets a non-existent destination, from /Names (NameTree) dictionary", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6204.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getDestination("non-existent-named-destination");
       });
       promise.then(function (destination) {
@@ -583,27 +568,27 @@ describe("api", function () {
       Promise.all([numberPromise, booleanPromise, arrayPromise]).then(done, done.fail);
     });
     it("gets non-existent page labels", function (done) {
-      var promise = pdfDocument.getPageLabels();
+      const promise = pdfDocument.getPageLabels();
       promise.then(function (data) {
         expect(data).toEqual(null);
         done();
       }).catch(done.fail);
     });
     it("gets page labels", function (done) {
-      var loadingTask0 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug793632.pdf"));
-      var promise0 = loadingTask0.promise.then(function (pdfDoc) {
+      const loadingTask0 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug793632.pdf"));
+      const promise0 = loadingTask0.promise.then(function (pdfDoc) {
         return pdfDoc.getPageLabels();
       });
-      var loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue1453.pdf"));
-      var promise1 = loadingTask1.promise.then(function (pdfDoc) {
+      const loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue1453.pdf"));
+      const promise1 = loadingTask1.promise.then(function (pdfDoc) {
         return pdfDoc.getPageLabels();
       });
-      var loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("rotation.pdf"));
-      var promise2 = loadingTask2.promise.then(function (pdfDoc) {
+      const loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("rotation.pdf"));
+      const promise2 = loadingTask2.promise.then(function (pdfDoc) {
         return pdfDoc.getPageLabels();
       });
-      var loadingTask3 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bad-PageLabels.pdf"));
-      var promise3 = loadingTask3.promise.then(function (pdfDoc) {
+      const loadingTask3 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bad-PageLabels.pdf"));
+      const promise3 = loadingTask3.promise.then(function (pdfDoc) {
         return pdfDoc.getPageLabels();
       });
       Promise.all([promise0, promise1, promise2, promise3]).then(function (pageLabels) {
@@ -615,7 +600,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets default page layout", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getPageLayout();
       }).then(function (mode) {
@@ -630,7 +615,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets default page mode", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getPageMode();
       }).then(function (mode) {
@@ -645,7 +630,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets default viewer preferences", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getViewerPreferences();
       }).then(function (prefs) {
@@ -662,7 +647,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets default open action", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getOpenAction();
       }).then(function (openAction) {
@@ -702,34 +687,34 @@ describe("api", function () {
       Promise.all([promise1, promise2]).then(done, done.fail);
     });
     it("gets non-existent attachments", function (done) {
-      var promise = pdfDocument.getAttachments();
+      const promise = pdfDocument.getAttachments();
       promise.then(function (data) {
         expect(data).toEqual(null);
         done();
       }).catch(done.fail);
     });
     it("gets attachments", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("attachment.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("attachment.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getAttachments();
       });
       promise.then(function (data) {
-        var attachment = data["foo.txt"];
+        const attachment = data["foo.txt"];
         expect(attachment.filename).toEqual("foo.txt");
         expect(attachment.content).toEqual(new Uint8Array([98, 97, 114, 32, 98, 97, 122, 32, 10]));
         loadingTask.destroy().then(done);
       }).catch(done.fail);
     });
     it("gets javascript", function (done) {
-      var promise = pdfDocument.getJavaScript();
+      const promise = pdfDocument.getJavaScript();
       promise.then(function (data) {
         expect(data).toEqual(null);
         done();
       }).catch(done.fail);
     });
     it("gets javascript with printing instructions (JS action)", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6106.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue6106.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getJavaScript();
       });
       promise.then(function (data) {
@@ -738,9 +723,45 @@ describe("api", function () {
         loadingTask.destroy().then(done);
       }).catch(done.fail);
     });
+    it("gets JSActions (none)", function (done) {
+      const promise = pdfDocument.getJSActions();
+      promise.then(function (data) {
+        expect(data).toEqual(null);
+        done();
+      }).catch(done.fail);
+    });
+    it("gets JSActions", function (done) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("doc_actions.pdf"));
+      const promise = loadingTask.promise.then(async pdfDoc => {
+        const docActions = await pdfDoc.getJSActions();
+        const page1 = await pdfDoc.getPage(1);
+        const page3 = await pdfDoc.getPage(3);
+        const page1Actions = await page1.getJSActions();
+        const page3Actions = await page3.getJSActions();
+        return [docActions, page1Actions, page3Actions];
+      });
+      promise.then(async ([docActions, page1Actions, page3Actions]) => {
+        expect(docActions).toEqual({
+          DidPrint: [`this.getField("Text2").value = "DidPrint";`],
+          DidSave: [`this.getField("Text2").value = "DidSave";`],
+          WillClose: [`this.getField("Text1").value = "WillClose";`],
+          WillPrint: [`this.getField("Text1").value = "WillPrint";`],
+          WillSave: [`this.getField("Text1").value = "WillSave";`]
+        });
+        expect(page1Actions).toEqual({
+          PageOpen: [`this.getField("Text1").value = "PageOpen 1";`],
+          PageClose: [`this.getField("Text2").value = "PageClose 1";`]
+        });
+        expect(page3Actions).toEqual({
+          PageOpen: [`this.getField("Text5").value = "PageOpen 3";`],
+          PageClose: [`this.getField("Text6").value = "PageClose 3";`]
+        });
+        loadingTask.destroy().then(done);
+      }).catch(done.fail);
+    });
     it("gets non-existent outline", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
-      var promise = loadingTask.promise.then(function (pdfDoc) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const promise = loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getOutline();
       });
       promise.then(function (outline) {
@@ -749,11 +770,11 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets outline", function (done) {
-      var promise = pdfDocument.getOutline();
+      const promise = pdfDocument.getOutline();
       promise.then(function (outline) {
         expect(Array.isArray(outline)).toEqual(true);
         expect(outline.length).toEqual(2);
-        var outlineItem = outline[1];
+        const outlineItem = outline[1];
         expect(outlineItem.title).toEqual("Chapter 1");
         expect(Array.isArray(outlineItem.dest)).toEqual(true);
         expect(outlineItem.url).toEqual(null);
@@ -768,18 +789,18 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets outline containing a url", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue3214.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue3214.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         pdfDoc.getOutline().then(function (outline) {
           expect(Array.isArray(outline)).toEqual(true);
           expect(outline.length).toEqual(5);
-          var outlineItemTwo = outline[2];
+          const outlineItemTwo = outline[2];
           expect(typeof outlineItemTwo.title).toEqual("string");
           expect(outlineItemTwo.dest).toEqual(null);
           expect(outlineItemTwo.url).toEqual("http://google.com/");
           expect(outlineItemTwo.unsafeUrl).toEqual("http://google.com");
           expect(outlineItemTwo.newWindow).toBeUndefined();
-          var outlineItemOne = outline[1];
+          const outlineItemOne = outline[1];
           expect(outlineItemOne.bold).toEqual(false);
           expect(outlineItemOne.italic).toEqual(true);
           expect(outlineItemOne.color).toEqual(new Uint8ClampedArray([0, 0, 0]));
@@ -819,11 +840,12 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets metadata", function (done) {
-      var promise = pdfDocument.getMetadata();
+      const promise = pdfDocument.getMetadata();
       promise.then(function ({
         info,
         metadata,
-        contentDispositionFilename
+        contentDispositionFilename,
+        contentLength
       }) {
         expect(info.Title).toEqual("Basic API Test");
         expect(info.Custom).toEqual(undefined);
@@ -835,17 +857,19 @@ describe("api", function () {
         expect(metadata instanceof _metadata.Metadata).toEqual(true);
         expect(metadata.get("dc:title")).toEqual("Basic API Test");
         expect(contentDispositionFilename).toEqual(null);
+        expect(contentLength).toEqual(basicApiFileLength);
         done();
       }).catch(done.fail);
     });
     it("gets metadata, with custom info dict entries", function (done) {
-      var loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
       loadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getMetadata();
       }).then(function ({
         info,
         metadata,
-        contentDispositionFilename
+        contentDispositionFilename,
+        contentLength
       }) {
         expect(info.Creator).toEqual("TeX");
         expect(info.Producer).toEqual("pdfeTeX-1.21a");
@@ -860,6 +884,7 @@ describe("api", function () {
         expect(info.IsCollectionPresent).toEqual(false);
         expect(metadata).toEqual(null);
         expect(contentDispositionFilename).toEqual(null);
+        expect(contentLength).toEqual(1016315);
         loadingTask.destroy().then(done);
       }).catch(done.fail);
     });
@@ -870,7 +895,8 @@ describe("api", function () {
       }).then(function ({
         info,
         metadata,
-        contentDispositionFilename
+        contentDispositionFilename,
+        contentLength
       }) {
         expect(info.PDFFormatVersion).toEqual(null);
         expect(info.IsLinearized).toEqual(false);
@@ -879,11 +905,23 @@ describe("api", function () {
         expect(info.IsCollectionPresent).toEqual(false);
         expect(metadata).toEqual(null);
         expect(contentDispositionFilename).toEqual(null);
+        expect(contentLength).toEqual(624);
         loadingTask.destroy().then(done);
       }).catch(done.fail);
     });
+    it("gets markInfo", function (done) {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("annotation-line.pdf"));
+      loadingTask.promise.then(function (pdfDoc) {
+        return pdfDoc.getMarkInfo();
+      }).then(function (info) {
+        expect(info.Marked).toEqual(true);
+        expect(info.UserProperties).toEqual(false);
+        expect(info.Suspects).toEqual(false);
+        done();
+      }).catch(done.fail);
+    });
     it("gets data", function (done) {
-      var promise = pdfDocument.getData();
+      const promise = pdfDocument.getData();
       promise.then(function (data) {
         expect(data instanceof Uint8Array).toEqual(true);
         expect(data.length).toEqual(basicApiFileLength);
@@ -891,7 +929,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets download info", function (done) {
-      var promise = pdfDocument.getDownloadInfo();
+      const promise = pdfDocument.getDownloadInfo();
       promise.then(function (data) {
         expect(data).toEqual({
           length: basicApiFileLength
@@ -900,7 +938,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets document stats", function (done) {
-      var promise = pdfDocument.getStats();
+      const promise = pdfDocument.getStats();
       promise.then(function (stats) {
         expect(stats).toEqual({
           streamTypes: {},
@@ -929,15 +967,15 @@ describe("api", function () {
       }).catch(done.fail);
     });
     describe("Cross-origin", function () {
-      var loadingTask;
+      let loadingTask;
 
       function _checkCanLoad(expectSuccess, filename, options) {
         if (_is_node.isNodeJS) {
           pending("Cannot simulate cross-origin requests in Node.js");
         }
 
-        var params = (0, _test_utils.buildGetDocumentParams)(filename, options);
-        var url = new URL(params.url);
+        const params = (0, _test_utils.buildGetDocumentParams)(filename, options);
+        const url = new URL(params.url);
 
         if (url.hostname === "localhost") {
           url.hostname = "127.0.0.1";
@@ -1060,7 +1098,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets viewport", function () {
-      var viewport = page.getViewport({
+      const viewport = page.getViewport({
         scale: 1.5,
         rotation: 90
       });
@@ -1108,15 +1146,15 @@ describe("api", function () {
       }).toThrow(new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees."));
     });
     it("gets annotations", function (done) {
-      var defaultPromise = page.getAnnotations().then(function (data) {
+      const defaultPromise = page.getAnnotations().then(function (data) {
         expect(data.length).toEqual(4);
       });
-      var displayPromise = page.getAnnotations({
+      const displayPromise = page.getAnnotations({
         intent: "display"
       }).then(function (data) {
         expect(data.length).toEqual(4);
       });
-      var printPromise = page.getAnnotations({
+      const printPromise = page.getAnnotations({
         intent: "print"
       }).then(function (data) {
         expect(data.length).toEqual(4);
@@ -1126,33 +1164,33 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets annotations containing relative URLs (bug 766086)", function (done) {
-      var filename = "bug766086.pdf";
-      var defaultLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename));
-      var defaultPromise = defaultLoadingTask.promise.then(function (pdfDoc) {
+      const filename = "bug766086.pdf";
+      const defaultLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename));
+      const defaultPromise = defaultLoadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getPage(1).then(function (pdfPage) {
           return pdfPage.getAnnotations();
         });
       });
-      var docBaseUrlLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const docBaseUrlLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         docBaseUrl: "http://www.example.com/test/pdfs/qwerty.pdf"
       }));
-      var docBaseUrlPromise = docBaseUrlLoadingTask.promise.then(function (pdfDoc) {
+      const docBaseUrlPromise = docBaseUrlLoadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getPage(1).then(function (pdfPage) {
           return pdfPage.getAnnotations();
         });
       });
-      var invalidDocBaseUrlLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
+      const invalidDocBaseUrlLoadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename, {
         docBaseUrl: "qwerty.pdf"
       }));
-      var invalidDocBaseUrlPromise = invalidDocBaseUrlLoadingTask.promise.then(function (pdfDoc) {
+      const invalidDocBaseUrlPromise = invalidDocBaseUrlLoadingTask.promise.then(function (pdfDoc) {
         return pdfDoc.getPage(1).then(function (pdfPage) {
           return pdfPage.getAnnotations();
         });
       });
       Promise.all([defaultPromise, docBaseUrlPromise, invalidDocBaseUrlPromise]).then(function (data) {
-        var defaultAnnotations = data[0];
-        var docBaseUrlAnnotations = data[1];
-        var invalidDocBaseUrlAnnotations = data[2];
+        const defaultAnnotations = data[0];
+        const docBaseUrlAnnotations = data[1];
+        const invalidDocBaseUrlAnnotations = data[2];
         expect(defaultAnnotations[0].url).toBeUndefined();
         expect(defaultAnnotations[0].unsafeUrl).toEqual("../../0021/002156/215675E.pdf#15");
         expect(docBaseUrlAnnotations[0].url).toEqual("http://www.example.com/0021/002156/215675E.pdf#15");
@@ -1163,12 +1201,12 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets text content", function (done) {
-      var defaultPromise = page.getTextContent();
-      var parametersPromise = page.getTextContent({
+      const defaultPromise = page.getTextContent();
+      const parametersPromise = page.getTextContent({
         normalizeWhitespace: true,
         disableCombineTextItems: true
       });
-      var promises = [defaultPromise, parametersPromise];
+      const promises = [defaultPromise, parametersPromise];
       Promise.all(promises).then(function (data) {
         expect(!!data[0].items).toEqual(true);
         expect(data[0].items.length).toEqual(7);
@@ -1207,7 +1245,7 @@ describe("api", function () {
       }).catch(done.fail);
     });
     it("gets operator list", function (done) {
-      var promise = page.getOperatorList();
+      const promise = page.getOperatorList();
       promise.then(function (oplist) {
         expect(!!oplist.fnArray).toEqual(true);
         expect(!!oplist.argsArray).toEqual(true);
@@ -1262,12 +1300,12 @@ describe("api", function () {
       Promise.all([result1, result2]).then(done, done.fail);
     });
     it("gets document stats after parsing page", function (done) {
-      var promise = page.getOperatorList().then(function () {
+      const promise = page.getOperatorList().then(function () {
         return pdfDocument.getStats();
       });
-      var expectedStreamTypes = {};
+      const expectedStreamTypes = {};
       expectedStreamTypes[_util.StreamType.FLATE] = true;
-      var expectedFontTypes = {};
+      const expectedFontTypes = {};
       expectedFontTypes[_util.FontType.TYPE1] = true;
       expectedFontTypes[_util.FontType.CIDFONTTYPE2] = true;
       promise.then(function (stats) {
@@ -1340,11 +1378,11 @@ describe("api", function () {
       }, done.fail);
     });
     it("cancels rendering of page", function (done) {
-      var viewport = page.getViewport({
+      const viewport = page.getViewport({
         scale: 1
       });
-      var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
-      var renderTask = page.render({
+      const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
+      const renderTask = page.render({
         canvasContext: canvasAndCtx.context,
         canvasFactory: CanvasFactory,
         viewport
@@ -1389,17 +1427,17 @@ describe("api", function () {
     });
     it("multiple render() on the same canvas", function (done) {
       const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
-      var viewport = page.getViewport({
+      const viewport = page.getViewport({
         scale: 1
       });
-      var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
-      var renderTask1 = page.render({
+      const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
+      const renderTask1 = page.render({
         canvasContext: canvasAndCtx.context,
         canvasFactory: CanvasFactory,
         viewport,
         optionalContentConfigPromise
       });
-      var renderTask2 = page.render({
+      const renderTask2 = page.render({
         canvasContext: canvasAndCtx.context,
         canvasFactory: CanvasFactory,
         viewport,
@@ -1532,10 +1570,10 @@ describe("api", function () {
     });
   });
   describe("Multiple `getDocument` instances", function () {
-    var pdf1 = (0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf");
-    var pdf2 = (0, _test_utils.buildGetDocumentParams)("TAMReview.pdf");
-    var pdf3 = (0, _test_utils.buildGetDocumentParams)("issue6068.pdf");
-    var loadingTasks = [];
+    const pdf1 = (0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf");
+    const pdf2 = (0, _test_utils.buildGetDocumentParams)("TAMReview.pdf");
+    const pdf3 = (0, _test_utils.buildGetDocumentParams)("issue6068.pdf");
+    const loadingTasks = [];
 
     async function renderPDF(filename) {
       const loadingTask = (0, _api.getDocument)(filename);
@@ -1564,8 +1602,8 @@ describe("api", function () {
       Promise.all(destroyPromises).then(done);
     });
     it("should correctly render PDFs in parallel", function (done) {
-      var baseline1, baseline2, baseline3;
-      var promiseDone = renderPDF(pdf1).then(function (data1) {
+      let baseline1, baseline2, baseline3;
+      const promiseDone = renderPDF(pdf1).then(function (data1) {
         baseline1 = data1;
         return renderPDF(pdf2);
       }).then(function (data2) {
@@ -1589,17 +1627,9 @@ describe("api", function () {
     let dataPromise;
     beforeAll(function (done) {
       const fileName = "tracemonkey.pdf";
-
-      if (_is_node.isNodeJS) {
-        dataPromise = _test_utils.NodeFileReaderFactory.fetch({
-          path: _test_utils.TEST_PDFS_PATH.node + fileName
-        });
-      } else {
-        dataPromise = _test_utils.DOMFileReaderFactory.fetch({
-          path: _test_utils.TEST_PDFS_PATH.dom + fileName
-        });
-      }
-
+      dataPromise = _test_utils.DefaultFileReaderFactory.fetch({
+        path: _test_utils.TEST_PDFS_PATH + fileName
+      });
       done();
     });
     afterAll(function () {

+ 6 - 6
lib/test/unit/bidi_spec.js

@@ -25,16 +25,16 @@ var _bidi = require("../../core/bidi.js");
 
 describe("bidi", function () {
   it("should mark text as RTL if more than 30% of text is RTL", function () {
-    var test = "\u0645\u0635\u0631 Egypt";
-    var result = "Egypt \u0631\u0635\u0645";
-    var bidiText = (0, _bidi.bidi)(test, -1, false);
+    const test = "\u0645\u0635\u0631 Egypt";
+    const result = "Egypt \u0631\u0635\u0645";
+    const bidiText = (0, _bidi.bidi)(test, -1, false);
     expect(bidiText.str).toEqual(result);
     expect(bidiText.dir).toEqual("rtl");
   });
   it("should mark text as LTR if less than 30% of text is RTL", function () {
-    var test = "Egypt is known as \u0645\u0635\u0631 in Arabic.";
-    var result = "Egypt is known as \u0631\u0635\u0645 in Arabic.";
-    var bidiText = (0, _bidi.bidi)(test, -1, false);
+    const test = "Egypt is known as \u0645\u0635\u0631 in Arabic.";
+    const result = "Egypt is known as \u0631\u0635\u0645 in Arabic.";
+    const bidiText = (0, _bidi.bidi)(test, -1, false);
     expect(bidiText.str).toEqual(result);
     expect(bidiText.dir).toEqual("ltr");
   });

+ 73 - 73
lib/test/unit/cff_parser_spec.js

@@ -29,28 +29,28 @@ var _stream = require("../../core/stream.js");
 
 describe("CFFParser", function () {
   function createWithNullProto(obj) {
-    var result = Object.create(null);
+    const result = Object.create(null);
 
-    for (var i in obj) {
+    for (const i in obj) {
       result[i] = obj[i];
     }
 
     return result;
   }
 
-  var privateDictStub = {
+  const privateDictStub = {
     getByName(name) {
       return 0;
     }
 
   };
-  var fontData, parser, cff;
+  let fontData, parser, cff;
   beforeAll(function (done) {
-    var exampleFont = "0100040100010101134142434445462b" + "54696d65732d526f6d616e000101011f" + "f81b00f81c02f81d03f819041c6f000d" + "fb3cfb6efa7cfa1605e911b8f1120003" + "01010813183030312e30303754696d65" + "7320526f6d616e54696d657300000002" + "010102030e0e7d99f92a99fb7695f773" + "8b06f79a93fc7c8c077d99f85695f75e" + "9908fb6e8cf87393f7108b09a70adf0b" + "f78e14";
-    var fontArr = [];
+    const exampleFont = "0100040100010101134142434445462b" + "54696d65732d526f6d616e000101011f" + "f81b00f81c02f81d03f819041c6f000d" + "fb3cfb6efa7cfa1605e911b8f1120003" + "01010813183030312e30303754696d65" + "7320526f6d616e54696d657300000002" + "010102030e0e7d99f92a99fb7695f773" + "8b06f79a93fc7c8c077d99f85695f75e" + "9908fb6e8cf87393f7108b09a70adf0b" + "f78e14";
+    const fontArr = [];
 
-    for (var i = 0, ii = exampleFont.length; i < ii; i += 2) {
-      var hex = exampleFont.substring(i, i + 2);
+    for (let i = 0, ii = exampleFont.length; i < ii; i += 2) {
+      const hex = exampleFont.substring(i, i + 2);
       fontArr.push(parseInt(hex, 16));
     }
 
@@ -70,25 +70,25 @@ describe("CFFParser", function () {
     done();
   });
   it("parses header", function () {
-    var header = cff.header;
+    const header = cff.header;
     expect(header.major).toEqual(1);
     expect(header.minor).toEqual(0);
     expect(header.hdrSize).toEqual(4);
     expect(header.offSize).toEqual(1);
   });
   it("parses name index", function () {
-    var names = cff.names;
+    const names = cff.names;
     expect(names.length).toEqual(1);
     expect(names[0]).toEqual("ABCDEF+Times-Roman");
   });
   it("parses string index", function () {
-    var strings = cff.strings;
+    const strings = cff.strings;
     expect(strings.count).toEqual(3);
     expect(strings.get(0)).toEqual(".notdef");
     expect(strings.get(391)).toEqual("001.007");
   });
   it("parses top dict", function () {
-    var topDict = cff.topDict;
+    const topDict = cff.topDict;
     expect(topDict.getByName("version")).toEqual(391);
     expect(topDict.getByName("FullName")).toEqual(392);
     expect(topDict.getByName("FamilyName")).toEqual(393);
@@ -99,30 +99,30 @@ describe("CFFParser", function () {
     expect(topDict.getByName("Private")).toEqual([45, 102]);
   });
   it("refuses to add topDict key with invalid value (bug 1068432)", function () {
-    var topDict = cff.topDict;
-    var defaultValue = topDict.getByName("UnderlinePosition");
+    const topDict = cff.topDict;
+    const defaultValue = topDict.getByName("UnderlinePosition");
     topDict.setByKey(3075, [NaN]);
     expect(topDict.getByName("UnderlinePosition")).toEqual(defaultValue);
   });
   it("ignores reserved commands in parseDict, and refuses to add privateDict " + "keys with invalid values (bug 1308536)", function () {
-    var bytes = new Uint8Array([64, 39, 31, 30, 252, 114, 137, 115, 79, 30, 197, 119, 2, 99, 127, 6]);
+    const bytes = new Uint8Array([64, 39, 31, 30, 252, 114, 137, 115, 79, 30, 197, 119, 2, 99, 127, 6]);
     parser.bytes = bytes;
-    var topDict = cff.topDict;
+    const topDict = cff.topDict;
     topDict.setByName("Private", [bytes.length, 0]);
 
-    var parsePrivateDict = function () {
+    const parsePrivateDict = function () {
       parser.parsePrivateDict(topDict);
     };
 
     expect(parsePrivateDict).not.toThrow();
-    var privateDict = topDict.privateDict;
+    const privateDict = topDict.privateDict;
     expect(privateDict.getByName("BlueValues")).toBeNull();
   });
   it("parses a CharString having cntrmask", function () {
-    var bytes = new Uint8Array([0, 1, 1, 0, 38, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 1, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 3, 20, 22, 22, 14]);
+    const bytes = new Uint8Array([0, 1, 1, 0, 38, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 1, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, 3, 20, 22, 22, 14]);
     parser.bytes = bytes;
-    var charStringsIndex = parser.parseIndex(0).obj;
-    var charStrings = parser.parseCharStrings({
+    const charStringsIndex = parser.parseIndex(0).obj;
+    const charStrings = parser.parseCharStrings({
       charStrings: charStringsIndex,
       privateDict: privateDictStub
     }).charStrings;
@@ -132,10 +132,10 @@ describe("CFFParser", function () {
   it("parses a CharString endchar with 4 args w/seac enabled", function () {
     const cffParser = new _cff_parser.CFFParser(fontData, {}, true);
     cffParser.parse();
-    var bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
+    const bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
     cffParser.bytes = bytes;
-    var charStringsIndex = cffParser.parseIndex(0).obj;
-    var result = cffParser.parseCharStrings({
+    const charStringsIndex = cffParser.parseIndex(0).obj;
+    const result = cffParser.parseCharStrings({
       charStrings: charStringsIndex,
       privateDict: privateDictStub
     });
@@ -151,10 +151,10 @@ describe("CFFParser", function () {
   it("parses a CharString endchar with 4 args w/seac disabled", function () {
     const cffParser = new _cff_parser.CFFParser(fontData, {}, false);
     cffParser.parse();
-    var bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
+    const bytes = new Uint8Array([0, 1, 1, 0, 237, 247, 22, 247, 72, 204, 247, 86, 14]);
     cffParser.bytes = bytes;
-    var charStringsIndex = cffParser.parseIndex(0).obj;
-    var result = cffParser.parseCharStrings({
+    const charStringsIndex = cffParser.parseIndex(0).obj;
+    const result = cffParser.parseCharStrings({
       charStrings: charStringsIndex,
       privateDict: privateDictStub
     });
@@ -163,10 +163,10 @@ describe("CFFParser", function () {
     expect(result.seacs.length).toEqual(0);
   });
   it("parses a CharString endchar no args", function () {
-    var bytes = new Uint8Array([0, 1, 1, 0, 14]);
+    const bytes = new Uint8Array([0, 1, 1, 0, 14]);
     parser.bytes = bytes;
-    var charStringsIndex = parser.parseIndex(0).obj;
-    var result = parser.parseCharStrings({
+    const charStringsIndex = parser.parseIndex(0).obj;
+    const result = parser.parseCharStrings({
       charStrings: charStringsIndex,
       privateDict: privateDictStub
     });
@@ -175,68 +175,68 @@ describe("CFFParser", function () {
     expect(result.seacs.length).toEqual(0);
   });
   it("parses predefined charsets", function () {
-    var charset = parser.parseCharsets(0, 0, null, true);
+    const charset = parser.parseCharsets(0, 0, null, true);
     expect(charset.predefined).toEqual(true);
   });
   it("parses charset format 0", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
     parser.bytes = bytes;
-    var charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
+    let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
     expect(charset.charset[1]).toEqual("exclam");
     charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
     expect(charset.charset[1]).toEqual(2);
   });
   it("parses charset format 1", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x01]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x01]);
     parser.bytes = bytes;
-    var charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
+    let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
     expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
     charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
     expect(charset.charset).toEqual([0, 8, 9]);
   });
   it("parses charset format 2", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01]);
     parser.bytes = bytes;
-    var charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
+    let charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), false);
     expect(charset.charset).toEqual([".notdef", "quoteright", "parenleft"]);
     charset = parser.parseCharsets(3, 2, new _cff_parser.CFFStrings(), true);
     expect(charset.charset).toEqual([0, 8, 9]);
   });
   it("parses encoding format 0", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x08]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x00, 0x01, 0x08]);
     parser.bytes = bytes;
-    var encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
+    const encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
     expect(encoding.encoding).toEqual(createWithNullProto({
       0x8: 1
     }));
   });
   it("parses encoding format 1", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x01, 0x01, 0x07, 0x01]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x01, 0x01, 0x07, 0x01]);
     parser.bytes = bytes;
-    var encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
+    const encoding = parser.parseEncoding(2, {}, new _cff_parser.CFFStrings(), null);
     expect(encoding.encoding).toEqual(createWithNullProto({
       0x7: 0x01,
       0x08: 0x02
     }));
   });
   it("parses fdselect format 0", function () {
-    var bytes = new Uint8Array([0x00, 0x00, 0x01]);
+    const bytes = new Uint8Array([0x00, 0x00, 0x01]);
     parser.bytes = bytes.slice();
-    var fdSelect = parser.parseFDSelect(0, 2);
+    const fdSelect = parser.parseFDSelect(0, 2);
     expect(fdSelect.fdSelect).toEqual([0, 1]);
     expect(fdSelect.format).toEqual(0);
   });
   it("parses fdselect format 3", function () {
-    var bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
+    const bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
     parser.bytes = bytes.slice();
-    var fdSelect = parser.parseFDSelect(0, 4);
+    const fdSelect = parser.parseFDSelect(0, 4);
     expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
     expect(fdSelect.format).toEqual(3);
   });
   it("parses invalid fdselect format 3 (bug 1146106)", function () {
-    var bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x01, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
+    const bytes = new Uint8Array([0x03, 0x00, 0x02, 0x00, 0x01, 0x09, 0x00, 0x02, 0x0a, 0x00, 0x04]);
     parser.bytes = bytes.slice();
-    var fdSelect = parser.parseFDSelect(0, 4);
+    const fdSelect = parser.parseFDSelect(0, 4);
     expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
     expect(fdSelect.format).toEqual(3);
   });
@@ -252,7 +252,7 @@ describe("CFFCompiler", function () {
   }
 
   it("encodes integers", function () {
-    var c = new _cff_parser.CFFCompiler();
+    const c = new _cff_parser.CFFCompiler();
     expect(c.encodeInteger(0)).toEqual([0x8b]);
     expect(c.encodeInteger(100)).toEqual([0xef]);
     expect(c.encodeInteger(-100)).toEqual([0x27]);
@@ -264,20 +264,20 @@ describe("CFFCompiler", function () {
     expect(c.encodeInteger(-100000)).toEqual([0x1d, 0xff, 0xfe, 0x79, 0x60]);
   });
   it("encodes floats", function () {
-    var c = new _cff_parser.CFFCompiler();
+    const c = new _cff_parser.CFFCompiler();
     expect(c.encodeFloat(-2.25)).toEqual([0x1e, 0xe2, 0xa2, 0x5f]);
     expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]);
   });
   it("sanitizes name index", function () {
-    var c = new _cff_parser.CFFCompiler();
-    var nameIndexCompiled = c.compileNameIndex(["[a"]);
-    var parser = testParser(nameIndexCompiled);
-    var nameIndex = parser.parseIndex(0);
-    var names = parser.parseNameIndex(nameIndex.obj);
+    const c = new _cff_parser.CFFCompiler();
+    let nameIndexCompiled = c.compileNameIndex(["[a"]);
+    let parser = testParser(nameIndexCompiled);
+    let nameIndex = parser.parseIndex(0);
+    let names = parser.parseNameIndex(nameIndex.obj);
     expect(names).toEqual(["_a"]);
-    var longName = "";
+    let longName = "";
 
-    for (var i = 0; i < 129; i++) {
+    for (let i = 0; i < 129; i++) {
       longName += "_";
     }
 
@@ -288,35 +288,35 @@ describe("CFFCompiler", function () {
     expect(names[0].length).toEqual(127);
   });
   it("compiles fdselect format 0", function () {
-    var fdSelect = new _cff_parser.CFFFDSelect(0, [3, 2, 1]);
-    var c = new _cff_parser.CFFCompiler();
-    var out = c.compileFDSelect(fdSelect);
+    const fdSelect = new _cff_parser.CFFFDSelect(0, [3, 2, 1]);
+    const c = new _cff_parser.CFFCompiler();
+    const out = c.compileFDSelect(fdSelect);
     expect(out).toEqual([0, 3, 2, 1]);
   });
   it("compiles fdselect format 3", function () {
-    var fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0, 1, 1]);
-    var c = new _cff_parser.CFFCompiler();
-    var out = c.compileFDSelect(fdSelect);
+    const fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0, 1, 1]);
+    const c = new _cff_parser.CFFCompiler();
+    const out = c.compileFDSelect(fdSelect);
     expect(out).toEqual([3, 0, 2, 0, 0, 0, 0, 2, 1, 0, 4]);
   });
   it("compiles fdselect format 3, single range", function () {
-    var fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0]);
-    var c = new _cff_parser.CFFCompiler();
-    var out = c.compileFDSelect(fdSelect);
+    const fdSelect = new _cff_parser.CFFFDSelect(3, [0, 0]);
+    const c = new _cff_parser.CFFCompiler();
+    const out = c.compileFDSelect(fdSelect);
     expect(out).toEqual([3, 0, 1, 0, 0, 0, 0, 2]);
   });
   it("compiles charset of CID font", function () {
-    var charset = new _cff_parser.CFFCharset();
-    var c = new _cff_parser.CFFCompiler();
-    var numGlyphs = 7;
-    var out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), true);
+    const charset = new _cff_parser.CFFCharset();
+    const c = new _cff_parser.CFFCompiler();
+    const numGlyphs = 7;
+    const out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), true);
     expect(out).toEqual([2, 0, 0, 0, numGlyphs - 1]);
   });
   it("compiles charset of non CID font", function () {
-    var charset = new _cff_parser.CFFCharset(false, 0, ["space", "exclam"]);
-    var c = new _cff_parser.CFFCompiler();
-    var numGlyphs = 3;
-    var out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), false);
+    const charset = new _cff_parser.CFFCharset(false, 0, ["space", "exclam"]);
+    const c = new _cff_parser.CFFCompiler();
+    const numGlyphs = 3;
+    const out = c.compileCharset(charset, numGlyphs, new _cff_parser.CFFStrings(), false);
     expect(out).toEqual([0, 0, 1, 0, 2]);
   });
 });

+ 2 - 0
lib/test/unit/clitests_helper.js

@@ -29,6 +29,8 @@ var _node_stream = require("../../display/node_stream.js");
 
 var _api = require("../../display/api.js");
 
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
+
 if (!_is_node.isNodeJS) {
   throw new Error("The `gulp unittestcli` command can only be used in Node.js environments.");
 }

+ 48 - 74
lib/test/unit/cmap_spec.js

@@ -23,37 +23,21 @@
 
 var _cmap = require("../../core/cmap.js");
 
-var _display_utils = require("../../display/display_utils.js");
+var _test_utils = require("./test_utils.js");
 
-var _is_node = require("../../shared/is_node.js");
+var _api = require("../../display/api.js");
 
 var _primitives = require("../../core/primitives.js");
 
-var _node_utils = require("../../display/node_utils.js");
-
 var _stream = require("../../core/stream.js");
 
-var cMapUrl = {
-  dom: "../../external/bcmaps/",
-  node: "./external/bcmaps/"
-};
-var cMapPacked = true;
 describe("cmap", function () {
-  var fetchBuiltInCMap;
+  let fetchBuiltInCMap;
   beforeAll(function (done) {
-    var CMapReaderFactory;
-
-    if (_is_node.isNodeJS) {
-      CMapReaderFactory = new _node_utils.NodeCMapReaderFactory({
-        baseUrl: cMapUrl.node,
-        isCompressed: cMapPacked
-      });
-    } else {
-      CMapReaderFactory = new _display_utils.DOMCMapReaderFactory({
-        baseUrl: cMapUrl.dom,
-        isCompressed: cMapPacked
-      });
-    }
+    const CMapReaderFactory = new _api.DefaultCMapReaderFactory({
+      baseUrl: _test_utils.CMAP_PARAMS.cMapUrl,
+      isCompressed: _test_utils.CMAP_PARAMS.cMapPacked
+    });
 
     fetchBuiltInCMap = function (name) {
       return CMapReaderFactory.fetch({
@@ -67,10 +51,10 @@ describe("cmap", function () {
     fetchBuiltInCMap = null;
   });
   it("parses beginbfchar", function (done) {
-    var str = "2 beginbfchar\n" + "<03> <00>\n" + "<04> <01>\n" + "endbfchar\n";
-    var stream = new _stream.StringStream(str);
+    const str = "2 beginbfchar\n" + "<03> <00>\n" + "<04> <01>\n" + "endbfchar\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -84,10 +68,10 @@ describe("cmap", function () {
     });
   });
   it("parses beginbfrange with range", function (done) {
-    var str = "1 beginbfrange\n" + "<06> <0B> 0\n" + "endbfrange\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 beginbfrange\n" + "<06> <0B> 0\n" + "endbfrange\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -102,10 +86,10 @@ describe("cmap", function () {
     });
   });
   it("parses beginbfrange with array", function (done) {
-    var str = "1 beginbfrange\n" + "<0D> <12> [ 0 1 2 3 4 5 ]\n" + "endbfrange\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 beginbfrange\n" + "<0D> <12> [ 0 1 2 3 4 5 ]\n" + "endbfrange\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -120,10 +104,10 @@ describe("cmap", function () {
     });
   });
   it("parses begincidchar", function (done) {
-    var str = "1 begincidchar\n" + "<14> 0\n" + "endcidchar\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 begincidchar\n" + "<14> 0\n" + "endcidchar\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -136,10 +120,10 @@ describe("cmap", function () {
     });
   });
   it("parses begincidrange", function (done) {
-    var str = "1 begincidrange\n" + "<0016> <001B>   0\n" + "endcidrange\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 begincidrange\n" + "<0016> <001B>   0\n" + "endcidrange\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -154,15 +138,15 @@ describe("cmap", function () {
     });
   });
   it("decodes codespace ranges", function (done) {
-    var str = "1 begincodespacerange\n" + "<01> <02>\n" + "<00000003> <00000004>\n" + "endcodespacerange\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 begincodespacerange\n" + "<01> <02>\n" + "<00000003> <00000004>\n" + "endcodespacerange\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
     cmapPromise.then(function (cmap) {
-      var c = {};
+      const c = {};
       cmap.readCharCode(String.fromCharCode(1), 0, c);
       expect(c.charcode).toEqual(1);
       expect(c.length).toEqual(1);
@@ -175,15 +159,15 @@ describe("cmap", function () {
     });
   });
   it("decodes 4 byte codespace ranges", function (done) {
-    var str = "1 begincodespacerange\n" + "<8EA1A1A1> <8EA1FEFE>\n" + "endcodespacerange\n";
-    var stream = new _stream.StringStream(str);
+    const str = "1 begincodespacerange\n" + "<8EA1A1A1> <8EA1FEFE>\n" + "endcodespacerange\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
     cmapPromise.then(function (cmap) {
-      var c = {};
+      const c = {};
       cmap.readCharCode(String.fromCharCode(0x8e, 0xa1, 0xa1, 0xa1), 0, c);
       expect(c.charcode).toEqual(0x8ea1a1a1);
       expect(c.length).toEqual(4);
@@ -193,10 +177,10 @@ describe("cmap", function () {
     });
   });
   it("read usecmap", function (done) {
-    var str = "/Adobe-Japan1-1 usecmap\n";
-    var stream = new _stream.StringStream(str);
+    const str = "/Adobe-Japan1-1 usecmap\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream,
       fetchBuiltInCMap,
       useCMap: null
@@ -214,10 +198,10 @@ describe("cmap", function () {
     });
   });
   it("parses cmapname", function (done) {
-    var str = "/CMapName /Identity-H def\n";
-    var stream = new _stream.StringStream(str);
+    const str = "/CMapName /Identity-H def\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -229,10 +213,10 @@ describe("cmap", function () {
     });
   });
   it("parses wmode", function (done) {
-    var str = "/WMode 1 def\n";
-    var stream = new _stream.StringStream(str);
+    const str = "/WMode 1 def\n";
+    const stream = new _stream.StringStream(str);
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: stream
     });
 
@@ -244,7 +228,7 @@ describe("cmap", function () {
     });
   });
   it("loads built in cmap", function (done) {
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: _primitives.Name.get("Adobe-Japan1-1"),
       fetchBuiltInCMap,
       useCMap: null
@@ -262,7 +246,7 @@ describe("cmap", function () {
     });
   });
   it("loads built in identity cmap", function (done) {
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: _primitives.Name.get("Identity-H"),
       fetchBuiltInCMap,
       useCMap: null
@@ -281,7 +265,7 @@ describe("cmap", function () {
     });
   });
   it("attempts to load a non-existent built-in CMap", function (done) {
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: _primitives.Name.get("null"),
       fetchBuiltInCMap,
       useCMap: null
@@ -297,13 +281,13 @@ describe("cmap", function () {
   });
   it("attempts to load a built-in CMap without the necessary API parameters", function (done) {
     function tmpFetchBuiltInCMap(name) {
-      var CMapReaderFactory = _is_node.isNodeJS ? new _node_utils.NodeCMapReaderFactory({}) : new _display_utils.DOMCMapReaderFactory({});
+      const CMapReaderFactory = new _api.DefaultCMapReaderFactory({});
       return CMapReaderFactory.fetch({
         name
       });
     }
 
-    var cmapPromise = _cmap.CMapFactory.create({
+    const cmapPromise = _cmap.CMapFactory.create({
       encoding: _primitives.Name.get("Adobe-Japan1-1"),
       fetchBuiltInCMap: tmpFetchBuiltInCMap,
       useCMap: null
@@ -319,20 +303,10 @@ describe("cmap", function () {
   });
   it("attempts to load a built-in CMap with inconsistent API parameters", function (done) {
     function tmpFetchBuiltInCMap(name) {
-      let CMapReaderFactory;
-
-      if (_is_node.isNodeJS) {
-        CMapReaderFactory = new _node_utils.NodeCMapReaderFactory({
-          baseUrl: cMapUrl.node,
-          isCompressed: false
-        });
-      } else {
-        CMapReaderFactory = new _display_utils.DOMCMapReaderFactory({
-          baseUrl: cMapUrl.dom,
-          isCompressed: false
-        });
-      }
-
+      const CMapReaderFactory = new _api.DefaultCMapReaderFactory({
+        baseUrl: _test_utils.CMAP_PARAMS.cMapUrl,
+        isCompressed: false
+      });
       return CMapReaderFactory.fetch({
         name
       });

+ 34 - 0
lib/test/unit/core_utils_spec.js

@@ -208,4 +208,38 @@ describe("core_utils", function () {
       expect((0, _core_utils.isWhiteSpace)(undefined)).toEqual(false);
     });
   });
+  describe("parseXFAPath", function () {
+    it("should get a correctly parsed path", function () {
+      const path = "foo.bar[12].oof[3].rab.FOO[123].BAR[456]";
+      expect((0, _core_utils.parseXFAPath)(path)).toEqual([{
+        name: "foo",
+        pos: 0
+      }, {
+        name: "bar",
+        pos: 12
+      }, {
+        name: "oof",
+        pos: 3
+      }, {
+        name: "rab",
+        pos: 0
+      }, {
+        name: "FOO",
+        pos: 123
+      }, {
+        name: "BAR",
+        pos: 456
+      }]);
+    });
+  });
+  describe("escapePDFName", function () {
+    it("should escape PDF name", function () {
+      expect((0, _core_utils.escapePDFName)("hello")).toEqual("hello");
+      expect((0, _core_utils.escapePDFName)("\xfehello")).toEqual("#fehello");
+      expect((0, _core_utils.escapePDFName)("he\xfell\xffo")).toEqual("he#fell#ffo");
+      expect((0, _core_utils.escapePDFName)("\xfehe\xfell\xffo\xff")).toEqual("#fehe#fell#ffo#ff");
+      expect((0, _core_utils.escapePDFName)("#h#e#l#l#o")).toEqual("#23h#23e#23l#23l#23o");
+      expect((0, _core_utils.escapePDFName)("#()<>[]{}/%")).toEqual("#23#28#29#3c#3e#5b#5d#7b#7d#2f#25");
+    });
+  });
 });

+ 162 - 199
lib/test/unit/crypto_spec.js

@@ -29,17 +29,15 @@ var _util = require("../../shared/util.js");
 
 describe("crypto", function () {
   function hex2binary(s) {
-    var digits = "0123456789ABCDEF";
+    const digits = "0123456789ABCDEF";
     s = s.toUpperCase();
-    var n = s.length >> 1,
-        i,
-        j;
-    var result = new Uint8Array(n);
+    const n = s.length >> 1;
+    const result = new Uint8Array(n);
 
-    for (i = 0, j = 0; i < n; ++i) {
-      var d1 = s.charAt(j++);
-      var d2 = s.charAt(j++);
-      var value = digits.indexOf(d1) << 4 | digits.indexOf(d2);
+    for (let i = 0, j = 0; i < n; ++i) {
+      const d1 = s.charAt(j++);
+      const d2 = s.charAt(j++);
+      const value = digits.indexOf(d1) << 4 | digits.indexOf(d2);
       result[i] = value;
     }
 
@@ -48,189 +46,167 @@ describe("crypto", function () {
 
   describe("calculateMD5", function () {
     it("should pass RFC 1321 test #1", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("d41d8cd98f00b204e9800998ecf8427e");
+      const input = (0, _util.stringToBytes)("");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("d41d8cd98f00b204e9800998ecf8427e");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #2", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("a");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("0cc175b9c0f1b6a831c399e269772661");
+      const input = (0, _util.stringToBytes)("a");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("0cc175b9c0f1b6a831c399e269772661");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #3", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abc");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("900150983cd24fb0d6963f7d28e17f72");
+      const input = (0, _util.stringToBytes)("abc");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("900150983cd24fb0d6963f7d28e17f72");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #4", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("message digest");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("f96b697d7cb7938d525a2f31aaf161d0");
+      const input = (0, _util.stringToBytes)("message digest");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("f96b697d7cb7938d525a2f31aaf161d0");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #5", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abcdefghijklmnopqrstuvwxyz");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("c3fcd3d76192e4007dfb496cca67e13b");
+      const input = (0, _util.stringToBytes)("abcdefghijklmnopqrstuvwxyz");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("c3fcd3d76192e4007dfb496cca67e13b");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #6", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("d174ab98d277d9f5a5611c2c9f419d9f");
+      const input = (0, _util.stringToBytes)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("d174ab98d277d9f5a5611c2c9f419d9f");
       expect(result).toEqual(expected);
     });
     it("should pass RFC 1321 test #7", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890");
-      result = (0, _crypto.calculateMD5)(input, 0, input.length);
-      expected = hex2binary("57edf4a22be3c955ac49da2e2107b67a");
+      const input = (0, _util.stringToBytes)("123456789012345678901234567890123456789012345678" + "90123456789012345678901234567890");
+      const result = (0, _crypto.calculateMD5)(input, 0, input.length);
+      const expected = hex2binary("57edf4a22be3c955ac49da2e2107b67a");
       expect(result).toEqual(expected);
     });
   });
   describe("ARCFourCipher", function () {
     it("should pass test #1", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("0123456789abcdef");
-      input = hex2binary("0123456789abcdef");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("75b7878099e0c596");
+      const key = hex2binary("0123456789abcdef");
+      const input = hex2binary("0123456789abcdef");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("75b7878099e0c596");
       expect(result).toEqual(expected);
     });
     it("should pass test #2", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("0123456789abcdef");
-      input = hex2binary("0000000000000000");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("7494c2e7104b0879");
+      const key = hex2binary("0123456789abcdef");
+      const input = hex2binary("0000000000000000");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("7494c2e7104b0879");
       expect(result).toEqual(expected);
     });
     it("should pass test #3", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("0000000000000000");
-      input = hex2binary("0000000000000000");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("de188941a3375d3a");
+      const key = hex2binary("0000000000000000");
+      const input = hex2binary("0000000000000000");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("de188941a3375d3a");
       expect(result).toEqual(expected);
     });
     it("should pass test #4", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("ef012345");
-      input = hex2binary("00000000000000000000");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("d6a141a7ec3c38dfbd61");
+      const key = hex2binary("ef012345");
+      const input = hex2binary("00000000000000000000");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("d6a141a7ec3c38dfbd61");
       expect(result).toEqual(expected);
     });
     it("should pass test #5", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("0123456789abcdef");
-      input = hex2binary("010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "101010101010101010101");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76" + "533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b" + "1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377" + "108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d" + "7a4303dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747" + "b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f5d44" + "c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421d43df9b4" + "2e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5585cb009290e" + "2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb272912426445998514c1" + "5d53a18c864ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405" + "b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8d31509" + "eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c822d4b8c569d849" + "aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c4bad9ba24b96abf924372c" + "8a8fffb10d55354900a77a3db5f205e1b99fcd8660863a159ad4abe40fa48934163d" + "dde542a6585540fd683cbfd8c00f12129a284deacc4cdefe58be7137541c047126c8" + "d49e2755ab181ab7e940b0c0");
+      const key = hex2binary("0123456789abcdef");
+      const input = hex2binary("010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "10101010101010101010101010101010101010101010101010101010101010101010" + "101010101010101010101");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76" + "533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b" + "1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377" + "108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d" + "7a4303dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747" + "b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f5d44" + "c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421d43df9b4" + "2e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5585cb009290e" + "2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb272912426445998514c1" + "5d53a18c864ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405" + "b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8d31509" + "eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c822d4b8c569d849" + "aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c4bad9ba24b96abf924372c" + "8a8fffb10d55354900a77a3db5f205e1b99fcd8660863a159ad4abe40fa48934163d" + "dde542a6585540fd683cbfd8c00f12129a284deacc4cdefe58be7137541c047126c8" + "d49e2755ab181ab7e940b0c0");
       expect(result).toEqual(expected);
     });
     it("should pass test #6", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("fb029e3031323334");
-      input = hex2binary("aaaa0300000008004500004e661a00008011be640a0001220af" + "fffff00890089003a000080a601100001000000000000204543454a4548454346434" + "550464545494546464343414341434143414341414100002000011bd0b604");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f" + "58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53" + "80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7");
+      const key = hex2binary("fb029e3031323334");
+      const input = hex2binary("aaaa0300000008004500004e661a00008011be640a0001220af" + "fffff00890089003a000080a601100001000000000000204543454a4548454346434" + "550464545494546464343414341434143414341414100002000011bd0b604");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f" + "58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53" + "80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7");
       expect(result).toEqual(expected);
     });
     it("should pass test #7", function () {
-      var key, input, result, expected, cipher;
-      key = hex2binary("0123456789abcdef");
-      input = hex2binary("123456789abcdef0123456789abcdef0123456789abcdef012345678");
-      cipher = new _crypto.ARCFourCipher(key);
-      result = cipher.encryptBlock(input);
-      expected = hex2binary("66a0949f8af7d6891f7f832ba833c00c892ebe30143ce28740011ecf");
+      const key = hex2binary("0123456789abcdef");
+      const input = hex2binary("123456789abcdef0123456789abcdef0123456789abcdef012345678");
+      const cipher = new _crypto.ARCFourCipher(key);
+      const result = cipher.encryptBlock(input);
+      const expected = hex2binary("66a0949f8af7d6891f7f832ba833c00c892ebe30143ce28740011ecf");
       expect(result).toEqual(expected);
     });
   });
   describe("calculateSHA256", function () {
     it("should properly hash abc", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abc");
-      result = (0, _crypto.calculateSHA256)(input, 0, input.length);
-      expected = hex2binary("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
+      const input = (0, _util.stringToBytes)("abc");
+      const result = (0, _crypto.calculateSHA256)(input, 0, input.length);
+      const expected = hex2binary("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD");
       expect(result).toEqual(expected);
     });
     it("should properly hash a multiblock input", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
-      result = (0, _crypto.calculateSHA256)(input, 0, input.length);
-      expected = hex2binary("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1");
+      const input = (0, _util.stringToBytes)("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+      const result = (0, _crypto.calculateSHA256)(input, 0, input.length);
+      const expected = hex2binary("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1");
       expect(result).toEqual(expected);
     });
   });
   describe("calculateSHA384", function () {
     it("should properly hash abc", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abc");
-      result = (0, _crypto.calculateSHA384)(input, 0, input.length);
-      expected = hex2binary("CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163" + "1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7");
+      const input = (0, _util.stringToBytes)("abc");
+      const result = (0, _crypto.calculateSHA384)(input, 0, input.length);
+      const expected = hex2binary("CB00753F45A35E8BB5A03D699AC65007272C32AB0EDED163" + "1A8B605A43FF5BED8086072BA1E7CC2358BAECA134C825A7");
       expect(result).toEqual(expected);
     });
     it("should properly hash a multiblock input", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" + "ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" + "mnopqrstnopqrstu");
-      result = (0, _crypto.calculateSHA384)(input, 0, input.length);
-      expected = hex2binary("09330C33F71147E83D192FC782CD1B4753111B173B3B05D2" + "2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039");
+      const input = (0, _util.stringToBytes)("abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" + "ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" + "mnopqrstnopqrstu");
+      const result = (0, _crypto.calculateSHA384)(input, 0, input.length);
+      const expected = hex2binary("09330C33F71147E83D192FC782CD1B4753111B173B3B05D2" + "2FA08086E3B0F712FCC7C71A557E2DB966C3E9FA91746039");
       expect(result).toEqual(expected);
     });
   });
   describe("calculateSHA512", function () {
     it("should properly hash abc", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abc");
-      result = (0, _crypto.calculateSHA512)(input, 0, input.length);
-      expected = hex2binary("DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" + "0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" + "454D4423643CE80E2A9AC94FA54CA49F");
+      const input = (0, _util.stringToBytes)("abc");
+      const result = (0, _crypto.calculateSHA512)(input, 0, input.length);
+      const expected = hex2binary("DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" + "0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" + "454D4423643CE80E2A9AC94FA54CA49F");
       expect(result).toEqual(expected);
     });
     it("should properly hash a multiblock input", function () {
-      var input, result, expected;
-      input = (0, _util.stringToBytes)("abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" + "ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" + "mnopqrstnopqrstu");
-      result = (0, _crypto.calculateSHA512)(input, 0, input.length);
-      expected = hex2binary("8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" + "7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" + "C7D329EEB6DD26545E96E55B874BE909");
+      const input = (0, _util.stringToBytes)("abcdefghbcdefghicdefghijdefghijkefghijklfghijklm" + "ghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrs" + "mnopqrstnopqrstu");
+      const result = (0, _crypto.calculateSHA512)(input, 0, input.length);
+      const expected = hex2binary("8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" + "7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" + "C7D329EEB6DD26545E96E55B874BE909");
       expect(result).toEqual(expected);
     });
   });
   describe("AES128", function () {
     describe("Encryption", function () {
       it("should be able to encrypt a block", function () {
-        var input, key, result, expected, iv, cipher;
-        input = hex2binary("00112233445566778899aabbccddeeff");
-        key = hex2binary("000102030405060708090a0b0c0d0e0f");
-        iv = hex2binary("00000000000000000000000000000000");
-        cipher = new _crypto.AES128Cipher(key);
-        result = cipher.encrypt(input, iv);
-        expected = hex2binary("69c4e0d86a7b0430d8cdb78070b4c55a");
+        const input = hex2binary("00112233445566778899aabbccddeeff");
+        const key = hex2binary("000102030405060708090a0b0c0d0e0f");
+        const iv = hex2binary("00000000000000000000000000000000");
+        const cipher = new _crypto.AES128Cipher(key);
+        const result = cipher.encrypt(input, iv);
+        const expected = hex2binary("69c4e0d86a7b0430d8cdb78070b4c55a");
         expect(result).toEqual(expected);
       });
     });
     describe("Decryption", function () {
       it("should be able to decrypt a block with IV in stream", function () {
-        var input, key, result, expected, cipher;
-        input = hex2binary("0000000000000000000000000000000069c4e0d86a7b0430d" + "8cdb78070b4c55a");
-        key = hex2binary("000102030405060708090a0b0c0d0e0f");
-        cipher = new _crypto.AES128Cipher(key);
-        result = cipher.decryptBlock(input);
-        expected = hex2binary("00112233445566778899aabbccddeeff");
+        const input = hex2binary("0000000000000000000000000000000069c4e0d86a7b0430d" + "8cdb78070b4c55a");
+        const key = hex2binary("000102030405060708090a0b0c0d0e0f");
+        const cipher = new _crypto.AES128Cipher(key);
+        const result = cipher.decryptBlock(input);
+        const expected = hex2binary("00112233445566778899aabbccddeeff");
         expect(result).toEqual(expected);
       });
     });
@@ -238,130 +214,117 @@ describe("crypto", function () {
   describe("AES256", function () {
     describe("Encryption", function () {
       it("should be able to encrypt a block", function () {
-        var input, key, result, expected, iv, cipher;
-        input = hex2binary("00112233445566778899aabbccddeeff");
-        key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
-        iv = hex2binary("00000000000000000000000000000000");
-        cipher = new _crypto.AES256Cipher(key);
-        result = cipher.encrypt(input, iv);
-        expected = hex2binary("8ea2b7ca516745bfeafc49904b496089");
+        const input = hex2binary("00112233445566778899aabbccddeeff");
+        const key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
+        const iv = hex2binary("00000000000000000000000000000000");
+        const cipher = new _crypto.AES256Cipher(key);
+        const result = cipher.encrypt(input, iv);
+        const expected = hex2binary("8ea2b7ca516745bfeafc49904b496089");
         expect(result).toEqual(expected);
       });
     });
     describe("Decryption", function () {
       it("should be able to decrypt a block with specified iv", function () {
-        var input, key, result, expected, cipher, iv;
-        input = hex2binary("8ea2b7ca516745bfeafc49904b496089");
-        key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
-        iv = hex2binary("00000000000000000000000000000000");
-        cipher = new _crypto.AES256Cipher(key);
-        result = cipher.decryptBlock(input, false, iv);
-        expected = hex2binary("00112233445566778899aabbccddeeff");
+        const input = hex2binary("8ea2b7ca516745bfeafc49904b496089");
+        const key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
+        const iv = hex2binary("00000000000000000000000000000000");
+        const cipher = new _crypto.AES256Cipher(key);
+        const result = cipher.decryptBlock(input, false, iv);
+        const expected = hex2binary("00112233445566778899aabbccddeeff");
         expect(result).toEqual(expected);
       });
       it("should be able to decrypt a block with IV in stream", function () {
-        var input, key, result, expected, cipher;
-        input = hex2binary("000000000000000000000000000000008ea2b7ca516745bf" + "eafc49904b496089");
-        key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
-        cipher = new _crypto.AES256Cipher(key);
-        result = cipher.decryptBlock(input, false);
-        expected = hex2binary("00112233445566778899aabbccddeeff");
+        const input = hex2binary("000000000000000000000000000000008ea2b7ca516745bf" + "eafc49904b496089");
+        const key = hex2binary("000102030405060708090a0b0c0d0e0f101112131415161718" + "191a1b1c1d1e1f");
+        const cipher = new _crypto.AES256Cipher(key);
+        const result = cipher.decryptBlock(input, false);
+        const expected = hex2binary("00112233445566778899aabbccddeeff");
         expect(result).toEqual(expected);
       });
     });
   });
   describe("PDF17Algorithm", function () {
     it("should correctly check a user key", function () {
-      var password, userValidation, userPassword, alg, result;
-      alg = new _crypto.PDF17();
-      password = new Uint8Array([117, 115, 101, 114]);
-      userValidation = new Uint8Array([117, 169, 4, 32, 159, 101, 22, 220]);
-      userPassword = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221]);
-      result = alg.checkUserPassword(password, userValidation, userPassword);
+      const alg = new _crypto.PDF17();
+      const password = new Uint8Array([117, 115, 101, 114]);
+      const userValidation = new Uint8Array([117, 169, 4, 32, 159, 101, 22, 220]);
+      const userPassword = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221]);
+      const result = alg.checkUserPassword(password, userValidation, userPassword);
       expect(result).toEqual(true);
     });
     it("should correctly check an owner key", function () {
-      var password, ownerValidation, ownerPassword, alg, result, uBytes;
-      alg = new _crypto.PDF17();
-      password = new Uint8Array([111, 119, 110, 101, 114]);
-      ownerValidation = new Uint8Array([243, 118, 71, 153, 128, 17, 101, 62]);
-      ownerPassword = new Uint8Array([60, 98, 137, 35, 51, 101, 200, 152, 210, 178, 226, 228, 134, 205, 163, 24, 204, 126, 177, 36, 106, 50, 36, 125, 210, 172, 171, 120, 222, 108, 139, 115]);
-      uBytes = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221, 117, 169, 4, 32, 159, 101, 22, 220, 168, 94, 215, 192, 100, 38, 188, 40]);
-      result = alg.checkOwnerPassword(password, ownerValidation, uBytes, ownerPassword);
+      const alg = new _crypto.PDF17();
+      const password = new Uint8Array([111, 119, 110, 101, 114]);
+      const ownerValidation = new Uint8Array([243, 118, 71, 153, 128, 17, 101, 62]);
+      const ownerPassword = new Uint8Array([60, 98, 137, 35, 51, 101, 200, 152, 210, 178, 226, 228, 134, 205, 163, 24, 204, 126, 177, 36, 106, 50, 36, 125, 210, 172, 171, 120, 222, 108, 139, 115]);
+      const uBytes = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221, 117, 169, 4, 32, 159, 101, 22, 220, 168, 94, 215, 192, 100, 38, 188, 40]);
+      const result = alg.checkOwnerPassword(password, ownerValidation, uBytes, ownerPassword);
       expect(result).toEqual(true);
     });
     it("should generate a file encryption key from the user key", function () {
-      var password, userKeySalt, expected, alg, result, userEncryption;
-      alg = new _crypto.PDF17();
-      password = new Uint8Array([117, 115, 101, 114]);
-      userKeySalt = new Uint8Array([168, 94, 215, 192, 100, 38, 188, 40]);
-      userEncryption = new Uint8Array([35, 150, 195, 169, 245, 51, 51, 255, 158, 158, 33, 242, 231, 75, 125, 190, 25, 126, 172, 114, 195, 244, 137, 245, 234, 165, 42, 74, 60, 38, 17, 17]);
-      result = alg.getUserKey(password, userKeySalt, userEncryption);
-      expected = new Uint8Array([63, 114, 136, 209, 87, 61, 12, 30, 249, 1, 186, 144, 254, 248, 163, 153, 151, 51, 133, 10, 80, 152, 206, 15, 72, 187, 231, 33, 224, 239, 13, 213]);
+      const alg = new _crypto.PDF17();
+      const password = new Uint8Array([117, 115, 101, 114]);
+      const userKeySalt = new Uint8Array([168, 94, 215, 192, 100, 38, 188, 40]);
+      const userEncryption = new Uint8Array([35, 150, 195, 169, 245, 51, 51, 255, 158, 158, 33, 242, 231, 75, 125, 190, 25, 126, 172, 114, 195, 244, 137, 245, 234, 165, 42, 74, 60, 38, 17, 17]);
+      const result = alg.getUserKey(password, userKeySalt, userEncryption);
+      const expected = new Uint8Array([63, 114, 136, 209, 87, 61, 12, 30, 249, 1, 186, 144, 254, 248, 163, 153, 151, 51, 133, 10, 80, 152, 206, 15, 72, 187, 231, 33, 224, 239, 13, 213]);
       expect(result).toEqual(expected);
     });
     it("should generate a file encryption key from the owner key", function () {
-      var password, ownerKeySalt, expected, alg, result, ownerEncryption;
-      var uBytes;
-      alg = new _crypto.PDF17();
-      password = new Uint8Array([111, 119, 110, 101, 114]);
-      ownerKeySalt = new Uint8Array([200, 245, 242, 12, 218, 123, 24, 120]);
-      ownerEncryption = new Uint8Array([213, 202, 14, 189, 110, 76, 70, 191, 6, 195, 10, 190, 157, 100, 144, 85, 8, 62, 123, 178, 156, 229, 50, 40, 229, 216, 54, 222, 34, 38, 106, 223]);
-      uBytes = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221, 117, 169, 4, 32, 159, 101, 22, 220, 168, 94, 215, 192, 100, 38, 188, 40]);
-      result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
-      expected = new Uint8Array([63, 114, 136, 209, 87, 61, 12, 30, 249, 1, 186, 144, 254, 248, 163, 153, 151, 51, 133, 10, 80, 152, 206, 15, 72, 187, 231, 33, 224, 239, 13, 213]);
+      const alg = new _crypto.PDF17();
+      const password = new Uint8Array([111, 119, 110, 101, 114]);
+      const ownerKeySalt = new Uint8Array([200, 245, 242, 12, 218, 123, 24, 120]);
+      const ownerEncryption = new Uint8Array([213, 202, 14, 189, 110, 76, 70, 191, 6, 195, 10, 190, 157, 100, 144, 85, 8, 62, 123, 178, 156, 229, 50, 40, 229, 216, 54, 222, 34, 38, 106, 223]);
+      const uBytes = new Uint8Array([131, 242, 143, 160, 87, 2, 138, 134, 79, 253, 189, 173, 224, 73, 144, 241, 190, 81, 197, 15, 249, 105, 145, 151, 15, 194, 65, 3, 1, 126, 187, 221, 117, 169, 4, 32, 159, 101, 22, 220, 168, 94, 215, 192, 100, 38, 188, 40]);
+      const result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
+      const expected = new Uint8Array([63, 114, 136, 209, 87, 61, 12, 30, 249, 1, 186, 144, 254, 248, 163, 153, 151, 51, 133, 10, 80, 152, 206, 15, 72, 187, 231, 33, 224, 239, 13, 213]);
       expect(result).toEqual(expected);
     });
   });
   describe("PDF20Algorithm", function () {
     it("should correctly check a user key", function () {
-      var password, userValidation, userPassword, alg, result;
-      alg = new _crypto.PDF20();
-      password = new Uint8Array([117, 115, 101, 114]);
-      userValidation = new Uint8Array([83, 245, 146, 101, 198, 247, 34, 198]);
-      userPassword = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184]);
-      result = alg.checkUserPassword(password, userValidation, userPassword);
+      const alg = new _crypto.PDF20();
+      const password = new Uint8Array([117, 115, 101, 114]);
+      const userValidation = new Uint8Array([83, 245, 146, 101, 198, 247, 34, 198]);
+      const userPassword = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184]);
+      const result = alg.checkUserPassword(password, userValidation, userPassword);
       expect(result).toEqual(true);
     });
     it("should correctly check an owner key", function () {
-      var password, ownerValidation, ownerPassword, alg, result, uBytes;
-      alg = new _crypto.PDF20();
-      password = new Uint8Array([111, 119, 110, 101, 114]);
-      ownerValidation = new Uint8Array([142, 232, 169, 208, 202, 214, 5, 185]);
-      ownerPassword = new Uint8Array([88, 232, 62, 54, 245, 26, 245, 209, 137, 123, 221, 72, 199, 49, 37, 217, 31, 74, 115, 167, 127, 158, 176, 77, 45, 163, 87, 47, 39, 90, 217, 141]);
-      uBytes = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184, 83, 245, 146, 101, 198, 247, 34, 198, 191, 11, 16, 94, 237, 216, 20, 175]);
-      result = alg.checkOwnerPassword(password, ownerValidation, uBytes, ownerPassword);
+      const alg = new _crypto.PDF20();
+      const password = new Uint8Array([111, 119, 110, 101, 114]);
+      const ownerValidation = new Uint8Array([142, 232, 169, 208, 202, 214, 5, 185]);
+      const ownerPassword = new Uint8Array([88, 232, 62, 54, 245, 26, 245, 209, 137, 123, 221, 72, 199, 49, 37, 217, 31, 74, 115, 167, 127, 158, 176, 77, 45, 163, 87, 47, 39, 90, 217, 141]);
+      const uBytes = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184, 83, 245, 146, 101, 198, 247, 34, 198, 191, 11, 16, 94, 237, 216, 20, 175]);
+      const result = alg.checkOwnerPassword(password, ownerValidation, uBytes, ownerPassword);
       expect(result).toEqual(true);
     });
     it("should generate a file encryption key from the user key", function () {
-      var password, userKeySalt, expected, alg, result, userEncryption;
-      alg = new _crypto.PDF20();
-      password = new Uint8Array([117, 115, 101, 114]);
-      userKeySalt = new Uint8Array([191, 11, 16, 94, 237, 216, 20, 175]);
-      userEncryption = new Uint8Array([121, 208, 2, 181, 230, 89, 156, 60, 253, 143, 212, 28, 84, 180, 196, 177, 173, 128, 221, 107, 46, 20, 94, 186, 135, 51, 95, 24, 20, 223, 254, 36]);
-      result = alg.getUserKey(password, userKeySalt, userEncryption);
-      expected = new Uint8Array([42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248, 133, 18, 189, 61, 34, 107, 79, 29, 56, 59, 181, 213, 118, 113, 34, 65, 210, 87, 174, 22, 239]);
+      const alg = new _crypto.PDF20();
+      const password = new Uint8Array([117, 115, 101, 114]);
+      const userKeySalt = new Uint8Array([191, 11, 16, 94, 237, 216, 20, 175]);
+      const userEncryption = new Uint8Array([121, 208, 2, 181, 230, 89, 156, 60, 253, 143, 212, 28, 84, 180, 196, 177, 173, 128, 221, 107, 46, 20, 94, 186, 135, 51, 95, 24, 20, 223, 254, 36]);
+      const result = alg.getUserKey(password, userKeySalt, userEncryption);
+      const expected = new Uint8Array([42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248, 133, 18, 189, 61, 34, 107, 79, 29, 56, 59, 181, 213, 118, 113, 34, 65, 210, 87, 174, 22, 239]);
       expect(result).toEqual(expected);
     });
     it("should generate a file encryption key from the owner key", function () {
-      var password, ownerKeySalt, expected, alg, result, ownerEncryption;
-      var uBytes;
-      alg = new _crypto.PDF20();
-      password = new Uint8Array([111, 119, 110, 101, 114]);
-      ownerKeySalt = new Uint8Array([29, 208, 185, 46, 11, 76, 135, 149]);
-      ownerEncryption = new Uint8Array([209, 73, 224, 77, 103, 155, 201, 181, 190, 68, 223, 20, 62, 90, 56, 210, 5, 240, 178, 128, 238, 124, 68, 254, 253, 244, 62, 108, 208, 135, 10, 251]);
-      uBytes = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184, 83, 245, 146, 101, 198, 247, 34, 198, 191, 11, 16, 94, 237, 216, 20, 175]);
-      result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
-      expected = new Uint8Array([42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248, 133, 18, 189, 61, 34, 107, 79, 29, 56, 59, 181, 213, 118, 113, 34, 65, 210, 87, 174, 22, 239]);
+      const alg = new _crypto.PDF20();
+      const password = new Uint8Array([111, 119, 110, 101, 114]);
+      const ownerKeySalt = new Uint8Array([29, 208, 185, 46, 11, 76, 135, 149]);
+      const ownerEncryption = new Uint8Array([209, 73, 224, 77, 103, 155, 201, 181, 190, 68, 223, 20, 62, 90, 56, 210, 5, 240, 178, 128, 238, 124, 68, 254, 253, 244, 62, 108, 208, 135, 10, 251]);
+      const uBytes = new Uint8Array([94, 230, 205, 75, 166, 99, 250, 76, 219, 128, 17, 85, 57, 17, 33, 164, 150, 46, 103, 176, 160, 156, 187, 233, 166, 223, 163, 253, 147, 235, 95, 184, 83, 245, 146, 101, 198, 247, 34, 198, 191, 11, 16, 94, 237, 216, 20, 175]);
+      const result = alg.getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
+      const expected = new Uint8Array([42, 218, 213, 39, 73, 91, 72, 79, 67, 38, 248, 133, 18, 189, 61, 34, 107, 79, 29, 56, 59, 181, 213, 118, 113, 34, 65, 210, 87, 174, 22, 239]);
       expect(result).toEqual(expected);
     });
   });
 });
 describe("CipherTransformFactory", function () {
   function buildDict(map) {
-    var dict = new _primitives.Dict();
+    const dict = new _primitives.Dict();
 
-    for (var key in map) {
+    for (const key in map) {
       dict.set(key, map[key]);
     }
 
@@ -370,7 +333,7 @@ describe("CipherTransformFactory", function () {
 
   function ensurePasswordCorrect(done, dict, fileId, password) {
     try {
-      var factory = new _crypto.CipherTransformFactory(dict, fileId, password);
+      const factory = new _crypto.CipherTransformFactory(dict, fileId, password);
       expect("createCipherTransform" in factory).toEqual(true);
     } catch (ex) {
       done.fail("Password should be accepted: " + ex);
@@ -414,8 +377,8 @@ describe("CipherTransformFactory", function () {
     expect(string).toEqual(decrypted);
   }
 
-  var fileId1, fileId2, dict1, dict2, dict3;
-  var aes256Dict, aes256IsoDict, aes256BlankDict, aes256IsoBlankDict;
+  let fileId1, fileId2, dict1, dict2, dict3;
+  let aes256Dict, aes256IsoDict, aes256BlankDict, aes256IsoBlankDict;
   beforeAll(function (done) {
     fileId1 = unescape("%F6%C6%AF%17%F3rR%8DRM%9A%80%D1%EF%DF%18");
     fileId2 = unescape("%3CL_%3AD%96%AF@%9A%9D%B3%3Cx%1Cv%AC");

+ 7 - 18
lib/test/unit/custom_spec.js

@@ -21,15 +21,9 @@
  */
 "use strict";
 
-var _test_utils = require("./test_utils.js");
-
-var _display_utils = require("../../display/display_utils.js");
-
 var _api = require("../../display/api.js");
 
-var _is_node = require("../../shared/is_node.js");
-
-var _node_utils = require("../../display/node_utils.js");
+var _test_utils = require("./test_utils.js");
 
 function getTopLeftPixel(canvasContext) {
   const imgData = canvasContext.getImageData(0, 0, 1, 1);
@@ -47,12 +41,7 @@ describe("custom canvas rendering", function () {
   let loadingTask;
   let page;
   beforeAll(function (done) {
-    if (_is_node.isNodeJS) {
-      CanvasFactory = new _node_utils.NodeCanvasFactory();
-    } else {
-      CanvasFactory = new _display_utils.DOMCanvasFactory();
-    }
-
+    CanvasFactory = new _api.DefaultCanvasFactory();
     loadingTask = (0, _api.getDocument)(transparentGetDocumentParams);
     loadingTask.promise.then(function (doc) {
       return doc.getPage(1);
@@ -67,10 +56,10 @@ describe("custom canvas rendering", function () {
     loadingTask.destroy().then(done);
   });
   it("renders to canvas with a default white background", function (done) {
-    var viewport = page.getViewport({
+    const viewport = page.getViewport({
       scale: 1
     });
-    var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
+    const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
     const renderTask = page.render({
       canvasContext: canvasAndCtx.context,
       viewport
@@ -87,10 +76,10 @@ describe("custom canvas rendering", function () {
     }).catch(done.fail);
   });
   it("renders to canvas with a custom background", function (done) {
-    var viewport = page.getViewport({
+    const viewport = page.getViewport({
       scale: 1
     });
-    var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
+    const canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height);
     const renderTask = page.render({
       canvasContext: canvasAndCtx.context,
       viewport,
@@ -163,7 +152,7 @@ describe("custom ownerDocument", function () {
         }]
       }
     };
-    const CanvasFactory = _is_node.isNodeJS ? new _node_utils.NodeCanvasFactory() : new _display_utils.DOMCanvasFactory({
+    const CanvasFactory = new _api.DefaultCanvasFactory({
       ownerDocument
     });
     return {

+ 54 - 0
lib/test/unit/default_appearance_spec.js

@@ -0,0 +1,54 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2020 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.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+var _default_appearance = require("../../core/default_appearance.js");
+
+var _primitives = require("../../core/primitives.js");
+
+describe("Default appearance", function () {
+  describe("parseDefaultAppearance and createDefaultAppearance", function () {
+    it("should parse and create default appearance", function () {
+      const da = "/F1 12 Tf 0.10 0.20 0.30 rg";
+      const result = {
+        fontSize: 12,
+        fontName: _primitives.Name.get("F1"),
+        fontColor: new Uint8ClampedArray([26, 51, 76])
+      };
+      expect((0, _default_appearance.parseDefaultAppearance)(da)).toEqual(result);
+      expect((0, _default_appearance.createDefaultAppearance)(result)).toEqual(da);
+      expect((0, _default_appearance.parseDefaultAppearance)("0.1 0.2 0.3 rg /F1 12 Tf 0.3 0.2 0.1 rg /F2 13 Tf")).toEqual({
+        fontSize: 13,
+        fontName: _primitives.Name.get("F2"),
+        fontColor: new Uint8ClampedArray([76, 51, 26])
+      });
+    });
+    it("should parse default appearance with save/restore", function () {
+      const da = "q Q 0.10 0.20 0.30 rg /F1 12 Tf q 0.30 0.20 0.10 rg /F2 13 Tf Q";
+      expect((0, _default_appearance.parseDefaultAppearance)(da)).toEqual({
+        fontSize: 12,
+        fontName: _primitives.Name.get("F1"),
+        fontColor: new Uint8ClampedArray([26, 51, 76])
+      });
+    });
+  });
+});

+ 24 - 19
lib/test/unit/display_svg_spec.js

@@ -21,8 +21,6 @@
  */
 "use strict";
 
-var _domstubs = require("../../examples/node/domstubs.js");
-
 var _test_utils = require("./test_utils.js");
 
 var _api = require("../../display/api.js");
@@ -46,9 +44,9 @@ function withZlib(isZlibRequired, callback) {
     return callback();
   }
 
-  var zlib = require("zlib");
+  const zlib = require("zlib");
 
-  var deflateSync = zlib.deflateSync;
+  const deflateSync = zlib.deflateSync;
   zlib.deflateSync = disabledDeflateSync;
 
   function disabledDeflateSync() {
@@ -61,14 +59,14 @@ function withZlib(isZlibRequired, callback) {
     }
   }
 
-  var promise = callback();
+  const promise = callback();
   promise.then(restoreDeflateSync, restoreDeflateSync);
   return promise;
 }
 
 describe("SVGGraphics", function () {
-  var loadingTask;
-  var page;
+  let loadingTask;
+  let page;
   beforeAll(function (done) {
     loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("xobject-image.pdf"));
     loadingTask.promise.then(function (doc) {
@@ -83,31 +81,39 @@ describe("SVGGraphics", function () {
   });
   describe("paintImageXObject", function () {
     function getSVGImage() {
-      var svgGfx;
+      let svgGfx;
       return page.getOperatorList().then(function (opList) {
-        var forceDataSchema = true;
+        const forceDataSchema = true;
         svgGfx = new _svg.SVGGraphics(page.commonObjs, page.objs, forceDataSchema);
         return svgGfx.loadDependencies(opList);
       }).then(function () {
-        var svgImg;
-        var elementContainer = {
+        let svgImg;
+        const elementContainer = {
           appendChild(element) {
             svgImg = element;
           }
 
         };
-        var xobjectObjId = "img_p0_1";
+        const xobjectObjId = "img_p0_1";
 
         if (_is_node.isNodeJS) {
-          (0, _domstubs.setStubs)(global);
+          const {
+            setStubs
+          } = require("../../examples/node/domstubs.js");
+
+          setStubs(global);
         }
 
         try {
-          var imgData = svgGfx.objs.get(xobjectObjId);
+          const imgData = svgGfx.objs.get(xobjectObjId);
           svgGfx.paintInlineImageXObject(imgData, elementContainer);
         } finally {
           if (_is_node.isNodeJS) {
-            (0, _domstubs.unsetStubs)(global);
+            const {
+              unsetStubs
+            } = require("../../examples/node/domstubs.js");
+
+            unsetStubs(global);
           }
         }
 
@@ -120,9 +126,8 @@ describe("SVGGraphics", function () {
         require("zlib");
       }
 
-      expect(testFunc.toString()).toMatch(/\srequire\(["']zlib["']\)/);
-
       if (_is_node.isNodeJS) {
+        expect(testFunc.toString()).toMatch(/\srequire\(["']zlib["']\)/);
         expect(testFunc).not.toThrow();
       } else {
         expect(testFunc).toThrow();
@@ -137,7 +142,7 @@ describe("SVGGraphics", function () {
         expect(svgImg.nodeName).toBe("svg:image");
         expect(svgImg.getAttributeNS(null, "width")).toBe("200px");
         expect(svgImg.getAttributeNS(null, "height")).toBe("100px");
-        var imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
+        const imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
         expect(imgUrl).toMatch(/^data:image\/png;base64,/);
         expect(imgUrl.length).toBeLessThan(367);
       }).then(done, done.fail);
@@ -147,7 +152,7 @@ describe("SVGGraphics", function () {
         expect(svgImg.nodeName).toBe("svg:image");
         expect(svgImg.getAttributeNS(null, "width")).toBe("200px");
         expect(svgImg.getAttributeNS(null, "height")).toBe("100px");
-        var imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
+        const imgUrl = svgImg.getAttributeNS(XLINK_NS, "href");
         expect(imgUrl).toMatch(/^data:image\/png;base64,/);
         expect(imgUrl.length).toBe(80246);
       }).then(done, done.fail);

+ 1 - 1
lib/test/unit/display_utils_spec.js

@@ -203,7 +203,7 @@ describe("display_utils", function () {
           undefined: null,
           null: null,
           42: null,
-          "2019": null,
+          2019: null,
           D2019: null,
           "D:": null,
           "D:201": null,

+ 187 - 20
lib/test/unit/document_spec.js

@@ -52,19 +52,37 @@ describe("document", function () {
     });
   });
   describe("PDFDocument", function () {
-    const pdfManager = {
-      get docId() {
-        return "d0";
-      }
-
-    };
     const stream = new _stream.StringStream("Dummy_PDF_data");
 
-    function getDocument(acroForm) {
-      const pdfDocument = new _document.PDFDocument(pdfManager, stream);
-      pdfDocument.catalog = {
+    function getDocument(acroForm, xref = new _test_utils.XRefMock()) {
+      const catalog = {
         acroForm
       };
+      const pdfManager = {
+        get docId() {
+          return "d0";
+        },
+
+        ensureCatalog(prop, args) {
+          return pdfManager.ensure(catalog, prop, args);
+        },
+
+        ensure(obj, prop, args) {
+          return new Promise(function (resolve) {
+            const value = obj[prop];
+
+            if (typeof value === "function") {
+              resolve(value.apply(obj, args));
+            } else {
+              resolve(value);
+            }
+          });
+        }
+
+      };
+      const pdfDocument = new _document.PDFDocument(pdfManager, stream);
+      pdfDocument.xref = xref;
+      pdfDocument.catalog = catalog;
       return pdfDocument;
     }
 
@@ -72,7 +90,8 @@ describe("document", function () {
       const pdfDocument = getDocument(null);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: false
       });
     });
     it("should get form info when XFA is present", function () {
@@ -81,25 +100,29 @@ describe("document", function () {
       let pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: false
       });
       acroForm.set("XFA", ["foo", "bar"]);
       pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: true
+        hasXfa: true,
+        hasFields: false
       });
       acroForm.set("XFA", new _stream.StringStream(""));
       pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: false
       });
       acroForm.set("XFA", new _stream.StringStream("non-empty"));
       pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: true
+        hasXfa: true,
+        hasFields: false
       });
     });
     it("should get form info when AcroForm is present", function () {
@@ -108,20 +131,23 @@ describe("document", function () {
       let pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: false
       });
       acroForm.set("Fields", ["foo", "bar"]);
       pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: true,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: true
       });
       acroForm.set("Fields", ["foo", "bar"]);
       acroForm.set("SigFlags", 2);
       pdfDocument = getDocument(acroForm);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: true,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: true
       });
       const annotationDict = new _primitives.Dict();
       annotationDict.set("FT", _primitives.Name.get("Sig"));
@@ -134,7 +160,7 @@ describe("document", function () {
 
       const kidsRef = _primitives.Ref.get(10, 0);
 
-      pdfDocument.xref = new _test_utils.XRefMock([{
+      const xref = new _test_utils.XRefMock([{
         ref: annotationRef,
         data: annotationDict
       }, {
@@ -143,11 +169,152 @@ describe("document", function () {
       }]);
       acroForm.set("Fields", [kidsRef]);
       acroForm.set("SigFlags", 3);
-      pdfDocument = getDocument(acroForm);
+      pdfDocument = getDocument(acroForm, xref);
       expect(pdfDocument.formInfo).toEqual({
         hasAcroForm: false,
-        hasXfa: false
+        hasXfa: false,
+        hasFields: true
       });
     });
+    it("should get calculation order array or null", function () {
+      const acroForm = new _primitives.Dict();
+      let pdfDocument = getDocument(acroForm);
+      expect(pdfDocument.calculationOrderIds).toEqual(null);
+      acroForm.set("CO", [_primitives.Ref.get(1, 0), _primitives.Ref.get(2, 0), _primitives.Ref.get(3, 0)]);
+      pdfDocument = getDocument(acroForm);
+      expect(pdfDocument.calculationOrderIds).toEqual(["1R", "2R", "3R"]);
+      acroForm.set("CO", []);
+      pdfDocument = getDocument(acroForm);
+      expect(pdfDocument.calculationOrderIds).toEqual(null);
+      acroForm.set("CO", ["1", "2"]);
+      pdfDocument = getDocument(acroForm);
+      expect(pdfDocument.calculationOrderIds).toEqual(null);
+      acroForm.set("CO", ["1", _primitives.Ref.get(1, 0), "2"]);
+      pdfDocument = getDocument(acroForm);
+      expect(pdfDocument.calculationOrderIds).toEqual(["1R"]);
+    });
+    it("should get field objects array or null", async function () {
+      const acroForm = new _primitives.Dict();
+      let pdfDocument = getDocument(acroForm);
+      let fields = await pdfDocument.fieldObjects;
+      expect(fields).toEqual(null);
+      acroForm.set("Fields", []);
+      pdfDocument = getDocument(acroForm);
+      fields = await pdfDocument.fieldObjects;
+      expect(fields).toEqual(null);
+
+      const kid1Ref = _primitives.Ref.get(314, 0);
+
+      const kid11Ref = _primitives.Ref.get(159, 0);
+
+      const kid2Ref = _primitives.Ref.get(265, 0);
+
+      const kid2BisRef = _primitives.Ref.get(266, 0);
+
+      const parentRef = _primitives.Ref.get(358, 0);
+
+      const allFields = Object.create(null);
+
+      for (const name of ["parent", "kid1", "kid2", "kid11"]) {
+        const buttonWidgetDict = new _primitives.Dict();
+        buttonWidgetDict.set("Type", _primitives.Name.get("Annot"));
+        buttonWidgetDict.set("Subtype", _primitives.Name.get("Widget"));
+        buttonWidgetDict.set("FT", _primitives.Name.get("Btn"));
+        buttonWidgetDict.set("T", name);
+        allFields[name] = buttonWidgetDict;
+      }
+
+      allFields.kid1.set("Kids", [kid11Ref]);
+      allFields.parent.set("Kids", [kid1Ref, kid2Ref, kid2BisRef]);
+      const xref = new _test_utils.XRefMock([{
+        ref: parentRef,
+        data: allFields.parent
+      }, {
+        ref: kid1Ref,
+        data: allFields.kid1
+      }, {
+        ref: kid11Ref,
+        data: allFields.kid11
+      }, {
+        ref: kid2Ref,
+        data: allFields.kid2
+      }, {
+        ref: kid2BisRef,
+        data: allFields.kid2
+      }]);
+      acroForm.set("Fields", [parentRef]);
+      pdfDocument = getDocument(acroForm, xref);
+      fields = await pdfDocument.fieldObjects;
+
+      for (const [name, objs] of Object.entries(fields)) {
+        fields[name] = objs.map(obj => obj.id);
+      }
+
+      expect(fields["parent.kid1"]).toEqual(["314R"]);
+      expect(fields["parent.kid1.kid11"]).toEqual(["159R"]);
+      expect(fields["parent.kid2"]).toEqual(["265R", "266R"]);
+      expect(fields.parent).toEqual(["358R"]);
+    });
+    it("should check if fields have any actions", async function () {
+      const acroForm = new _primitives.Dict();
+      let pdfDocument = getDocument(acroForm);
+      let hasJSActions = await pdfDocument.hasJSActions;
+      expect(hasJSActions).toEqual(false);
+      acroForm.set("Fields", []);
+      pdfDocument = getDocument(acroForm);
+      hasJSActions = await pdfDocument.hasJSActions;
+      expect(hasJSActions).toEqual(false);
+
+      const kid1Ref = _primitives.Ref.get(314, 0);
+
+      const kid11Ref = _primitives.Ref.get(159, 0);
+
+      const kid2Ref = _primitives.Ref.get(265, 0);
+
+      const parentRef = _primitives.Ref.get(358, 0);
+
+      const allFields = Object.create(null);
+
+      for (const name of ["parent", "kid1", "kid2", "kid11"]) {
+        const buttonWidgetDict = new _primitives.Dict();
+        buttonWidgetDict.set("Type", _primitives.Name.get("Annot"));
+        buttonWidgetDict.set("Subtype", _primitives.Name.get("Widget"));
+        buttonWidgetDict.set("FT", _primitives.Name.get("Btn"));
+        buttonWidgetDict.set("T", name);
+        allFields[name] = buttonWidgetDict;
+      }
+
+      allFields.kid1.set("Kids", [kid11Ref]);
+      allFields.parent.set("Kids", [kid1Ref, kid2Ref]);
+      const xref = new _test_utils.XRefMock([{
+        ref: parentRef,
+        data: allFields.parent
+      }, {
+        ref: kid1Ref,
+        data: allFields.kid1
+      }, {
+        ref: kid11Ref,
+        data: allFields.kid11
+      }, {
+        ref: kid2Ref,
+        data: allFields.kid2
+      }]);
+      acroForm.set("Fields", [parentRef]);
+      pdfDocument = getDocument(acroForm, xref);
+      hasJSActions = await pdfDocument.hasJSActions;
+      expect(hasJSActions).toEqual(false);
+
+      const JS = _primitives.Name.get("JavaScript");
+
+      const additionalActionsDict = new _primitives.Dict();
+      const eDict = new _primitives.Dict();
+      eDict.set("JS", "hello()");
+      eDict.set("S", JS);
+      additionalActionsDict.set("E", eDict);
+      allFields.kid2.set("AA", additionalActionsDict);
+      pdfDocument = getDocument(acroForm, xref);
+      hasJSActions = await pdfDocument.hasJSActions;
+      expect(hasJSActions).toEqual(true);
+    });
   });
 });

+ 30 - 30
lib/test/unit/evaluator_spec.js

@@ -60,8 +60,8 @@ describe("evaluator", function () {
   };
 
   function runOperatorListCheck(evaluator, stream, resources, callback) {
-    var result = new _operator_list.OperatorList();
-    var task = new _worker.WorkerTask("OperatorListCheck");
+    const result = new _operator_list.OperatorList();
+    const task = new _worker.WorkerTask("OperatorListCheck");
     evaluator.getOperatorList({
       stream,
       task,
@@ -74,7 +74,7 @@ describe("evaluator", function () {
     });
   }
 
-  var partialEvaluator;
+  let partialEvaluator;
   beforeAll(function (done) {
     partialEvaluator = new _evaluator.PartialEvaluator({
       xref: new _test_utils.XRefMock(),
@@ -89,7 +89,7 @@ describe("evaluator", function () {
   });
   describe("splitCombinedOperations", function () {
     it("should reject unknown operations", function (done) {
-      var stream = new _stream.StringStream("fTT");
+      const stream = new _stream.StringStream("fTT");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(1);
@@ -99,7 +99,7 @@ describe("evaluator", function () {
       });
     });
     it("should handle one operation", function (done) {
-      var stream = new _stream.StringStream("Q");
+      const stream = new _stream.StringStream("Q");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(1);
@@ -118,7 +118,7 @@ describe("evaluator", function () {
       xObject.set("Res1", imgStream);
       const resources = new ResourcesMock();
       resources.XObject = xObject;
-      var stream = new _stream.StringStream("/Res1 DoQ");
+      const stream = new _stream.StringStream("/Res1 DoQ");
       runOperatorListCheck(partialEvaluator, stream, resources, function (result) {
         expect(result.fnArray.length).toEqual(3);
         expect(result.fnArray[0]).toEqual(_util.OPS.dependency);
@@ -132,7 +132,7 @@ describe("evaluator", function () {
       });
     });
     it("should handle three glued operations", function (done) {
-      var stream = new _stream.StringStream("fff");
+      const stream = new _stream.StringStream("fff");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(3);
@@ -143,9 +143,9 @@ describe("evaluator", function () {
       });
     });
     it("should handle three glued operations #2", function (done) {
-      var resources = new ResourcesMock();
+      const resources = new ResourcesMock();
       resources.Res1 = {};
-      var stream = new _stream.StringStream("B*Bf*");
+      const stream = new _stream.StringStream("B*Bf*");
       runOperatorListCheck(partialEvaluator, stream, resources, function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(3);
@@ -156,7 +156,7 @@ describe("evaluator", function () {
       });
     });
     it("should handle glued operations and operands", function (done) {
-      var stream = new _stream.StringStream("f5 Ts");
+      const stream = new _stream.StringStream("f5 Ts");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(2);
@@ -169,7 +169,7 @@ describe("evaluator", function () {
       });
     });
     it("should handle glued operations and literals", function (done) {
-      var stream = new _stream.StringStream("trueifalserinulln");
+      const stream = new _stream.StringStream("trueifalserinulln");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(3);
@@ -188,7 +188,7 @@ describe("evaluator", function () {
   });
   describe("validateNumberOfArgs", function () {
     it("should execute if correct number of arguments", function (done) {
-      var stream = new _stream.StringStream("5 1 d0");
+      const stream = new _stream.StringStream("5 1 d0");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(result.argsArray[0][0]).toEqual(5);
         expect(result.argsArray[0][1]).toEqual(1);
@@ -197,7 +197,7 @@ describe("evaluator", function () {
       });
     });
     it("should execute if too many arguments", function (done) {
-      var stream = new _stream.StringStream("5 1 4 d0");
+      const stream = new _stream.StringStream("5 1 4 d0");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(result.argsArray[0][0]).toEqual(1);
         expect(result.argsArray[0][1]).toEqual(4);
@@ -213,7 +213,7 @@ describe("evaluator", function () {
       extGState.set("GS2", gState);
       const resources = new ResourcesMock();
       resources.ExtGState = extGState;
-      var stream = new _stream.StringStream("/F2 /GS2 gs 5.711 Tf");
+      const stream = new _stream.StringStream("/F2 /GS2 gs 5.711 Tf");
       runOperatorListCheck(partialEvaluator, stream, resources, function (result) {
         expect(result.fnArray.length).toEqual(3);
         expect(result.fnArray[0]).toEqual(_util.OPS.setGState);
@@ -227,7 +227,7 @@ describe("evaluator", function () {
       });
     });
     it("should skip if too few arguments", function (done) {
-      var stream = new _stream.StringStream("5 d0");
+      const stream = new _stream.StringStream("5 d0");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(result.argsArray).toEqual([]);
         expect(result.fnArray).toEqual([]);
@@ -253,7 +253,7 @@ describe("evaluator", function () {
       });
     });
     it("should close opened saves", function (done) {
-      var stream = new _stream.StringStream("qq");
+      const stream = new _stream.StringStream("qq");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(!!result.fnArray && !!result.argsArray).toEqual(true);
         expect(result.fnArray.length).toEqual(4);
@@ -265,7 +265,7 @@ describe("evaluator", function () {
       });
     });
     it("should error on paintXObject if name is missing", function (done) {
-      var stream = new _stream.StringStream("/ Do");
+      const stream = new _stream.StringStream("/ Do");
       runOperatorListCheck(partialEvaluator, stream, new ResourcesMock(), function (result) {
         expect(result instanceof _util.FormatError).toEqual(true);
         expect(result.message).toEqual("XObject must be referred to by name.");
@@ -273,14 +273,14 @@ describe("evaluator", function () {
       });
     });
     it("should skip paintXObject if subtype is PS", function (done) {
-      var xobjStreamDict = new _primitives.Dict();
+      const xobjStreamDict = new _primitives.Dict();
       xobjStreamDict.set("Subtype", _primitives.Name.get("PS"));
-      var xobjStream = new _stream.Stream([], 0, 0, xobjStreamDict);
-      var xobjs = new _primitives.Dict();
+      const xobjStream = new _stream.Stream([], 0, 0, xobjStreamDict);
+      const xobjs = new _primitives.Dict();
       xobjs.set("Res1", xobjStream);
-      var resources = new _primitives.Dict();
+      const resources = new _primitives.Dict();
       resources.set("XObject", xobjs);
-      var stream = new _stream.StringStream("/Res1 Do");
+      const stream = new _stream.StringStream("/Res1 Do");
       runOperatorListCheck(partialEvaluator, stream, resources, function (result) {
         expect(result.argsArray).toEqual([]);
         expect(result.fnArray).toEqual([]);
@@ -290,10 +290,10 @@ describe("evaluator", function () {
   });
   describe("thread control", function () {
     it("should abort operator list parsing", function (done) {
-      var stream = new _stream.StringStream("qqQQ");
-      var resources = new ResourcesMock();
-      var result = new _operator_list.OperatorList();
-      var task = new _worker.WorkerTask("OperatorListAbort");
+      const stream = new _stream.StringStream("qqQQ");
+      const resources = new ResourcesMock();
+      const result = new _operator_list.OperatorList();
+      const task = new _worker.WorkerTask("OperatorListAbort");
       task.terminate();
       partialEvaluator.getOperatorList({
         stream,
@@ -307,9 +307,9 @@ describe("evaluator", function () {
       });
     });
     it("should abort text parsing parsing", function (done) {
-      var resources = new ResourcesMock();
-      var stream = new _stream.StringStream("qqQQ");
-      var task = new _worker.WorkerTask("TextContentAbort");
+      const resources = new ResourcesMock();
+      const stream = new _stream.StringStream("qqQQ");
+      const task = new _worker.WorkerTask("TextContentAbort");
       task.terminate();
       partialEvaluator.getTextContent({
         stream,
@@ -328,7 +328,7 @@ describe("evaluator", function () {
     }
 
     it("should get correct total length after flushing", function () {
-      var operatorList = new _operator_list.OperatorList(null, new StreamSinkMock());
+      const operatorList = new _operator_list.OperatorList(null, new StreamSinkMock());
       operatorList.addOp(_util.OPS.save, null);
       operatorList.addOp(_util.OPS.restore, null);
       expect(operatorList.totalLength).toEqual(2);

+ 165 - 165
lib/test/unit/function_spec.js

@@ -33,7 +33,7 @@ describe("function", function () {
       toMatchArray(util, customEqualityTesters) {
         return {
           compare(actual, expected) {
-            var result = {};
+            const result = {};
 
             if (actual.length !== expected.length) {
               result.pass = false;
@@ -43,9 +43,9 @@ describe("function", function () {
 
             result.pass = true;
 
-            for (var i = 0; i < expected.length; i++) {
-              var a = actual[i],
-                  b = expected[i];
+            for (let i = 0; i < expected.length; i++) {
+              const a = actual[i],
+                    b = expected[i];
 
               if (Array.isArray(b)) {
                 if (a.length !== b.length) {
@@ -53,9 +53,9 @@ describe("function", function () {
                   break;
                 }
 
-                for (var j = 0; j < a.length; j++) {
-                  var suba = a[j],
-                      subb = b[j];
+                for (let j = 0; j < a.length; j++) {
+                  const suba = a[j],
+                        subb = b[j];
 
                   if (suba !== subb) {
                     result.pass = false;
@@ -80,46 +80,46 @@ describe("function", function () {
   });
   describe("PostScriptParser", function () {
     function parse(program) {
-      var stream = new _stream.StringStream(program);
-      var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
+      const stream = new _stream.StringStream(program);
+      const parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
       return parser.parse();
     }
 
     it("parses empty programs", function () {
-      var output = parse("{}");
+      const output = parse("{}");
       expect(output.length).toEqual(0);
     });
     it("parses positive numbers", function () {
-      var number = 999;
-      var program = parse("{ " + number + " }");
-      var expectedProgram = [number];
+      const number = 999;
+      const program = parse("{ " + number + " }");
+      const expectedProgram = [number];
       expect(program).toMatchArray(expectedProgram);
     });
     it("parses negative numbers", function () {
-      var number = -999;
-      var program = parse("{ " + number + " }");
-      var expectedProgram = [number];
+      const number = -999;
+      const program = parse("{ " + number + " }");
+      const expectedProgram = [number];
       expect(program).toMatchArray(expectedProgram);
     });
     it("parses negative floats", function () {
-      var number = 3.3;
-      var program = parse("{ " + number + " }");
-      var expectedProgram = [number];
+      const number = 3.3;
+      const program = parse("{ " + number + " }");
+      const expectedProgram = [number];
       expect(program).toMatchArray(expectedProgram);
     });
     it("parses operators", function () {
-      var program = parse("{ sub }");
-      var expectedProgram = ["sub"];
+      const program = parse("{ sub }");
+      const expectedProgram = ["sub"];
       expect(program).toMatchArray(expectedProgram);
     });
     it("parses if statements", function () {
-      var program = parse("{ { 99 } if }");
-      var expectedProgram = [3, "jz", 99];
+      const program = parse("{ { 99 } if }");
+      const expectedProgram = [3, "jz", 99];
       expect(program).toMatchArray(expectedProgram);
     });
     it("parses ifelse statements", function () {
-      var program = parse("{ { 99 } { 44 } ifelse }");
-      var expectedProgram = [5, "jz", 99, 6, "j", 44];
+      const program = parse("{ { 99 } { 44 } ifelse }");
+      const expectedProgram = [5, "jz", 99, 6, "j", 44];
       expect(program).toMatchArray(expectedProgram);
     });
     it("handles missing brackets", function () {
@@ -128,346 +128,346 @@ describe("function", function () {
       }).toThrow(new Error("Unexpected symbol: found undefined expected 1."));
     });
     it("handles junk after the end", function () {
-      var number = 3.3;
-      var program = parse("{ " + number + " }#");
-      var expectedProgram = [number];
+      const number = 3.3;
+      const program = parse("{ " + number + " }#");
+      const expectedProgram = [number];
       expect(program).toMatchArray(expectedProgram);
     });
   });
   describe("PostScriptEvaluator", function () {
     function evaluate(program) {
-      var stream = new _stream.StringStream(program);
-      var parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
-      var code = parser.parse();
-      var evaluator = new _function.PostScriptEvaluator(code);
-      var output = evaluator.execute();
+      const stream = new _stream.StringStream(program);
+      const parser = new _ps_parser.PostScriptParser(new _ps_parser.PostScriptLexer(stream));
+      const code = parser.parse();
+      const evaluator = new _function.PostScriptEvaluator(code);
+      const output = evaluator.execute();
       return output;
     }
 
     it("pushes stack", function () {
-      var stack = evaluate("{ 99 }");
-      var expectedStack = [99];
+      const stack = evaluate("{ 99 }");
+      const expectedStack = [99];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles if with true", function () {
-      var stack = evaluate("{ 1 {99} if }");
-      var expectedStack = [99];
+      const stack = evaluate("{ 1 {99} if }");
+      const expectedStack = [99];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles if with false", function () {
-      var stack = evaluate("{ 0 {99} if }");
-      var expectedStack = [];
+      const stack = evaluate("{ 0 {99} if }");
+      const expectedStack = [];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles ifelse with true", function () {
-      var stack = evaluate("{ 1 {99} {77} ifelse }");
-      var expectedStack = [99];
+      const stack = evaluate("{ 1 {99} {77} ifelse }");
+      const expectedStack = [99];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles ifelse with false", function () {
-      var stack = evaluate("{ 0 {99} {77} ifelse }");
-      var expectedStack = [77];
+      const stack = evaluate("{ 0 {99} {77} ifelse }");
+      const expectedStack = [77];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles nested if", function () {
-      var stack = evaluate("{ 1 {1 {77} if} if }");
-      var expectedStack = [77];
+      const stack = evaluate("{ 1 {1 {77} if} if }");
+      const expectedStack = [77];
       expect(stack).toMatchArray(expectedStack);
     });
     it("abs", function () {
-      var stack = evaluate("{ -2 abs }");
-      var expectedStack = [2];
+      const stack = evaluate("{ -2 abs }");
+      const expectedStack = [2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("adds", function () {
-      var stack = evaluate("{ 1 2 add }");
-      var expectedStack = [3];
+      const stack = evaluate("{ 1 2 add }");
+      const expectedStack = [3];
       expect(stack).toMatchArray(expectedStack);
     });
     it("boolean and", function () {
-      var stack = evaluate("{ true false and }");
-      var expectedStack = [false];
+      const stack = evaluate("{ true false and }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("bitwise and", function () {
-      var stack = evaluate("{ 254 1 and }");
-      var expectedStack = [254 & 1];
+      const stack = evaluate("{ 254 1 and }");
+      const expectedStack = [254 & 1];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the inverse tangent of a number", function () {
-      var stack = evaluate("{ 90 atan }");
-      var expectedStack = [Math.atan(90)];
+      const stack = evaluate("{ 90 atan }");
+      const expectedStack = [Math.atan(90)];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles bitshifting ", function () {
-      var stack = evaluate("{ 50 2 bitshift }");
-      var expectedStack = [200];
+      const stack = evaluate("{ 50 2 bitshift }");
+      const expectedStack = [200];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the ceiling value", function () {
-      var stack = evaluate("{ 9.9 ceiling }");
-      var expectedStack = [10];
+      const stack = evaluate("{ 9.9 ceiling }");
+      const expectedStack = [10];
       expect(stack).toMatchArray(expectedStack);
     });
     it("copies", function () {
-      var stack = evaluate("{ 99 98 2 copy }");
-      var expectedStack = [99, 98, 99, 98];
+      const stack = evaluate("{ 99 98 2 copy }");
+      const expectedStack = [99, 98, 99, 98];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the cosine of a number", function () {
-      var stack = evaluate("{ 90 cos }");
-      var expectedStack = [Math.cos(90)];
+      const stack = evaluate("{ 90 cos }");
+      const expectedStack = [Math.cos(90)];
       expect(stack).toMatchArray(expectedStack);
     });
     it("converts to int", function () {
-      var stack = evaluate("{ 9.9 cvi }");
-      var expectedStack = [9];
+      const stack = evaluate("{ 9.9 cvi }");
+      const expectedStack = [9];
       expect(stack).toMatchArray(expectedStack);
     });
     it("converts negatives to int", function () {
-      var stack = evaluate("{ -9.9 cvi }");
-      var expectedStack = [-9];
+      const stack = evaluate("{ -9.9 cvi }");
+      const expectedStack = [-9];
       expect(stack).toMatchArray(expectedStack);
     });
     it("converts to real", function () {
-      var stack = evaluate("{ 55.34 cvr }");
-      var expectedStack = [55.34];
+      const stack = evaluate("{ 55.34 cvr }");
+      const expectedStack = [55.34];
       expect(stack).toMatchArray(expectedStack);
     });
     it("divides", function () {
-      var stack = evaluate("{ 6 5 div }");
-      var expectedStack = [1.2];
+      const stack = evaluate("{ 6 5 div }");
+      const expectedStack = [1.2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("maps division by zero to infinity", function () {
-      var stack = evaluate("{ 6 0 div }");
-      var expectedStack = [Infinity];
+      const stack = evaluate("{ 6 0 div }");
+      const expectedStack = [Infinity];
       expect(stack).toMatchArray(expectedStack);
     });
     it("duplicates", function () {
-      var stack = evaluate("{ 99 dup }");
-      var expectedStack = [99, 99];
+      const stack = evaluate("{ 99 dup }");
+      const expectedStack = [99, 99];
       expect(stack).toMatchArray(expectedStack);
     });
     it("accepts an equality", function () {
-      var stack = evaluate("{ 9 9 eq }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 9 9 eq }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects an inequality", function () {
-      var stack = evaluate("{ 9 8 eq }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 9 8 eq }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("exchanges", function () {
-      var stack = evaluate("{ 44 99 exch }");
-      var expectedStack = [99, 44];
+      const stack = evaluate("{ 44 99 exch }");
+      const expectedStack = [99, 44];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles exponentiation", function () {
-      var stack = evaluate("{ 10 2 exp }");
-      var expectedStack = [100];
+      const stack = evaluate("{ 10 2 exp }");
+      const expectedStack = [100];
       expect(stack).toMatchArray(expectedStack);
     });
     it("pushes false onto the stack", function () {
-      var stack = evaluate("{ false }");
-      var expectedStack = [false];
+      const stack = evaluate("{ false }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the floor value", function () {
-      var stack = evaluate("{ 9.9 floor }");
-      var expectedStack = [9];
+      const stack = evaluate("{ 9.9 floor }");
+      const expectedStack = [9];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles greater than or equal to", function () {
-      var stack = evaluate("{ 10 9 ge }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 10 9 ge }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects less than for greater than or equal to", function () {
-      var stack = evaluate("{ 8 9 ge }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 8 9 ge }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles greater than", function () {
-      var stack = evaluate("{ 10 9 gt }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 10 9 gt }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects less than or equal for greater than", function () {
-      var stack = evaluate("{ 9 9 gt }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 9 9 gt }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("divides to integer", function () {
-      var stack = evaluate("{ 2 3 idiv }");
-      var expectedStack = [0];
+      const stack = evaluate("{ 2 3 idiv }");
+      const expectedStack = [0];
       expect(stack).toMatchArray(expectedStack);
     });
     it("divides to negative integer", function () {
-      var stack = evaluate("{ -2 3 idiv }");
-      var expectedStack = [0];
+      const stack = evaluate("{ -2 3 idiv }");
+      const expectedStack = [0];
       expect(stack).toMatchArray(expectedStack);
     });
     it("duplicates index", function () {
-      var stack = evaluate("{ 4 3 2 1 2 index }");
-      var expectedStack = [4, 3, 2, 1, 3];
+      const stack = evaluate("{ 4 3 2 1 2 index }");
+      const expectedStack = [4, 3, 2, 1, 3];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles less than or equal to", function () {
-      var stack = evaluate("{ 9 10 le }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 9 10 le }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects greater than for less than or equal to", function () {
-      var stack = evaluate("{ 10 9 le }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 10 9 le }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the natural logarithm", function () {
-      var stack = evaluate("{ 10 ln }");
-      var expectedStack = [Math.log(10)];
+      const stack = evaluate("{ 10 ln }");
+      const expectedStack = [Math.log(10)];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the base 10 logarithm", function () {
-      var stack = evaluate("{ 100 log }");
-      var expectedStack = [2];
+      const stack = evaluate("{ 100 log }");
+      const expectedStack = [2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("handles less than", function () {
-      var stack = evaluate("{ 9 10 lt }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 9 10 lt }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects greater than or equal to for less than", function () {
-      var stack = evaluate("{ 10 9 lt }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 10 9 lt }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("performs the modulo operation", function () {
-      var stack = evaluate("{ 4 3 mod }");
-      var expectedStack = [1];
+      const stack = evaluate("{ 4 3 mod }");
+      const expectedStack = [1];
       expect(stack).toMatchArray(expectedStack);
     });
     it("multiplies two numbers (positive result)", function () {
-      var stack = evaluate("{ 9 8 mul }");
-      var expectedStack = [72];
+      const stack = evaluate("{ 9 8 mul }");
+      const expectedStack = [72];
       expect(stack).toMatchArray(expectedStack);
     });
     it("multiplies two numbers (negative result)", function () {
-      var stack = evaluate("{ 9 -8 mul }");
-      var expectedStack = [-72];
+      const stack = evaluate("{ 9 -8 mul }");
+      const expectedStack = [-72];
       expect(stack).toMatchArray(expectedStack);
     });
     it("accepts an inequality", function () {
-      var stack = evaluate("{ 9 8 ne }");
-      var expectedStack = [true];
+      const stack = evaluate("{ 9 8 ne }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rejects an equality", function () {
-      var stack = evaluate("{ 9 9 ne }");
-      var expectedStack = [false];
+      const stack = evaluate("{ 9 9 ne }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("negates", function () {
-      var stack = evaluate("{ 4.5 neg }");
-      var expectedStack = [-4.5];
+      const stack = evaluate("{ 4.5 neg }");
+      const expectedStack = [-4.5];
       expect(stack).toMatchArray(expectedStack);
     });
     it("boolean not", function () {
-      var stack = evaluate("{ true not }");
-      var expectedStack = [false];
+      const stack = evaluate("{ true not }");
+      const expectedStack = [false];
       expect(stack).toMatchArray(expectedStack);
     });
     it("bitwise not", function () {
-      var stack = evaluate("{ 12 not }");
-      var expectedStack = [-13];
+      const stack = evaluate("{ 12 not }");
+      const expectedStack = [-13];
       expect(stack).toMatchArray(expectedStack);
     });
     it("boolean or", function () {
-      var stack = evaluate("{ true false or }");
-      var expectedStack = [true];
+      const stack = evaluate("{ true false or }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("bitwise or", function () {
-      var stack = evaluate("{ 254 1 or }");
-      var expectedStack = [254 | 1];
+      const stack = evaluate("{ 254 1 or }");
+      const expectedStack = [254 | 1];
       expect(stack).toMatchArray(expectedStack);
     });
     it("pops stack", function () {
-      var stack = evaluate("{ 1 2 pop }");
-      var expectedStack = [1];
+      const stack = evaluate("{ 1 2 pop }");
+      const expectedStack = [1];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rolls stack right", function () {
-      var stack = evaluate("{ 1 3 2 2 4 1 roll }");
-      var expectedStack = [2, 1, 3, 2];
+      const stack = evaluate("{ 1 3 2 2 4 1 roll }");
+      const expectedStack = [2, 1, 3, 2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rolls stack left", function () {
-      var stack = evaluate("{ 1 3 2 2 4 -1 roll }");
-      var expectedStack = [3, 2, 2, 1];
+      const stack = evaluate("{ 1 3 2 2 4 -1 roll }");
+      const expectedStack = [3, 2, 2, 1];
       expect(stack).toMatchArray(expectedStack);
     });
     it("rounds a number", function () {
-      var stack = evaluate("{ 9.52 round }");
-      var expectedStack = [10];
+      const stack = evaluate("{ 9.52 round }");
+      const expectedStack = [10];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates the sine of a number", function () {
-      var stack = evaluate("{ 90 sin }");
-      var expectedStack = [Math.sin(90)];
+      const stack = evaluate("{ 90 sin }");
+      const expectedStack = [Math.sin(90)];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates a square root (integer)", function () {
-      var stack = evaluate("{ 100 sqrt }");
-      var expectedStack = [10];
+      const stack = evaluate("{ 100 sqrt }");
+      const expectedStack = [10];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates a square root (float)", function () {
-      var stack = evaluate("{ 99 sqrt }");
-      var expectedStack = [Math.sqrt(99)];
+      const stack = evaluate("{ 99 sqrt }");
+      const expectedStack = [Math.sqrt(99)];
       expect(stack).toMatchArray(expectedStack);
     });
     it("subtracts (positive result)", function () {
-      var stack = evaluate("{ 6 4 sub }");
-      var expectedStack = [2];
+      const stack = evaluate("{ 6 4 sub }");
+      const expectedStack = [2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("subtracts (negative result)", function () {
-      var stack = evaluate("{ 4 6 sub }");
-      var expectedStack = [-2];
+      const stack = evaluate("{ 4 6 sub }");
+      const expectedStack = [-2];
       expect(stack).toMatchArray(expectedStack);
     });
     it("pushes true onto the stack", function () {
-      var stack = evaluate("{ true }");
-      var expectedStack = [true];
+      const stack = evaluate("{ true }");
+      const expectedStack = [true];
       expect(stack).toMatchArray(expectedStack);
     });
     it("truncates a number", function () {
-      var stack = evaluate("{ 35.004 truncate }");
-      var expectedStack = [35];
+      const stack = evaluate("{ 35.004 truncate }");
+      const expectedStack = [35];
       expect(stack).toMatchArray(expectedStack);
     });
     it("calculates an exclusive or value", function () {
-      var stack = evaluate("{ 3 9 xor }");
-      var expectedStack = [10];
+      const stack = evaluate("{ 3 9 xor }");
+      const expectedStack = [10];
       expect(stack).toMatchArray(expectedStack);
     });
   });
   describe("PostScriptCompiler", function () {
     function check(code, domain, range, samples) {
-      var compiler = new _function.PostScriptCompiler();
-      var compiledCode = compiler.compile(code, domain, range);
+      const compiler = new _function.PostScriptCompiler();
+      const compiledCode = compiler.compile(code, domain, range);
 
       if (samples === null) {
         expect(compiledCode).toBeNull();
       } else {
         expect(compiledCode).not.toBeNull();
-        var fn = new Function("src", "srcOffset", "dest", "destOffset", compiledCode);
+        const fn = new Function("src", "srcOffset", "dest", "destOffset", compiledCode);
 
-        for (var i = 0; i < samples.length; i++) {
-          var out = new Float32Array(samples[i].output.length);
+        for (let i = 0; i < samples.length; i++) {
+          const out = new Float32Array(samples[i].output.length);
           fn(samples[i].input, 0, out, 0);
           expect(Array.prototype.slice.call(out, 0)).toMatchArray(samples[i].output);
         }
@@ -636,9 +636,9 @@ describe("function", function () {
       }]);
     });
     it("compile optimized", function () {
-      var compiler = new _function.PostScriptCompiler();
-      var code = [0, "add", 1, 1, 3, -1, "roll", "sub", "sub", 1, "mul"];
-      var compiledCode = compiler.compile(code, [0, 1], [0, 1]);
+      const compiler = new _function.PostScriptCompiler();
+      const code = [0, "add", 1, 1, 3, -1, "roll", "sub", "sub", 1, "mul"];
+      const compiledCode = compiler.compile(code, [0, 1], [0, 1]);
       expect(compiledCode).toEqual("dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));");
     });
   });

+ 52 - 53
lib/test/unit/jasmine-boot.js

@@ -21,73 +21,71 @@
  */
 "use strict";
 
-function initializePDFJS(callback) {
-  Promise.all(["pdfjs/display/api.js", "pdfjs/display/worker_options.js", "pdfjs/display/network.js", "pdfjs/display/fetch_stream.js", "pdfjs/shared/is_node.js", "pdfjs-test/unit/annotation_spec.js", "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", "pdfjs-test/unit/core_utils_spec.js", "pdfjs-test/unit/crypto_spec.js", "pdfjs-test/unit/custom_spec.js", "pdfjs-test/unit/display_svg_spec.js", "pdfjs-test/unit/display_utils_spec.js", "pdfjs-test/unit/document_spec.js", "pdfjs-test/unit/encodings_spec.js", "pdfjs-test/unit/evaluator_spec.js", "pdfjs-test/unit/function_spec.js", "pdfjs-test/unit/fetch_stream_spec.js", "pdfjs-test/unit/message_handler_spec.js", "pdfjs-test/unit/metadata_spec.js", "pdfjs-test/unit/murmurhash3_spec.js", "pdfjs-test/unit/network_spec.js", "pdfjs-test/unit/network_utils_spec.js", "pdfjs-test/unit/parser_spec.js", "pdfjs-test/unit/pdf_find_controller_spec.js", "pdfjs-test/unit/pdf_find_utils_spec.js", "pdfjs-test/unit/pdf_history_spec.js", "pdfjs-test/unit/primitives_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/type1_parser_spec.js", "pdfjs-test/unit/ui_utils_spec.js", "pdfjs-test/unit/unicode_spec.js", "pdfjs-test/unit/util_spec.js", "pdfjs-test/unit/writer_spec.js"].map(function (moduleName) {
-    return SystemJS.import(moduleName);
-  })).then(function (modules) {
-    const displayApi = modules[0];
-    const {
-      GlobalWorkerOptions
-    } = modules[1];
-    const {
-      PDFNetworkStream
-    } = modules[2];
-    const {
-      PDFFetchStream
-    } = modules[3];
-    const {
-      isNodeJS
-    } = modules[4];
-
-    if (isNodeJS) {
-      throw new Error("The `gulp unittest` command cannot be used in Node.js environments.");
-    }
+var _worker_options = require("pdfjs/display/worker_options.js");
 
-    if (typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream !== "undefined") {
-      displayApi.setPDFNetworkStreamFactory(function (params) {
-        return new PDFFetchStream(params);
-      });
-    } else {
-      displayApi.setPDFNetworkStreamFactory(function (params) {
-        return new PDFNetworkStream(params);
-      });
-    }
+var _is_node = require("pdfjs/shared/is_node.js");
 
-    GlobalWorkerOptions.workerSrc = "../../build/generic/build/pdf.worker.js";
-    callback();
-  });
+var _fetch_stream = require("pdfjs/display/fetch_stream.js");
+
+var _network = require("pdfjs/display/network.js");
+
+var _api = require("pdfjs/display/api.js");
+
+var _testreporter = require("./testreporter.js");
+
+async function initializePDFJS(callback) {
+  await Promise.all(["pdfjs-test/unit/annotation_spec.js", "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", "pdfjs-test/unit/core_utils_spec.js", "pdfjs-test/unit/crypto_spec.js", "pdfjs-test/unit/custom_spec.js", "pdfjs-test/unit/default_appearance_spec.js", "pdfjs-test/unit/display_svg_spec.js", "pdfjs-test/unit/display_utils_spec.js", "pdfjs-test/unit/document_spec.js", "pdfjs-test/unit/encodings_spec.js", "pdfjs-test/unit/evaluator_spec.js", "pdfjs-test/unit/function_spec.js", "pdfjs-test/unit/fetch_stream_spec.js", "pdfjs-test/unit/message_handler_spec.js", "pdfjs-test/unit/metadata_spec.js", "pdfjs-test/unit/murmurhash3_spec.js", "pdfjs-test/unit/network_spec.js", "pdfjs-test/unit/network_utils_spec.js", "pdfjs-test/unit/parser_spec.js", "pdfjs-test/unit/pdf_find_controller_spec.js", "pdfjs-test/unit/pdf_find_utils_spec.js", "pdfjs-test/unit/pdf_history_spec.js", "pdfjs-test/unit/primitives_spec.js", "pdfjs-test/unit/scripting_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/type1_parser_spec.js", "pdfjs-test/unit/ui_utils_spec.js", "pdfjs-test/unit/unicode_spec.js", "pdfjs-test/unit/util_spec.js", "pdfjs-test/unit/writer_spec.js", "pdfjs-test/unit/xml_spec.js"].map(function (moduleName) {
+    return import(moduleName);
+  }));
+
+  if (_is_node.isNodeJS) {
+    throw new Error("The `gulp unittest` command cannot be used in Node.js environments.");
+  }
+
+  if (typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream !== "undefined") {
+    (0, _api.setPDFNetworkStreamFactory)(function (params) {
+      return new _fetch_stream.PDFFetchStream(params);
+    });
+  } else {
+    (0, _api.setPDFNetworkStreamFactory)(function (params) {
+      return new _network.PDFNetworkStream(params);
+    });
+  }
+
+  _worker_options.GlobalWorkerOptions.workerSrc = "../../build/generic/build/pdf.worker.js";
+  callback();
 }
 
 (function () {
   window.jasmine = jasmineRequire.core(jasmineRequire);
   jasmineRequire.html(jasmine);
-  var env = jasmine.getEnv();
-  var jasmineInterface = jasmineRequire.interface(jasmine, env);
+  const env = jasmine.getEnv();
+  const jasmineInterface = jasmineRequire.interface(jasmine, env);
   extend(window, jasmineInterface);
-  var queryString = new jasmine.QueryString({
+  const queryString = new jasmine.QueryString({
     getWindowLocation() {
       return window.location;
     }
 
   });
-  var config = {
+  const config = {
     failFast: queryString.getParam("failFast"),
     oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"),
     hideDisabled: queryString.getParam("hideDisabled")
   };
-  var random = queryString.getParam("random");
+  const random = queryString.getParam("random");
 
   if (random !== undefined && random !== "") {
     config.random = random;
   }
 
-  var seed = queryString.getParam("seed");
+  const seed = queryString.getParam("seed");
 
   if (seed) {
     config.seed = seed;
   }
 
-  var htmlReporter = new jasmine.HtmlReporter({
+  const htmlReporter = new jasmine.HtmlReporter({
     env,
 
     navigateWithNewParam(key, value) {
@@ -115,11 +113,11 @@ function initializePDFJS(callback) {
   env.addReporter(htmlReporter);
 
   if (queryString.getParam("browser")) {
-    var testReporter = new TestReporter(queryString.getParam("browser"));
+    const testReporter = new _testreporter.TestReporter(queryString.getParam("browser"));
     env.addReporter(testReporter);
   }
 
-  var specFilter = new jasmine.HtmlSpecFilter({
+  const specFilter = new jasmine.HtmlSpecFilter({
     filterString() {
       return queryString.getParam("spec");
     }
@@ -132,24 +130,25 @@ function initializePDFJS(callback) {
 
   env.configure(config);
   jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
-  var currentWindowOnload = window.onload;
 
-  window.onload = function () {
-    if (currentWindowOnload) {
-      currentWindowOnload();
+  function extend(destination, source) {
+    for (const property in source) {
+      destination[property] = source[property];
     }
 
+    return destination;
+  }
+
+  function unitTestInit() {
     initializePDFJS(function () {
       htmlReporter.initialize();
       env.execute();
     });
-  };
-
-  function extend(destination, source) {
-    for (var property in source) {
-      destination[property] = source[property];
-    }
+  }
 
-    return destination;
+  if (document.readyState === "interactive" || document.readyState === "complete") {
+    unitTestInit();
+  } else {
+    document.addEventListener("DOMContentLoaded", unitTestInit, true);
   }
 })();

+ 2 - 2
lib/test/unit/metadata_spec.js

@@ -56,7 +56,7 @@ describe("metadata", function () {
     expect(metadata.get("dc:title")).toEqual("L'Odissee thématique logo Odisséé - décembre 2008.pub");
     expect(metadata.get("dc:qux")).toEqual(null);
     expect(metadata.getAll()).toEqual({
-      "dc:creator": "ODIS",
+      "dc:creator": ["ODIS"],
       "dc:title": "L'Odissee thématique logo Odisséé - décembre 2008.pub",
       "xap:creatortool": "PDFCreator Version 0.9.6"
     });
@@ -74,7 +74,7 @@ describe("metadata", function () {
     expect(metadata.get("dc:title")).toEqual("");
     expect(metadata.get("dc:qux")).toEqual(null);
     expect(metadata.getAll()).toEqual({
-      "dc:creator": "",
+      "dc:creator": [""],
       "dc:description": "",
       "dc:format": "application/pdf",
       "dc:subject": "",

+ 29 - 16
lib/test/unit/murmurhash3_spec.js

@@ -25,47 +25,60 @@ var _murmurhash = require("../../core/murmurhash3.js");
 
 describe("MurmurHash3_64", function () {
   it("instantiates without seed", function () {
-    var hash = new _murmurhash.MurmurHash3_64();
+    const hash = new _murmurhash.MurmurHash3_64();
     expect(hash).toEqual(jasmine.any(_murmurhash.MurmurHash3_64));
   });
   it("instantiates with seed", function () {
-    var hash = new _murmurhash.MurmurHash3_64(1);
+    const hash = new _murmurhash.MurmurHash3_64(1);
     expect(hash).toEqual(jasmine.any(_murmurhash.MurmurHash3_64));
   });
-  var hexDigestExpected = "f61cfdbfdae0f65e";
-  var sourceText = "test";
-  var sourceCharCodes = [116, 101, 115, 116];
+  const hexDigestExpected = "f61cfdbfdae0f65e";
+  const sourceText = "test";
+  const sourceCharCodes = [116, 101, 115, 116];
   it("correctly generates a hash from a string", function () {
-    var hash = new _murmurhash.MurmurHash3_64();
+    const hash = new _murmurhash.MurmurHash3_64();
     hash.update(sourceText);
     expect(hash.hexdigest()).toEqual(hexDigestExpected);
   });
   it("correctly generates a hash from a Uint8Array", function () {
-    var hash = new _murmurhash.MurmurHash3_64();
+    const hash = new _murmurhash.MurmurHash3_64();
     hash.update(new Uint8Array(sourceCharCodes));
     expect(hash.hexdigest()).toEqual(hexDigestExpected);
   });
   it("correctly generates a hash from a Uint32Array", function () {
-    var hash = new _murmurhash.MurmurHash3_64();
+    const hash = new _murmurhash.MurmurHash3_64();
     hash.update(new Uint32Array(new Uint8Array(sourceCharCodes).buffer));
     expect(hash.hexdigest()).toEqual(hexDigestExpected);
   });
   it("changes the hash after update without seed", function () {
-    var hash = new _murmurhash.MurmurHash3_64();
-    var hexdigest1, hexdigest2;
+    const hash = new _murmurhash.MurmurHash3_64();
     hash.update(sourceText);
-    hexdigest1 = hash.hexdigest();
+    const hexdigest1 = hash.hexdigest();
     hash.update(sourceText);
-    hexdigest2 = hash.hexdigest();
+    const hexdigest2 = hash.hexdigest();
     expect(hexdigest1).not.toEqual(hexdigest2);
   });
   it("changes the hash after update with seed", function () {
-    var hash = new _murmurhash.MurmurHash3_64(1);
-    var hexdigest1, hexdigest2;
+    const hash = new _murmurhash.MurmurHash3_64(1);
     hash.update(sourceText);
-    hexdigest1 = hash.hexdigest();
+    const hexdigest1 = hash.hexdigest();
     hash.update(sourceText);
-    hexdigest2 = hash.hexdigest();
+    const hexdigest2 = hash.hexdigest();
     expect(hexdigest1).not.toEqual(hexdigest2);
   });
+  it("generates correct hashes for TypedArrays which share the same " + "underlying ArrayBuffer (issue 12533)", function () {
+    const typedArray = new Uint8Array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
+    const startArray = new Uint8Array(typedArray.buffer, 0, 10);
+    const endArray = new Uint8Array(typedArray.buffer, 10, 10);
+    expect(startArray).not.toEqual(endArray);
+    const startHash = new _murmurhash.MurmurHash3_64();
+    startHash.update(startArray);
+    const startHexdigest = startHash.hexdigest();
+    const endHash = new _murmurhash.MurmurHash3_64();
+    endHash.update(endArray);
+    const endHexdigest = endHash.hexdigest();
+    expect(startHexdigest).not.toEqual(endHexdigest);
+    expect(startHexdigest).toEqual("a49de339cc5b0819");
+    expect(endHexdigest).toEqual("f81a92d9e214ab35");
+  });
 });

+ 21 - 21
lib/test/unit/network_spec.js

@@ -26,25 +26,25 @@ var _util = require("../../shared/util.js");
 var _network = require("../../display/network.js");
 
 describe("network", function () {
-  var pdf1 = new URL("../pdfs/tracemonkey.pdf", window.location).href;
-  var pdf1Length = 1016315;
+  const pdf1 = new URL("../pdfs/tracemonkey.pdf", window.location).href;
+  const pdf1Length = 1016315;
   it("read without stream and range", function (done) {
-    var stream = new _network.PDFNetworkStream({
+    const stream = new _network.PDFNetworkStream({
       url: pdf1,
       rangeChunkSize: 65536,
       disableStream: true,
       disableRange: true
     });
-    var fullReader = stream.getFullReader();
-    var isStreamingSupported, isRangeSupported;
-    var promise = fullReader.headersReady.then(function () {
+    const fullReader = stream.getFullReader();
+    let isStreamingSupported, isRangeSupported;
+    const promise = fullReader.headersReady.then(function () {
       isStreamingSupported = fullReader.isStreamingSupported;
       isRangeSupported = fullReader.isRangeSupported;
     });
-    var len = 0,
+    let len = 0,
         count = 0;
 
-    var read = function () {
+    const read = function () {
       return fullReader.read().then(function (result) {
         if (result.done) {
           return undefined;
@@ -56,7 +56,7 @@ describe("network", function () {
       });
     };
 
-    var readPromise = Promise.all([read(), promise]);
+    const readPromise = Promise.all([read(), promise]);
     readPromise.then(function (page) {
       expect(len).toEqual(pdf1Length);
       expect(count).toEqual(1);
@@ -68,33 +68,33 @@ describe("network", function () {
     });
   });
   it("read custom ranges", function (done) {
-    var rangeSize = 32768;
-    var stream = new _network.PDFNetworkStream({
+    const rangeSize = 32768;
+    const stream = new _network.PDFNetworkStream({
       url: pdf1,
       length: pdf1Length,
       rangeChunkSize: rangeSize,
       disableStream: true,
       disableRange: false
     });
-    var fullReader = stream.getFullReader();
-    var isStreamingSupported, isRangeSupported, fullReaderCancelled;
-    var promise = fullReader.headersReady.then(function () {
+    const fullReader = stream.getFullReader();
+    let isStreamingSupported, isRangeSupported, fullReaderCancelled;
+    const promise = fullReader.headersReady.then(function () {
       isStreamingSupported = fullReader.isStreamingSupported;
       isRangeSupported = fullReader.isRangeSupported;
       fullReader.cancel(new _util.AbortException("Don't need fullReader."));
       fullReaderCancelled = true;
     });
-    var tailSize = pdf1Length % rangeSize || rangeSize;
-    var range1Reader = stream.getRangeReader(pdf1Length - tailSize - rangeSize, pdf1Length - tailSize);
-    var range2Reader = stream.getRangeReader(pdf1Length - tailSize, pdf1Length);
-    var result1 = {
+    const tailSize = pdf1Length % rangeSize || rangeSize;
+    const range1Reader = stream.getRangeReader(pdf1Length - tailSize - rangeSize, pdf1Length - tailSize);
+    const range2Reader = stream.getRangeReader(pdf1Length - tailSize, pdf1Length);
+    const result1 = {
       value: 0
     },
-        result2 = {
+          result2 = {
       value: 0
     };
 
-    var read = function (reader, lenResult) {
+    const read = function (reader, lenResult) {
       return reader.read().then(function (result) {
         if (result.done) {
           return undefined;
@@ -105,7 +105,7 @@ describe("network", function () {
       });
     };
 
-    var readPromises = Promise.all([read(range1Reader, result1), read(range2Reader, result2), promise]);
+    const readPromises = Promise.all([read(range1Reader, result1), read(range2Reader, result2), promise]);
     readPromises.then(function () {
       expect(result1.value).toEqual(rangeSize);
       expect(result2.value).toEqual(tailSize);

+ 131 - 69
lib/test/unit/pdf_find_controller_spec.js

@@ -31,6 +31,8 @@ var _pdf_find_controller = require("../../web/pdf_find_controller.js");
 
 var _pdf_link_service = require("../../web/pdf_link_service.js");
 
+const tracemonkeyFileName = "tracemonkey.pdf";
+
 class MockLinkService extends _pdf_link_service.SimpleLinkService {
   constructor() {
     super();
@@ -56,68 +58,80 @@ class MockLinkService extends _pdf_link_service.SimpleLinkService {
 
 }
 
-describe("pdf_find_controller", function () {
-  let eventBus;
-  let pdfFindController;
-  beforeEach(function (done) {
-    const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("tracemonkey.pdf"));
-    loadingTask.promise.then(function (pdfDocument) {
-      eventBus = new _ui_utils.EventBus();
-      const linkService = new MockLinkService();
-      linkService.setDocument(pdfDocument);
-      pdfFindController = new _pdf_find_controller.PDFFindController({
-        linkService,
-        eventBus
-      });
-      pdfFindController.setDocument(pdfDocument);
-      done();
-    });
-  });
-  afterEach(function () {
-    eventBus = null;
-    pdfFindController = null;
+async function initPdfFindController(filename) {
+  const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename || tracemonkeyFileName));
+  const pdfDocument = await loadingTask.promise;
+  const eventBus = new _ui_utils.EventBus();
+  const linkService = new MockLinkService();
+  linkService.setDocument(pdfDocument);
+  const pdfFindController = new _pdf_find_controller.PDFFindController({
+    linkService,
+    eventBus
   });
+  pdfFindController.setDocument(pdfDocument);
+  return {
+    eventBus,
+    pdfFindController
+  };
+}
 
-  function testSearch({
-    parameters,
-    matchesPerPage,
-    selectedMatch
-  }) {
-    return new Promise(function (resolve) {
-      pdfFindController.executeCommand("find", parameters);
-      let totalPages = matchesPerPage.length;
-
-      for (let i = totalPages - 1; i >= 0; i--) {
-        if (matchesPerPage[i] > 0) {
-          totalPages = i + 1;
-          break;
-        }
+function testSearch({
+  eventBus,
+  pdfFindController,
+  parameters,
+  matchesPerPage,
+  selectedMatch,
+  pageMatches = null,
+  pageMatchesLength = null
+}) {
+  return new Promise(function (resolve) {
+    pdfFindController.executeCommand("find", parameters);
+    let totalPages = matchesPerPage.length;
+
+    for (let i = totalPages - 1; i >= 0; i--) {
+      if (matchesPerPage[i] > 0) {
+        totalPages = i + 1;
+        break;
       }
+    }
 
-      const totalMatches = matchesPerPage.reduce((a, b) => {
-        return a + b;
-      });
-      eventBus.on("updatefindmatchescount", function onUpdateFindMatchesCount(evt) {
-        if (pdfFindController.pageMatches.length !== totalPages) {
-          return;
-        }
-
-        eventBus.off("updatefindmatchescount", onUpdateFindMatchesCount);
-        expect(evt.matchesCount.total).toBe(totalMatches);
-
-        for (let i = 0; i < totalPages; i++) {
-          expect(pdfFindController.pageMatches[i].length).toEqual(matchesPerPage[i]);
-        }
-
-        expect(pdfFindController.selected.pageIdx).toEqual(selectedMatch.pageIndex);
-        expect(pdfFindController.selected.matchIdx).toEqual(selectedMatch.matchIndex);
-        resolve();
-      });
+    const totalMatches = matchesPerPage.reduce((a, b) => {
+      return a + b;
     });
-  }
+    eventBus.on("updatefindmatchescount", function onUpdateFindMatchesCount(evt) {
+      if (pdfFindController.pageMatches.length !== totalPages) {
+        return;
+      }
+
+      eventBus.off("updatefindmatchescount", onUpdateFindMatchesCount);
+      expect(evt.matchesCount.total).toBe(totalMatches);
+
+      for (let i = 0; i < totalPages; i++) {
+        expect(pdfFindController.pageMatches[i].length).toEqual(matchesPerPage[i]);
+      }
+
+      expect(pdfFindController.selected.pageIdx).toEqual(selectedMatch.pageIndex);
+      expect(pdfFindController.selected.matchIdx).toEqual(selectedMatch.matchIndex);
+
+      if (pageMatches) {
+        expect(pdfFindController.pageMatches).toEqual(pageMatches);
+        expect(pdfFindController.pageMatchesLength).toEqual(pageMatchesLength);
+      }
+
+      resolve();
+    });
+  });
+}
 
-  it("performs a normal search", function (done) {
-    testSearch({
+describe("pdf_find_controller", function () {
+  it("performs a normal search", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController();
+    await testSearch({
+      eventBus,
+      pdfFindController,
       parameters: {
         query: "Dynamic",
         caseSensitive: false,
@@ -130,10 +144,16 @@ describe("pdf_find_controller", function () {
         pageIndex: 0,
         matchIndex: 0
       }
-    }).then(done);
+    });
   });
-  it("performs a normal search and finds the previous result", function (done) {
-    testSearch({
+  it("performs a normal search and finds the previous result", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController();
+    await testSearch({
+      eventBus,
+      pdfFindController,
       parameters: {
         query: "conference",
         caseSensitive: false,
@@ -146,10 +166,16 @@ describe("pdf_find_controller", function () {
         pageIndex: 13,
         matchIndex: 4
       }
-    }).then(done);
+    });
   });
-  it("performs a case sensitive search", function (done) {
-    testSearch({
+  it("performs a case sensitive search", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController();
+    await testSearch({
+      eventBus,
+      pdfFindController,
       parameters: {
         query: "Dynamic",
         caseSensitive: true,
@@ -162,10 +188,16 @@ describe("pdf_find_controller", function () {
         pageIndex: 0,
         matchIndex: 0
       }
-    }).then(done);
+    });
   });
-  it("performs an entire word search", function (done) {
-    testSearch({
+  it("performs an entire word search", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController();
+    await testSearch({
+      eventBus,
+      pdfFindController,
       parameters: {
         query: "Government",
         caseSensitive: false,
@@ -178,10 +210,16 @@ describe("pdf_find_controller", function () {
         pageIndex: 12,
         matchIndex: 0
       }
-    }).then(done);
+    });
   });
-  it("performs a multiple term (no phrase) search", function (done) {
-    testSearch({
+  it("performs a multiple term (no phrase) search", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController();
+    await testSearch({
+      eventBus,
+      pdfFindController,
       parameters: {
         query: "alternate solution",
         caseSensitive: false,
@@ -194,6 +232,30 @@ describe("pdf_find_controller", function () {
         pageIndex: 5,
         matchIndex: 0
       }
-    }).then(done);
+    });
+  });
+  it("performs a normal search, where the text is normalized", async function () {
+    const {
+      eventBus,
+      pdfFindController
+    } = await initPdfFindController("fraction-highlight.pdf");
+    await testSearch({
+      eventBus,
+      pdfFindController,
+      parameters: {
+        query: "fraction",
+        caseSensitive: false,
+        entireWord: false,
+        phraseSearch: true,
+        findPrevious: false
+      },
+      matchesPerPage: [3],
+      selectedMatch: {
+        pageIndex: 0,
+        matchIndex: 0
+      },
+      pageMatches: [[19, 48, 66]],
+      pageMatchesLength: [[8, 8, 8]]
+    });
   });
 });

+ 10 - 10
lib/test/unit/pdf_find_utils_spec.js

@@ -29,10 +29,10 @@ describe("pdf_find_utils", function () {
       const characters = {
         A: _pdf_find_utils.CharacterType.ALPHA_LETTER,
         a: _pdf_find_utils.CharacterType.ALPHA_LETTER,
-        "0": _pdf_find_utils.CharacterType.ALPHA_LETTER,
-        "5": _pdf_find_utils.CharacterType.ALPHA_LETTER,
-        Ä: _pdf_find_utils.CharacterType.ALPHA_LETTER,
-        ä: _pdf_find_utils.CharacterType.ALPHA_LETTER,
+        0: _pdf_find_utils.CharacterType.ALPHA_LETTER,
+        5: _pdf_find_utils.CharacterType.ALPHA_LETTER,
+        "\xC4": _pdf_find_utils.CharacterType.ALPHA_LETTER,
+        "\xE4": _pdf_find_utils.CharacterType.ALPHA_LETTER,
         _: _pdf_find_utils.CharacterType.ALPHA_LETTER,
         " ": _pdf_find_utils.CharacterType.SPACE,
         "\t": _pdf_find_utils.CharacterType.SPACE,
@@ -45,12 +45,12 @@ describe("pdf_find_utils", function () {
         ";": _pdf_find_utils.CharacterType.PUNCT,
         ":": _pdf_find_utils.CharacterType.PUNCT,
         "\u2122": _pdf_find_utils.CharacterType.ALPHA_LETTER,
-        : _pdf_find_utils.CharacterType.THAI_LETTER,
-        䀀: _pdf_find_utils.CharacterType.HAN_LETTER,
-        : _pdf_find_utils.CharacterType.HAN_LETTER,
-        : _pdf_find_utils.CharacterType.KATAKANA_LETTER,
-        : _pdf_find_utils.CharacterType.HIRAGANA_LETTER,
-        : _pdf_find_utils.CharacterType.HALFWIDTH_KATAKANA_LETTER
+        "\u0E25": _pdf_find_utils.CharacterType.THAI_LETTER,
+        "\u4000": _pdf_find_utils.CharacterType.HAN_LETTER,
+        "\uF950": _pdf_find_utils.CharacterType.HAN_LETTER,
+        "\u30C0": _pdf_find_utils.CharacterType.KATAKANA_LETTER,
+        "\u3050": _pdf_find_utils.CharacterType.HIRAGANA_LETTER,
+        "\uFF80": _pdf_find_utils.CharacterType.HALFWIDTH_KATAKANA_LETTER
       };
 
       for (const character in characters) {

+ 1104 - 0
lib/test/unit/scripting_spec.js

@@ -0,0 +1,1104 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2020 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.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+var _display_utils = require("../../display/display_utils.js");
+
+const sandboxBundleSrc = "../../build/generic/build/pdf.sandbox.js";
+describe("Scripting", function () {
+  let sandbox, send_queue, test_id, ref;
+
+  function getId() {
+    const id = `${ref++}R`;
+    return id;
+  }
+
+  function myeval(code) {
+    const key = (test_id++).toString();
+    return sandbox.eval(code, key).then(() => {
+      const result = send_queue.get(key).result;
+      send_queue.delete(key);
+      return result;
+    });
+  }
+
+  beforeAll(function (done) {
+    test_id = 0;
+    ref = 1;
+    send_queue = new Map();
+
+    window.dispatchEvent = event => {
+      if (event.detail.command) {
+        send_queue.set(event.detail.command, event.detail);
+      } else if (send_queue.has(event.detail.id)) {
+        const prev = send_queue.get(event.detail.id);
+        Object.assign(prev, event.detail);
+      } else {
+        send_queue.set(event.detail.id, event.detail);
+      }
+    };
+
+    window.alert = value => {
+      const command = "alert";
+      send_queue.set(command, {
+        command,
+        value
+      });
+    };
+
+    const promise = (0, _display_utils.loadScript)(sandboxBundleSrc).then(() => {
+      return window.pdfjsSandbox.QuickJSSandbox();
+    });
+    sandbox = {
+      createSandbox(data) {
+        promise.then(sbx => sbx.create(data));
+      },
+
+      dispatchEventInSandbox(data) {
+        return promise.then(sbx => sbx.dispatchEvent(data));
+      },
+
+      nukeSandbox() {
+        promise.then(sbx => sbx.nukeSandbox());
+      },
+
+      eval(code, key) {
+        return promise.then(sbx => sbx.evalForTesting(code, key));
+      }
+
+    };
+    done();
+  });
+  afterAll(function () {
+    sandbox.nukeSandbox();
+    sandbox = null;
+    send_queue = null;
+  });
+  describe("Sandbox", function () {
+    it("should send a value, execute an action and get back a new value", function (done) {
+      function compute(n) {
+        let s = 0;
+
+        for (let i = 0; i < n; i++) {
+          s += i;
+        }
+
+        return s;
+      }
+
+      const number = 123;
+      const expected = ((number - 1) * number / 2).toString();
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {
+              Keystroke: [`${compute.toString()}event.value = compute(parseInt(event.value));`]
+            },
+            type: "text"
+          }]
+        },
+        calculationOrder: [],
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        }
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId,
+        value: `${number}`,
+        name: "Keystroke",
+        willCommit: true
+      }).then(() => {
+        expect(send_queue.has(refId)).toEqual(true);
+        expect(send_queue.get(refId)).toEqual({
+          id: refId,
+          valueAsString: expected
+        });
+        done();
+      }).catch(done.fail);
+    });
+  });
+  describe("Doc", function () {
+    it("should treat globalThis as the doc", async function (done) {
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {},
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: [],
+        dispatchEventName: "_dispatchMe"
+      };
+      sandbox.createSandbox(data);
+
+      try {
+        await myeval(`(this.foobar = 123456, 0)`);
+        await myeval(`this.getField("field").doc.foobar`).then(value => {
+          expect(value).toEqual(123456);
+        });
+        done();
+      } catch (ex) {
+        done.fail(ex);
+      }
+    });
+  });
+  describe("Util", function () {
+    beforeAll(function (done) {
+      sandbox.createSandbox({
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        objects: {},
+        calculationOrder: []
+      });
+      done();
+    });
+    describe("printd", function () {
+      it("should print a date according to a format", function (done) {
+        const date = `new Date("Sun Apr 15 2007 03:14:15")`;
+        Promise.all([myeval(`util.printd(0, ${date})`).then(value => {
+          expect(value).toEqual("D:20070415031415");
+        }), myeval(`util.printd(1, ${date})`).then(value => {
+          expect(value).toEqual("2007.04.15 03:14:15");
+        }), myeval(`util.printd(2, ${date})`).then(value => {
+          expect(value).toEqual("4/15/07 3:14:15 am");
+        }), myeval(`util.printd("mmmm mmm mm m", ${date})`).then(value => {
+          expect(value).toEqual("April Apr 04 4");
+        }), myeval(`util.printd("dddd ddd dd d", ${date})`).then(value => {
+          expect(value).toEqual("Sunday Sun 15 15");
+        })]).then(() => done());
+      });
+    });
+    describe("scand", function () {
+      it("should parse a date according to a format", function (done) {
+        const date = new Date("Sun Apr 15 2007 03:14:15");
+        Promise.all([myeval(`util.scand(0, "D:20070415031415").toString()`).then(value => {
+          expect(new Date(value)).toEqual(date);
+        }), myeval(`util.scand(1, "2007.04.15 03:14:15").toString()`).then(value => {
+          expect(new Date(value)).toEqual(date);
+        }), myeval(`util.scand(2, "4/15/07 3:14:15 am").toString()`).then(value => {
+          expect(new Date(value)).toEqual(date);
+        })]).then(() => done());
+      });
+    });
+    describe("printf", function () {
+      it("should print some data according to a format", function (done) {
+        Promise.all([myeval(`util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)`).then(value => {
+          expect(value).toEqual("Integer numbers: 1, 56,...");
+        }), myeval(`util.printf("Hex numbers: %x, %x,...", 1234, 56789)`).then(value => {
+          expect(value).toEqual("Hex numbers: 4D2, DDD5,...");
+        }), myeval(`util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)`).then(value => {
+          expect(value).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
+        }), myeval(`util.printf("Decimal number: %,0+.3f", 1234567.89123)`).then(value => {
+          expect(value).toEqual("Decimal number: +1,234,567.891");
+        }), myeval(`util.printf("Decimal number: %,0+8.3f", 1.234567)`).then(value => {
+          expect(value).toEqual("Decimal number: +  1.235");
+        }), myeval(`util.printf("Decimal number: %,0.2f", -12.34567)`).then(value => {
+          expect(value).toEqual("Decimal number: -12.35");
+        })]).then(() => done());
+      });
+      it("should print a string with no argument", function (done) {
+        myeval(`util.printf("hello world")`).then(value => {
+          expect(value).toEqual("hello world");
+        }).then(() => done());
+      });
+      it("print a string with a percent", function (done) {
+        myeval(`util.printf("%%s")`).then(value => {
+          expect(value).toEqual("%%s");
+        }).then(() => done());
+      });
+    });
+    describe("printx", function () {
+      it("should print some data according to a format", function (done) {
+        myeval(`util.printx("9 (999) 999-9999", "aaa14159697489zzz")`).then(value => {
+          expect(value).toEqual("1 (415) 969-7489");
+        }).then(() => done());
+      });
+    });
+  });
+  describe("Events", function () {
+    it("should trigger an event and modify the source", function (done) {
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {
+              test: [`event.source.value = "123";`]
+            },
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: []
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId,
+        value: "",
+        name: "test",
+        willCommit: true
+      }).then(() => {
+        expect(send_queue.has(refId)).toEqual(true);
+        expect(send_queue.get(refId)).toEqual({
+          id: refId,
+          value: "123"
+        });
+        done();
+      }).catch(done.fail);
+    });
+    it("should trigger a Keystroke event and invalidate it", function (done) {
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {
+              Keystroke: [`event.rc = false;`]
+            },
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: []
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId,
+        value: "hell",
+        name: "Keystroke",
+        willCommit: false,
+        change: "o",
+        selStart: 4,
+        selEnd: 4
+      }).then(() => {
+        expect(send_queue.has(refId)).toEqual(true);
+        expect(send_queue.get(refId)).toEqual({
+          id: refId,
+          value: "hell",
+          selRange: [4, 4]
+        });
+        done();
+      }).catch(done.fail);
+    });
+    it("should trigger a Keystroke event and change it", function (done) {
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {
+              Keystroke: [`event.change = "a";`]
+            },
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: []
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId,
+        value: "hell",
+        name: "Keystroke",
+        willCommit: false,
+        change: "o",
+        selStart: 4,
+        selEnd: 4
+      }).then(() => {
+        expect(send_queue.has(refId)).toEqual(true);
+        expect(send_queue.get(refId)).toEqual({
+          id: refId,
+          value: "hella"
+        });
+        done();
+      }).catch(done.fail);
+    });
+    it("should trigger an invalid commit Keystroke event", function (done) {
+      const refId = getId();
+      const data = {
+        objects: {
+          field: [{
+            id: refId,
+            value: "",
+            actions: {
+              test: [`event.rc = false;`]
+            },
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: []
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId,
+        value: "",
+        name: "test",
+        willCommit: true
+      }).then(() => {
+        expect(send_queue.has(refId)).toEqual(false);
+        done();
+      }).catch(done.fail);
+    });
+    it("should trigger a valid commit Keystroke event", function (done) {
+      const refId1 = getId();
+      const refId2 = getId();
+      const data = {
+        objects: {
+          field1: [{
+            id: refId1,
+            value: "",
+            actions: {
+              Validate: [`event.value = "world";`]
+            },
+            type: "text"
+          }],
+          field2: [{
+            id: refId2,
+            value: "",
+            actions: {
+              Calculate: [`event.value = "hello";`]
+            },
+            type: "text"
+          }]
+        },
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        calculationOrder: [refId2]
+      };
+      sandbox.createSandbox(data);
+      sandbox.dispatchEventInSandbox({
+        id: refId1,
+        value: "hello",
+        name: "Keystroke",
+        willCommit: true
+      }).then(() => {
+        expect(send_queue.has(refId1)).toEqual(true);
+        expect(send_queue.get(refId1)).toEqual({
+          id: refId1,
+          value: "world",
+          valueAsString: "world"
+        });
+        done();
+      }).catch(done.fail);
+    });
+  });
+  describe("Color", function () {
+    beforeAll(function (done) {
+      sandbox.createSandbox({
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        objects: {},
+        calculationOrder: []
+      });
+      done();
+    });
+
+    function round(color) {
+      return [color[0], ...color.slice(1).map(x => Math.round(x * 1000) / 1000)];
+    }
+
+    it("should convert RGB color for different color spaces", function (done) {
+      Promise.all([myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "T")`).then(value => {
+        expect(round(value)).toEqual(["T"]);
+      }), myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "G")`).then(value => {
+        expect(round(value)).toEqual(["G", 0.181]);
+      }), myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "RGB")`).then(value => {
+        expect(round(value)).toEqual(["RGB", 0.1, 0.2, 0.3]);
+      }), myeval(`color.convert(["RGB", 0.1, 0.2, 0.3], "CMYK")`).then(value => {
+        expect(round(value)).toEqual(["CMYK", 0.9, 0.8, 0.7, 0.7]);
+      })]).then(() => done());
+    });
+    it("should convert CMYK color for different color spaces", function (done) {
+      Promise.all([myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "T")`).then(value => {
+        expect(round(value)).toEqual(["T"]);
+      }), myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "G")`).then(value => {
+        expect(round(value)).toEqual(["G", 0.371]);
+      }), myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "RGB")`).then(value => {
+        expect(round(value)).toEqual(["RGB", 0.5, 0.3, 0.4]);
+      }), myeval(`color.convert(["CMYK", 0.1, 0.2, 0.3, 0.4], "CMYK")`).then(value => {
+        expect(round(value)).toEqual(["CMYK", 0.1, 0.2, 0.3, 0.4]);
+      })]).then(() => done());
+    });
+    it("should convert Gray color for different color spaces", function (done) {
+      Promise.all([myeval(`color.convert(["G", 0.1], "T")`).then(value => {
+        expect(round(value)).toEqual(["T"]);
+      }), myeval(`color.convert(["G", 0.1], "G")`).then(value => {
+        expect(round(value)).toEqual(["G", 0.1]);
+      }), myeval(`color.convert(["G", 0.1], "RGB")`).then(value => {
+        expect(round(value)).toEqual(["RGB", 0.1, 0.1, 0.1]);
+      }), myeval(`color.convert(["G", 0.1], "CMYK")`).then(value => {
+        expect(round(value)).toEqual(["CMYK", 0, 0, 0, 0.9]);
+      })]).then(() => done());
+    });
+    it("should convert Transparent color for different color spaces", function (done) {
+      Promise.all([myeval(`color.convert(["T"], "T")`).then(value => {
+        expect(round(value)).toEqual(["T"]);
+      }), myeval(`color.convert(["T"], "G")`).then(value => {
+        expect(round(value)).toEqual(["G", 0]);
+      }), myeval(`color.convert(["T"], "RGB")`).then(value => {
+        expect(round(value)).toEqual(["RGB", 0, 0, 0]);
+      }), myeval(`color.convert(["T"], "CMYK")`).then(value => {
+        expect(round(value)).toEqual(["CMYK", 0, 0, 0, 1]);
+      })]).then(() => done());
+    });
+  });
+  describe("App", function () {
+    beforeAll(function (done) {
+      sandbox.createSandbox({
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        objects: {},
+        calculationOrder: []
+      });
+      done();
+    });
+    it("should test language", function (done) {
+      Promise.all([myeval(`app.language`).then(value => {
+        expect(value).toEqual("ENU");
+      }), myeval(`app.language = "hello"`).then(value => {
+        expect(value).toEqual("app.language is read-only");
+      })]).then(() => done());
+    });
+    it("should test platform", function (done) {
+      Promise.all([myeval(`app.platform`).then(value => {
+        expect(value).toEqual("UNIX");
+      }), myeval(`app.platform = "hello"`).then(value => {
+        expect(value).toEqual("app.platform is read-only");
+      })]).then(() => done());
+    });
+  });
+  describe("AForm", function () {
+    beforeAll(function (done) {
+      sandbox.createSandbox({
+        appInfo: {
+          language: "en-US",
+          platform: "Linux x86_64"
+        },
+        objects: {},
+        calculationOrder: [],
+        dispatchEventName: "_dispatchMe"
+      });
+      done();
+    });
+    describe("AFExtractNums", function () {
+      it("should extract numbers", function (done) {
+        Promise.all([myeval(`AFExtractNums("123 456 789")`).then(value => {
+          expect(value).toEqual(["123", "456", "789"]);
+        }), myeval(`AFExtractNums("123.456")`).then(value => {
+          expect(value).toEqual(["123", "456"]);
+        }), myeval(`AFExtractNums("123")`).then(value => {
+          expect(value).toEqual(["123"]);
+        }), myeval(`AFExtractNums(".123")`).then(value => {
+          expect(value).toEqual(["0", "123"]);
+        }), myeval(`AFExtractNums(",123")`).then(value => {
+          expect(value).toEqual(["0", "123"]);
+        })]).then(() => done());
+      });
+    });
+    describe("AFMakeNumber", function () {
+      it("should convert string to number", function (done) {
+        Promise.all([myeval(`AFMakeNumber("123.456")`).then(value => {
+          expect(value).toEqual(123.456);
+        }), myeval(`AFMakeNumber(123.456)`).then(value => {
+          expect(value).toEqual(123.456);
+        }), myeval(`AFMakeNumber("-123.456")`).then(value => {
+          expect(value).toEqual(-123.456);
+        }), myeval(`AFMakeNumber("-123,456")`).then(value => {
+          expect(value).toEqual(-123.456);
+        }), myeval(`AFMakeNumber("not a number")`).then(value => {
+          expect(value).toEqual(null);
+        })]).then(() => done());
+      });
+    });
+    describe("AFMakeArrayFromList", function () {
+      it("should split a string into an array of strings", async function (done) {
+        const value = await myeval(`AFMakeArrayFromList("aaaa,  bbbbbbb,cc,ddd, e")`);
+        expect(value).toEqual(["aaaa", " bbbbbbb", "cc", "ddd", "e"]);
+        done();
+      });
+    });
+    describe("AFNumber_format", function () {
+      it("should format a number", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                test1: [`AFNumber_Format(2, 0, 0, 0, "€", false);` + `event.source.value = event.value;`],
+                test2: [`AFNumber_Format(1, 3, 0, 0, "$", true);` + `event.source.value = event.value;`],
+                test3: [`AFNumber_Format(2, 0, 1, 0, "€", false);` + `event.source.value = event.value;`],
+                test4: [`AFNumber_Format(2, 0, 2, 0, "€", false);` + `event.source.value = event.value;`],
+                test5: [`AFNumber_Format(2, 0, 3, 0, "€", false);` + `event.source.value = event.value;`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "123456.789",
+            name: "test1"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "123,456.79€"
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "223456.789",
+            name: "test2"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "$223456,8"
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "-323456.789",
+            name: "test3"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "323,456.79€",
+            textColor: ["RGB", 1, 0, 0]
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "-423456.789",
+            name: "test4"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "(423,456.79€)"
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "-52345.678",
+            name: "test5"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "(52,345.68€)",
+            textColor: ["RGB", 1, 0, 0]
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("AFNumber_Keystroke", function () {
+      it("should validate a number on a keystroke event", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                Validate: [`AFNumber_Keystroke(null, 0, null, null, null, null);`]
+              },
+              type: "text",
+              name: "MyField"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "123456.789",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "123456.789",
+            valueAsString: "123456.789"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+      it("should not validate a number on a keystroke event", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                Validate: [`AFNumber_Keystroke(null, 0, null, null, null, null);`]
+              },
+              type: "text",
+              name: "MyField"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "123s456.789",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has("alert")).toEqual(true);
+          expect(send_queue.get("alert")).toEqual({
+            command: "alert",
+            value: "The value entered does not match the format of the field [ MyField ]"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("AFPercent_Format", function () {
+      it("should format a percentage", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                test1: [`AFPercent_Format(2, 1, false);` + `event.source.value = event.value;`],
+                test2: [`AFPercent_Format(2, 1, true);` + `event.source.value = event.value;`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "0.456789",
+            name: "test1"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "45.68%"
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "0.456789",
+            name: "test2"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "%45.68"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("AFDate_Format", function () {
+      it("should format a date", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                test1: [`AFDate_Format(0);event.source.value = event.value;`],
+                test2: [`AFDate_Format(12);event.source.value = event.value;`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "Sun Apr 15 2007 03:14:15",
+            name: "test1"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "4/15"
+          });
+          send_queue.delete(refId);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "Sun Apr 15 2007 03:14:15",
+            name: "test2"
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "4/15/07 3:14 am"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("AFRange_Validate", function () {
+      it("should validate a number in range [a, b]", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                Validate: [`AFRange_Validate(true, 123, true, 456);`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "321",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "321",
+            valueAsString: "321"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+      it("should invalidate a number out of range [a, b]", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                Validate: [`AFRange_Validate(true, 123, true, 456);`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "12",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has("alert")).toEqual(true);
+          expect(send_queue.get("alert")).toEqual({
+            command: "alert",
+            value: "Invalid value: must be greater than or equal to 123 and less than or equal to 456."
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("ASSimple_Calculate", function () {
+      it("should compute the sum of several fields", async function (done) {
+        const refIds = [0, 1, 2, 3].map(_ => getId());
+        const data = {
+          objects: {
+            field1: [{
+              id: refIds[0],
+              value: "",
+              actions: {},
+              type: "text"
+            }],
+            field2: [{
+              id: refIds[1],
+              value: "",
+              actions: {},
+              type: "text"
+            }],
+            field3: [{
+              id: refIds[2],
+              value: "",
+              actions: {},
+              type: "text"
+            }],
+            field4: [{
+              id: refIds[3],
+              value: "",
+              actions: {
+                Calculate: [`AFSimple_Calculate("SUM", ["field1", "field2", "field3"]);`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [refIds[3]],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refIds[0],
+            value: "1",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has(refIds[3])).toEqual(true);
+          expect(send_queue.get(refIds[3])).toEqual({
+            id: refIds[3],
+            value: 1,
+            valueAsString: "1"
+          });
+          await sandbox.dispatchEventInSandbox({
+            id: refIds[1],
+            value: "2",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has(refIds[3])).toEqual(true);
+          expect(send_queue.get(refIds[3])).toEqual({
+            id: refIds[3],
+            value: 3,
+            valueAsString: "3"
+          });
+          await sandbox.dispatchEventInSandbox({
+            id: refIds[2],
+            value: "3",
+            name: "Keystroke",
+            willCommit: true
+          });
+          expect(send_queue.has(refIds[3])).toEqual(true);
+          expect(send_queue.get(refIds[3])).toEqual({
+            id: refIds[3],
+            value: 6,
+            valueAsString: "6"
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("AFSpecial_KeystrokeEx", function () {
+      it("should validate a phone number on a keystroke event", async function (done) {
+        const refId = getId();
+        const data = {
+          objects: {
+            field: [{
+              id: refId,
+              value: "",
+              actions: {
+                Keystroke: [`AFSpecial_KeystrokeEx("9AXO");`]
+              },
+              type: "text"
+            }]
+          },
+          appInfo: {
+            language: "en-US",
+            platform: "Linux x86_64"
+          },
+          calculationOrder: [],
+          dispatchEventName: "_dispatchMe"
+        };
+
+        try {
+          sandbox.createSandbox(data);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "",
+            change: "3",
+            name: "Keystroke",
+            willCommit: false,
+            selStart: 0,
+            selEnd: 0
+          });
+          expect(send_queue.has(refId)).toEqual(false);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "3",
+            change: "F",
+            name: "Keystroke",
+            willCommit: false,
+            selStart: 1,
+            selEnd: 1
+          });
+          expect(send_queue.has(refId)).toEqual(false);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "3F",
+            change: "?",
+            name: "Keystroke",
+            willCommit: false,
+            selStart: 2,
+            selEnd: 2
+          });
+          expect(send_queue.has(refId)).toEqual(false);
+          await sandbox.dispatchEventInSandbox({
+            id: refId,
+            value: "3F?",
+            change: "@",
+            name: "Keystroke",
+            willCommit: false,
+            selStart: 3,
+            selEnd: 3
+          });
+          expect(send_queue.has(refId)).toEqual(true);
+          expect(send_queue.get(refId)).toEqual({
+            id: refId,
+            value: "3F?",
+            selRange: [3, 3]
+          });
+          done();
+        } catch (ex) {
+          done.fail(ex);
+        }
+      });
+    });
+    describe("eMailValidate", function () {
+      it("should validate an e-mail address", function (done) {
+        Promise.all([myeval(`eMailValidate(123)`).then(value => {
+          expect(value).toEqual(false);
+        }), myeval(`eMailValidate("foo@bar.com")`).then(value => {
+          expect(value).toEqual(true);
+        }), myeval(`eMailValidate("foo bar")`).then(value => {
+          expect(value).toEqual(false);
+        })]).then(() => done());
+      });
+    });
+  });
+});

+ 8 - 8
lib/test/unit/stream_spec.js

@@ -31,7 +31,7 @@ describe("stream", function () {
       toMatchTypedArray(util, customEqualityTesters) {
         return {
           compare(actual, expected) {
-            var result = {};
+            const result = {};
 
             if (actual.length !== expected.length) {
               result.pass = false;
@@ -41,9 +41,9 @@ describe("stream", function () {
 
             result.pass = true;
 
-            for (var i = 0, ii = expected.length; i < ii; i++) {
-              var a = actual[i],
-                  b = expected[i];
+            for (let i = 0, ii = expected.length; i < ii; i++) {
+              const a = actual[i],
+                    b = expected[i];
 
               if (a !== b) {
                 result.pass = false;
@@ -61,14 +61,14 @@ describe("stream", function () {
   });
   describe("PredictorStream", function () {
     it("should decode simple predictor data", function () {
-      var dict = new _primitives.Dict();
+      const dict = new _primitives.Dict();
       dict.set("Predictor", 12);
       dict.set("Colors", 1);
       dict.set("BitsPerComponent", 8);
       dict.set("Columns", 2);
-      var input = new _stream.Stream(new Uint8Array([2, 100, 3, 2, 1, 255, 2, 1, 255]), 0, 9, dict);
-      var predictor = new _stream.PredictorStream(input, 9, dict);
-      var result = predictor.getBytes(6);
+      const input = new _stream.Stream(new Uint8Array([2, 100, 3, 2, 1, 255, 2, 1, 255]), 0, 9, dict);
+      const predictor = new _stream.PredictorStream(input, 9, dict);
+      const result = predictor.getBytes(6);
       expect(result).toMatchTypedArray(new Uint8Array([100, 3, 101, 2, 102, 1]));
       predictor.reset();
       const clampedResult = predictor.getBytes(6, true);

+ 16 - 19
lib/test/unit/test_utils.js

@@ -27,7 +27,7 @@ Object.defineProperty(exports, "__esModule", {
 exports.buildGetDocumentParams = buildGetDocumentParams;
 exports.createIdFactory = createIdFactory;
 exports.isEmptyObj = isEmptyObj;
-exports.TEST_PDFS_PATH = exports.XRefMock = exports.NodeFileReaderFactory = exports.DOMFileReaderFactory = void 0;
+exports.XRefMock = exports.TEST_PDFS_PATH = exports.DefaultFileReaderFactory = exports.CMAP_PARAMS = void 0;
 
 var _primitives = require("../../core/primitives.js");
 
@@ -39,6 +39,14 @@ var _is_node = require("../../shared/is_node.js");
 
 var _stream = require("../../core/stream.js");
 
+const TEST_PDFS_PATH = _is_node.isNodeJS ? "./test/pdfs/" : "../pdfs/";
+exports.TEST_PDFS_PATH = TEST_PDFS_PATH;
+const CMAP_PARAMS = {
+  cMapUrl: _is_node.isNodeJS ? "./external/bcmaps/" : "../../external/bcmaps/",
+  cMapPacked: true
+};
+exports.CMAP_PARAMS = CMAP_PARAMS;
+
 class DOMFileReaderFactory {
   static async fetch(params) {
     const response = await fetch(params.path);
@@ -52,8 +60,6 @@ class DOMFileReaderFactory {
 
 }
 
-exports.DOMFileReaderFactory = DOMFileReaderFactory;
-
 class NodeFileReaderFactory {
   static async fetch(params) {
     const fs = require("fs");
@@ -72,21 +78,12 @@ class NodeFileReaderFactory {
 
 }
 
-exports.NodeFileReaderFactory = NodeFileReaderFactory;
-const TEST_PDFS_PATH = {
-  dom: "../pdfs/",
-  node: "./test/pdfs/"
-};
-exports.TEST_PDFS_PATH = TEST_PDFS_PATH;
+const DefaultFileReaderFactory = _is_node.isNodeJS ? NodeFileReaderFactory : DOMFileReaderFactory;
+exports.DefaultFileReaderFactory = DefaultFileReaderFactory;
 
 function buildGetDocumentParams(filename, options) {
   const params = Object.create(null);
-
-  if (_is_node.isNodeJS) {
-    params.url = TEST_PDFS_PATH.node + filename;
-  } else {
-    params.url = new URL(TEST_PDFS_PATH.dom + filename, window.location).href;
-  }
+  params.url = _is_node.isNodeJS ? TEST_PDFS_PATH + filename : new URL(TEST_PDFS_PATH + filename, window.location).href;
 
   for (const option in options) {
     params[option] = options[option];
@@ -126,8 +123,8 @@ class XRefMock {
     return this._map[ref.toString()];
   }
 
-  fetchAsync(ref) {
-    return Promise.resolve(this.fetch(ref));
+  async fetchAsync(ref) {
+    return this.fetch(ref);
   }
 
   fetchIfRef(obj) {
@@ -138,8 +135,8 @@ class XRefMock {
     return this.fetch(obj);
   }
 
-  fetchIfRefAsync(obj) {
-    return Promise.resolve(this.fetchIfRef(obj));
+  async fetchIfRefAsync(obj) {
+    return this.fetchIfRef(obj);
   }
 
 }

+ 11 - 4
lib/test/unit/testreporter.js

@@ -21,9 +21,14 @@
  */
 "use strict";
 
-var TestReporter = function (browser) {
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.TestReporter = void 0;
+
+const TestReporter = function (browser) {
   function send(action, json, cb) {
-    var r = new XMLHttpRequest();
+    const r = new XMLHttpRequest();
     r.open("POST", action, true);
     r.setRequestHeader("Content-Type", "application/json");
 
@@ -50,7 +55,7 @@ var TestReporter = function (browser) {
   }
 
   function sendResult(status, description, error) {
-    var message = {
+    const message = {
       status,
       description
     };
@@ -110,4 +115,6 @@ var TestReporter = function (browser) {
   this.jasmineDone = function () {
     setTimeout(sendQuitRequest, 500);
   };
-};
+};
+
+exports.TestReporter = TestReporter;

+ 23 - 23
lib/test/unit/type1_parser_spec.js

@@ -29,8 +29,8 @@ var _type1_parser = require("../../core/type1_parser.js");
 
 describe("Type1Parser", function () {
   it("splits tokens", function () {
-    var stream = new _stream.StringStream("/BlueValues[-17 0]noaccess def");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("/BlueValues[-17 0]noaccess def");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.getToken()).toEqual("/");
     expect(parser.getToken()).toEqual("BlueValues");
     expect(parser.getToken()).toEqual("[");
@@ -42,60 +42,60 @@ describe("Type1Parser", function () {
     expect(parser.getToken()).toEqual(null);
   });
   it("handles glued tokens", function () {
-    var stream = new _stream.StringStream("dup/CharStrings");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("dup/CharStrings");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.getToken()).toEqual("dup");
     expect(parser.getToken()).toEqual("/");
     expect(parser.getToken()).toEqual("CharStrings");
   });
   it("ignores whitespace", function () {
-    var stream = new _stream.StringStream("\nab   c\t");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("\nab   c\t");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.getToken()).toEqual("ab");
     expect(parser.getToken()).toEqual("c");
   });
   it("parses numbers", function () {
-    var stream = new _stream.StringStream("123");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("123");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.readNumber()).toEqual(123);
   });
   it("parses booleans", function () {
-    var stream = new _stream.StringStream("true false");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("true false");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.readBoolean()).toEqual(1);
     expect(parser.readBoolean()).toEqual(0);
   });
   it("parses number arrays", function () {
-    var stream = new _stream.StringStream("[1 2]");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    let stream = new _stream.StringStream("[1 2]");
+    let parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.readNumberArray()).toEqual([1, 2]);
     stream = new _stream.StringStream("[ 1 2 ]");
     parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.readNumberArray()).toEqual([1, 2]);
   });
   it("skips comments", function () {
-    var stream = new _stream.StringStream("%!PS-AdobeFont-1.0: CMSY10 003.002\n" + "%%Title: CMSY10\n" + "%Version: 003.002\n" + "FontDirectory");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const stream = new _stream.StringStream("%!PS-AdobeFont-1.0: CMSY10 003.002\n" + "%%Title: CMSY10\n" + "%Version: 003.002\n" + "FontDirectory");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
     expect(parser.getToken()).toEqual("FontDirectory");
   });
   it("parses font program", function () {
-    var stream = new _stream.StringStream("/ExpansionFactor  99\n" + "/Subrs 1 array\n" + "dup 0 1 RD x noaccess put\n" + "end\n" + "/CharStrings 46 dict dup begin\n" + "/.notdef 1 RD x ND\n" + "end");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
-    var program = parser.extractFontProgram({});
+    const stream = new _stream.StringStream("/ExpansionFactor  99\n" + "/Subrs 1 array\n" + "dup 0 1 RD x noaccess put\n" + "end\n" + "/CharStrings 46 dict dup begin\n" + "/.notdef 1 RD x ND\n" + "end");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const program = parser.extractFontProgram({});
     expect(program.charstrings.length).toEqual(1);
     expect(program.properties.privateData.ExpansionFactor).toEqual(99);
   });
   it("parses font header font matrix", function () {
-    var stream = new _stream.StringStream("/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
-    var props = {};
+    const stream = new _stream.StringStream("/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const props = {};
     parser.extractFontHeader(props);
     expect(props.fontMatrix).toEqual([0.001, 0, 0, 0.001, 0, 0]);
   });
   it("parses font header encoding", function () {
-    var stream = new _stream.StringStream("/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n" + "dup 33 /arrowright put\n" + "readonly def\n");
-    var parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
-    var props = {
+    const stream = new _stream.StringStream("/Encoding 256 array\n" + "0 1 255 {1 index exch /.notdef put} for\n" + "dup 33 /arrowright put\n" + "readonly def\n");
+    const parser = new _type1_parser.Type1Parser(stream, false, _fonts.SEAC_ANALYSIS_ENABLED);
+    const props = {
       overridableEncoding: true
     };
     parser.extractFontHeader(props);

+ 78 - 35
lib/test/unit/ui_utils_spec.js

@@ -91,9 +91,9 @@ describe("ui_utils", function () {
       expect((0, _ui_utils.getPDFFileNameFromURL)("http://www.example.com/pdfs/file2.pdf#file.pdf")).toEqual("file2.pdf");
     });
     it("gets PDF filename from URI-encoded data", function () {
-      var encodedUrl = encodeURIComponent("http://www.example.com/pdfs/file1.pdf");
+      const encodedUrl = encodeURIComponent("http://www.example.com/pdfs/file1.pdf");
       expect((0, _ui_utils.getPDFFileNameFromURL)(encodedUrl)).toEqual("file1.pdf");
-      var encodedUrlWithQuery = encodeURIComponent("http://www.example.com/pdfs/file.txt?file2.pdf");
+      const encodedUrlWithQuery = encodeURIComponent("http://www.example.com/pdfs/file.txt?file2.pdf");
       expect((0, _ui_utils.getPDFFileNameFromURL)(encodedUrlWithQuery)).toEqual("file2.pdf");
     });
     it("gets PDF filename from data mistaken for URI-encoded", function () {
@@ -111,14 +111,14 @@ describe("ui_utils", function () {
         pending("Blob in not supported in Node.js.");
       }
 
-      var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
-      var blobUrl = (0, _util.createObjectURL)(typedArray, "application/pdf");
+      const typedArray = new Uint8Array([1, 2, 3, 4, 5]);
+      const blobUrl = (0, _util.createObjectURL)(typedArray, "application/pdf");
       expect(blobUrl.startsWith("blob:")).toEqual(true);
       expect((0, _ui_utils.getPDFFileNameFromURL)(blobUrl + "?file.pdf")).toEqual("file.pdf");
     });
     it('gets fallback filename from query string appended to "data:" URL', function () {
-      var typedArray = new Uint8Array([1, 2, 3, 4, 5]);
-      var dataUrl = (0, _util.createObjectURL)(typedArray, "application/pdf", true);
+      const typedArray = new Uint8Array([1, 2, 3, 4, 5]);
+      const dataUrl = (0, _util.createObjectURL)(typedArray, "application/pdf", true);
       expect(dataUrl.startsWith("data:")).toEqual(true);
       expect((0, _ui_utils.getPDFFileNameFromURL)(dataUrl + "?file1.pdf")).toEqual("document.pdf");
       expect((0, _ui_utils.getPDFFileNameFromURL)("     " + dataUrl + "?file2.pdf")).toEqual("document.pdf");
@@ -126,8 +126,8 @@ describe("ui_utils", function () {
   });
   describe("EventBus", function () {
     it("dispatch event", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
       eventBus.on("test", function (evt) {
         expect(evt).toEqual(undefined);
         count++;
@@ -150,8 +150,8 @@ describe("ui_utils", function () {
       expect(count).toEqual(1);
     });
     it("dispatch different event", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
       eventBus.on("test", function () {
         count++;
       });
@@ -159,8 +159,8 @@ describe("ui_utils", function () {
       expect(count).toEqual(0);
     });
     it("dispatch event multiple times", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
       eventBus.dispatch("test");
       eventBus.on("test", function () {
         count++;
@@ -170,8 +170,8 @@ describe("ui_utils", function () {
       expect(count).toEqual(2);
     });
     it("dispatch event to multiple handlers", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
       eventBus.on("test", function () {
         count++;
       });
@@ -182,10 +182,10 @@ describe("ui_utils", function () {
       expect(count).toEqual(2);
     });
     it("dispatch to detached", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
 
-      var listener = function () {
+      const listener = function () {
         count++;
       };
 
@@ -196,8 +196,8 @@ describe("ui_utils", function () {
       expect(count).toEqual(1);
     });
     it("dispatch to wrong detached", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
       eventBus.on("test", function () {
         count++;
       });
@@ -209,15 +209,15 @@ describe("ui_utils", function () {
       expect(count).toEqual(2);
     });
     it("dispatch to detached during handling", function () {
-      var eventBus = new _ui_utils.EventBus();
-      var count = 0;
+      const eventBus = new _ui_utils.EventBus();
+      let count = 0;
 
-      var listener1 = function () {
+      const listener1 = function () {
         eventBus.off("test", listener2);
         count++;
       };
 
-      var listener2 = function () {
+      const listener2 = function () {
         eventBus.off("test", listener1);
         count++;
       };
@@ -228,6 +228,24 @@ describe("ui_utils", function () {
       eventBus.dispatch("test");
       expect(count).toEqual(2);
     });
+    it("dispatch event to handlers with/without 'once' option", function () {
+      const eventBus = new _ui_utils.EventBus();
+      let multipleCount = 0,
+          onceCount = 0;
+      eventBus.on("test", function () {
+        multipleCount++;
+      });
+      eventBus.on("test", function () {
+        onceCount++;
+      }, {
+        once: true
+      });
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      expect(multipleCount).toEqual(3);
+      expect(onceCount).toEqual(1);
+    });
     it("should not re-dispatch to DOM", function (done) {
       if (_is_node.isNodeJS) {
         pending("Document in not supported in Node.js.");
@@ -484,14 +502,16 @@ describe("ui_utils", function () {
         if (viewLeft < scrollRight && viewRight > scrollLeft && viewTop < scrollBottom && viewBottom > scrollTop) {
           const hiddenHeight = Math.max(0, scrollTop - viewTop) + Math.max(0, viewBottom - scrollBottom);
           const hiddenWidth = Math.max(0, scrollLeft - viewLeft) + Math.max(0, viewRight - scrollRight);
-          const visibleArea = (div.clientHeight - hiddenHeight) * (div.clientWidth - hiddenWidth);
-          const percent = visibleArea * 100 / div.clientHeight / div.clientWidth | 0;
+          const fractionHeight = (div.clientHeight - hiddenHeight) / div.clientHeight;
+          const fractionWidth = (div.clientWidth - hiddenWidth) / div.clientWidth;
+          const percent = fractionHeight * fractionWidth * 100 | 0;
           views.push({
             id: view.id,
             x: viewLeft,
             y: viewTop,
             view,
-            percent
+            percent,
+            widthPercent: fractionWidth * 100 | 0
           });
         }
       }
@@ -503,16 +523,16 @@ describe("ui_utils", function () {
       };
     }
 
-    function scrollOverDocument(pages, horizontally = false) {
+    function scrollOverDocument(pages, horizontal = false, rtl = false) {
       const size = pages.reduce(function (max, {
         div
       }) {
-        return Math.max(max, horizontally ? div.offsetLeft + div.clientLeft + div.clientWidth : div.offsetTop + div.clientTop + div.clientHeight);
+        return Math.max(max, horizontal ? Math.abs(div.offsetLeft + div.clientLeft + div.clientWidth) : div.offsetTop + div.clientTop + div.clientHeight);
       }, 0);
 
-      for (let i = 0; i < size; i += 7) {
+      for (let i = -size; i < size; i += 7) {
         for (let j = i + 5; j < size; j += j - i) {
-          const scroll = horizontally ? {
+          const scrollEl = horizontal ? {
             scrollTop: 0,
             scrollLeft: i,
             clientHeight: 10000,
@@ -523,7 +543,13 @@ describe("ui_utils", function () {
             clientHeight: j - i,
             clientWidth: 10000
           };
-          expect((0, _ui_utils.getVisibleElements)(scroll, pages, false, horizontally)).toEqual(slowGetVisibleElements(scroll, pages));
+          expect((0, _ui_utils.getVisibleElements)({
+            scrollEl,
+            views: pages,
+            sortByVisibility: false,
+            horizontal,
+            rtl
+          })).toEqual(slowGetVisibleElements(scrollEl, pages));
         }
       }
     }
@@ -540,6 +566,10 @@ describe("ui_utils", function () {
       const pages = makePages([[[10, 50], [20, 20], [30, 10]]]);
       scrollOverDocument(pages, true);
     });
+    it("works with horizontal scrolling with RTL-documents", function () {
+      const pages = makePages([[[-10, 50], [-20, 20], [-30, 10]]]);
+      scrollOverDocument(pages, true, true);
+    });
     it("handles `sortByVisibility` correctly", function () {
       const scrollEl = {
         scrollTop: 75,
@@ -548,8 +578,15 @@ describe("ui_utils", function () {
         clientWidth: 1500
       };
       const views = makePages([[[100, 150]], [[100, 150]], [[100, 150]]]);
-      const visible = (0, _ui_utils.getVisibleElements)(scrollEl, views);
-      const visibleSorted = (0, _ui_utils.getVisibleElements)(scrollEl, views, true);
+      const visible = (0, _ui_utils.getVisibleElements)({
+        scrollEl,
+        views
+      });
+      const visibleSorted = (0, _ui_utils.getVisibleElements)({
+        scrollEl,
+        views,
+        sortByVisibility: true
+      });
       const viewsOrder = [],
             viewsSortedOrder = [];
 
@@ -572,7 +609,10 @@ describe("ui_utils", function () {
         clientWidth: 1500
       };
       const views = [];
-      expect((0, _ui_utils.getVisibleElements)(scrollEl, views)).toEqual({
+      expect((0, _ui_utils.getVisibleElements)({
+        scrollEl,
+        views
+      })).toEqual({
         first: undefined,
         last: undefined,
         views: []
@@ -586,7 +626,10 @@ describe("ui_utils", function () {
         clientWidth: 1500
       };
       const views = makePages([[[100, 150]], [[100, 150]], [[100, 150]]]);
-      expect((0, _ui_utils.getVisibleElements)(scrollEl, views)).toEqual({
+      expect((0, _ui_utils.getVisibleElements)({
+        scrollEl,
+        views
+      })).toEqual({
         first: undefined,
         last: undefined,
         views: []

+ 7 - 7
lib/test/unit/unicode_spec.js

@@ -37,7 +37,7 @@ describe("unicode", function () {
     });
   });
   describe("getUnicodeForGlyph", function () {
-    var standardMap, dingbatsMap;
+    let standardMap, dingbatsMap;
     beforeAll(function (done) {
       standardMap = (0, _glyphlist.getGlyphsUnicode)();
       dingbatsMap = (0, _glyphlist.getDingbatsGlyphsUnicode)();
@@ -71,7 +71,7 @@ describe("unicode", function () {
     });
   });
   describe("getNormalizedUnicodes", function () {
-    var NormalizedUnicodes;
+    let NormalizedUnicodes;
     beforeAll(function (done) {
       NormalizedUnicodes = (0, _unicode.getNormalizedUnicodes)();
       done();
@@ -88,7 +88,7 @@ describe("unicode", function () {
     });
   });
   describe("reverseIfRtl", function () {
-    var NormalizedUnicodes;
+    let NormalizedUnicodes;
 
     function getGlyphUnicode(char) {
       if (NormalizedUnicodes[char] !== undefined) {
@@ -106,15 +106,15 @@ describe("unicode", function () {
       NormalizedUnicodes = null;
     });
     it("should not reverse LTR characters", function () {
-      var A = getGlyphUnicode("A");
+      const A = getGlyphUnicode("A");
       expect((0, _unicode.reverseIfRtl)(A)).toEqual("A");
-      var fi = getGlyphUnicode("\uFB01");
+      const fi = getGlyphUnicode("\uFB01");
       expect((0, _unicode.reverseIfRtl)(fi)).toEqual("fi");
     });
     it("should reverse RTL characters", function () {
-      var heAlef = getGlyphUnicode("\u05D0");
+      const heAlef = getGlyphUnicode("\u05D0");
       expect((0, _unicode.reverseIfRtl)(heAlef)).toEqual("\u05D0");
-      var arAlef = getGlyphUnicode("\u0675");
+      const arAlef = getGlyphUnicode("\u0675");
       expect((0, _unicode.reverseIfRtl)(arAlef)).toEqual("\u0674\u0627");
     });
   });

Some files were not shown because too many files changed in this diff