Pārlūkot izejas kodu

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

pdfjsbot 3 gadi atpakaļ
vecāks
revīzija
f06d82e191
100 mainītis faili ar 14789 papildinājumiem un 10462 dzēšanām
  1. 1 1
      bower.json
  2. 294 290
      build/pdf.js
  3. 0 0
      build/pdf.js.map
  4. 0 0
      build/pdf.min.js
  5. 0 0
      build/pdf.sandbox.js
  6. 0 0
      build/pdf.sandbox.js.map
  7. 0 0
      build/pdf.sandbox.min.js
  8. 359 241
      build/pdf.worker.js
  9. 0 0
      build/pdf.worker.js.map
  10. 0 0
      build/pdf.worker.min.js
  11. 1236 1241
      image_decoders/pdf.image_decoders.js
  12. 0 0
      image_decoders/pdf.image_decoders.js.map
  13. 0 0
      image_decoders/pdf.image_decoders.min.js
  14. 178 211
      legacy/build/pdf.js
  15. 0 0
      legacy/build/pdf.js.map
  16. 0 0
      legacy/build/pdf.min.js
  17. 0 0
      legacy/build/pdf.sandbox.js
  18. 0 0
      legacy/build/pdf.sandbox.js.map
  19. 0 0
      legacy/build/pdf.sandbox.min.js
  20. 199 204
      legacy/build/pdf.worker.js
  21. 0 0
      legacy/build/pdf.worker.js.map
  22. 0 0
      legacy/build/pdf.worker.min.js
  23. 406 509
      legacy/image_decoders/pdf.image_decoders.js
  24. 0 0
      legacy/image_decoders/pdf.image_decoders.js.map
  25. 0 0
      legacy/image_decoders/pdf.image_decoders.min.js
  26. 127 37
      legacy/web/pdf_viewer.css
  27. 346 86
      legacy/web/pdf_viewer.js
  28. 0 0
      legacy/web/pdf_viewer.js.map
  29. 211 59
      lib/core/annotation.js
  30. 116 0
      lib/core/ascii_85_stream.js
  31. 90 0
      lib/core/ascii_hex_stream.js
  32. 123 0
      lib/core/base_stream.js
  33. 32 32
      lib/core/bidi.js
  34. 119 1228
      lib/core/catalog.js
  35. 0 12
      lib/core/ccitt.js
  36. 7 11
      lib/core/ccitt_stream.js
  37. 131 0
      lib/core/cff_font.js
  38. 1 1
      lib/core/cff_parser.js
  39. 20 77
      lib/core/chunked_stream.js
  40. 0 0
      lib/core/cmap.js
  41. 38 1
      lib/core/core_utils.js
  42. 40 46
      lib/core/crypto.js
  43. 195 0
      lib/core/decode_stream.js
  44. 75 0
      lib/core/decrypt_stream.js
  45. 156 41
      lib/core/document.js
  46. 446 201
      lib/core/evaluator.js
  47. 120 0
      lib/core/file_spec.js
  48. 35 0
      lib/core/flate_stream.js
  49. 438 530
      lib/core/font_renderer.js
  50. 13 20
      lib/core/fonts.js
  51. 53 0
      lib/core/fonts_utils.js
  52. 157 154
      lib/core/function.js
  53. 84 84
      lib/core/image.js
  54. 1236 1242
      lib/core/jbig2.js
  55. 12 19
      lib/core/jbig2_stream.js
  56. 12 18
      lib/core/jpeg_stream.js
  57. 979 982
      lib/core/jpg.js
  58. 692 639
      lib/core/jpx.js
  59. 12 18
      lib/core/jpx_stream.js
  60. 166 0
      lib/core/lzw_stream.js
  61. 181 0
      lib/core/name_number_tree.js
  62. 164 0
      lib/core/object_loader.js
  63. 156 0
      lib/core/opentype_file_builder.js
  64. 510 517
      lib/core/operator_list.js
  65. 25 13
      lib/core/parser.js
  66. 372 371
      lib/core/pattern.js
  67. 4 0
      lib/core/pdf_manager.js
  68. 268 0
      lib/core/predictor_stream.js
  69. 127 112
      lib/core/primitives.js
  70. 74 0
      lib/core/run_length_stream.js
  71. 13 346
      lib/core/stream.js
  72. 379 0
      lib/core/struct_tree.js
  73. 118 0
      lib/core/to_unicode_map.js
  74. 381 0
      lib/core/type1_font.js
  75. 47 36
      lib/core/worker.js
  76. 2 4
      lib/core/worker_stream.js
  77. 7 9
      lib/core/writer.js
  78. 23 7
      lib/core/xfa/bind.js
  79. 4 4
      lib/core/xfa/builder.js
  80. 5 5
      lib/core/xfa/config.js
  81. 415 6
      lib/core/xfa/html_utils.js
  82. 190 0
      lib/core/xfa/layout.js
  83. 9 0
      lib/core/xfa/parser.js
  84. 25 7
      lib/core/xfa/som.js
  85. 535 140
      lib/core/xfa/template.js
  86. 21 2
      lib/core/xfa/utils.js
  87. 105 14
      lib/core/xfa/xfa_object.js
  88. 186 41
      lib/core/xfa/xhtml.js
  89. 1 1
      lib/core/xml_parser.js
  90. 833 0
      lib/core/xref.js
  91. 143 155
      lib/display/annotation_layer.js
  92. 0 14
      lib/display/annotation_storage.js
  93. 57 39
      lib/display/api.js
  94. 336 325
      lib/display/canvas.js
  95. 4 4
      lib/display/content_disposition.js
  96. 2 4
      lib/display/fetch_stream.js
  97. 43 26
      lib/display/font_loader.js
  98. 18 20
      lib/display/network.js
  99. 2 4
      lib/display/node_stream.js
  100. 49 1
      lib/display/optional_content_config.js

+ 1 - 1
bower.json

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

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 294 - 290
build/pdf.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.sandbox.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.sandbox.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.sandbox.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 359 - 241
build/pdf.worker.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.worker.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
build/pdf.worker.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1236 - 1241
image_decoders/pdf.image_decoders.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
image_decoders/pdf.image_decoders.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
image_decoders/pdf.image_decoders.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 178 - 211
legacy/build/pdf.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.sandbox.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.sandbox.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.sandbox.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 199 - 204
legacy/build/pdf.worker.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.worker.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/build/pdf.worker.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 406 - 509
legacy/image_decoders/pdf.image_decoders.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/image_decoders/pdf.image_decoders.js.map


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/image_decoders/pdf.image_decoders.min.js


+ 127 - 37
legacy/web/pdf_viewer.css

@@ -24,7 +24,8 @@
   line-height: 1;
 }
 
-.textLayer > span {
+.textLayer span,
+.textLayer br {
   color: transparent;
   position: absolute;
   white-space: pre;
@@ -33,6 +34,7 @@
 }
 
 .textLayer .highlight {
+  position: relative;
   margin: -1px;
   padding: 1px;
   background-color: rgba(180, 0, 170, 1);
@@ -277,20 +279,6 @@
   cursor: pointer;
 }
 
-*/* Copyright 2021 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.
- */
 
 .xfaLayer {
   position: absolute;
@@ -303,20 +291,29 @@
 .xfaLayer * {
   color: inherit;
   font: inherit;
-  -webkit-font-kerning: inherit;
-          font-kerning: inherit;
+  font-style: inherit;
+  font-weight: inherit;
+  font-kerning: inherit;
   letter-spacing: inherit;
   text-align: inherit;
   text-decoration: inherit;
   vertical-align: inherit;
   box-sizing: border-box;
+  background: transparent;
+}
+
+.xfaLayer a {
+  color: blue;
+}
+
+.xfaRich li {
+  margin-left: 3em;
 }
 
 .xfaFont {
   color: black;
   font-weight: normal;
-  -webkit-font-kerning: none;
-          font-kerning: none;
+  font-kerning: none;
   font-size: 10px;
   font-style: normal;
   letter-spacing: 0;
@@ -325,19 +322,24 @@
 }
 
 .xfaDraw {
-  z-index: 200;
+  z-index: 100;
 }
 
 .xfaExclgroup {
-  z-index: 300;
+  z-index: 200;
 }
 
 .xfaField {
   z-index: 300;
 }
 
+.xfaRich {
+  z-index: 300;
+  line-height: 1.2;
+}
+
 .xfaSubform {
-  z-index: 100;
+  z-index: 200;
 }
 
 .xfaLabel {
@@ -352,22 +354,52 @@
   flex: 1 1 auto;
 }
 
+.xfaBorderDiv {
+  background: transparent;
+  position: absolute;
+  pointer-events: none;
+}
+
+.xfaWrapper {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: auto;
+  height: auto;
+}
+
+.xfaContentArea {
+  overflow: hidden;
+}
+
+.xfaTextfield,
+.xfaSelect {
+  background-color: rgba(0, 54, 255, 0.13);
+}
+
+.xfaTextfield:focus,
+.xfaSelect:focus {
+  background-color: transparent;
+}
+
 .xfaTextfield,
 .xfaSelect {
   width: 100%;
   height: 100%;
-  flex: 1 1 auto;
+  flex: 100 1 0;
   border: none;
+  resize: none;
 }
 
-.xfaLabel > input[type="checkbox"] {
+.xfaLabel > input[type="radio"] {
   /* Use this trick to make the checkbox invisible but
        but still focusable. */
   position: absolute;
   left: -99999px;
 }
 
-.xfaLabel > input[type="checkbox"]:focus + .xfaCheckboxMark {
+.xfaLabel > input[type="radio"]:focus + .xfaCheckboxMark {
   box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
 }
 
@@ -404,17 +436,60 @@
   background: Highlight;
 }
 
-.xfaLrTb,
-.xfaRlTb,
-.xfaTb,
+.xfaRich {
+  white-space: pre-wrap;
+}
+
+.xfaImage {
+  width: 100%;
+  height: 100%;
+}
+
+.xfaRich {
+  width: 100%;
+  height: auto;
+}
+
 .xfaPosition {
   display: block;
 }
 
+.xfaLrTb,
+.xfaRlTb,
+.xfaTb {
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+}
+
+.xfaLr,
+.xfaRl,
+.xfaTb > div {
+  flex: 1 1 auto;
+}
+
+.xfaTb > div {
+  justify-content: left;
+}
+
+.xfaLr > div {
+  display: inline;
+  float: left;
+}
+
+.xfaRl > div {
+  display: inline;
+  float: right;
+}
+
 .xfaPosition {
   position: relative;
 }
 
+.xfaArea {
+  position: relative;
+}
+
 .xfaValignMiddle {
   display: flex;
   align-items: center;
@@ -455,6 +530,26 @@
   flex: 1;
 }
 
+:root {
+  --pdfViewer-padding-bottom: none;
+  --page-margin: 1px auto -8px;
+  --page-border: 9px solid transparent;
+  --spreadHorizontalWrapped-margin-LR: -3.5px;
+}
+
+@media screen and (forced-colors: active) {
+  :root {
+    --pdfViewer-padding-bottom: 9px;
+    --page-margin: 9px auto 0;
+    --page-border: none;
+    --spreadHorizontalWrapped-margin-LR: 4.5px;
+  }
+}
+
+.pdfViewer {
+  padding-bottom: var(--pdfViewer-padding-bottom);
+}
+
 .pdfViewer .canvasWrapper {
   overflow: hidden;
 }
@@ -463,10 +558,10 @@
   direction: ltr;
   width: 816px;
   height: 1056px;
-  margin: 1px auto -8px;
+  margin: var(--page-margin);
   position: relative;
   overflow: visible;
-  border: 9px solid transparent;
+  border: var(--page-border);
   background-clip: content-box;
   -o-border-image: url(images/shadow.png) 9 9 repeat;
      border-image: url(images/shadow.png) 9 9 repeat;
@@ -519,8 +614,8 @@
 .spread .page,
 .pdfViewer.scrollHorizontal .page,
 .pdfViewer.scrollWrapped .page {
-  margin-left: -3.5px;
-  margin-right: -3.5px;
+  margin-left: var(--spreadHorizontalWrapped-margin-LR);
+  margin-right: var(--spreadHorizontalWrapped-margin-LR);
 }
 
 .pdfViewer.removePageBorders .spread .page,
@@ -570,11 +665,6 @@
   border: 0;
 }
 
-.pdfPresentationMode:-moz-full-screen .pdfViewer .page {
-  margin-bottom: 100%;
-  border: 0;
-}
-
 .pdfPresentationMode:fullscreen .pdfViewer .page {
   margin-bottom: 100%;
   border: 0;

+ 346 - 86
legacy/web/pdf_viewer.js

@@ -58,7 +58,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -1098,12 +1098,6 @@ var PDFLinkService = /*#__PURE__*/function () {
     set: function set(value) {
       this.pdfViewer.pagesRotation = value;
     }
-  }, {
-    key: "navigateTo",
-    value: function navigateTo(dest) {
-      console.error("Deprecated method: `navigateTo`, use `goToDestination` instead.");
-      this.goToDestination(dest);
-    }
   }, {
     key: "_goToDestinationHelper",
     value: function _goToDestinationHelper(rawDest) {
@@ -1627,6 +1621,8 @@ exports.waitOnEventOrTimeout = waitOnEventOrTimeout;
 exports.watchScroll = watchScroll;
 exports.WaitOnType = exports.VERTICAL_PADDING = exports.UNKNOWN_SCALE = exports.TextLayerMode = exports.SpreadMode = exports.SidebarView = exports.ScrollMode = exports.SCROLLBAR_PADDING = exports.RendererType = exports.ProgressBar = exports.PresentationModeState = exports.MIN_SCALE = exports.MAX_SCALE = exports.MAX_AUTO_SCALE = exports.EventBus = exports.DEFAULT_SCALE_VALUE = exports.DEFAULT_SCALE = exports.CSS_UNITS = exports.AutoPrintRegExp = exports.animationStarted = void 0;
 
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
@@ -1643,7 +1639,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -2167,8 +2163,6 @@ var EventBus = /*#__PURE__*/function () {
   }, {
     key: "dispatch",
     value: function dispatch(eventName) {
-      var _this = this;
-
       var eventListeners = this._listeners[eventName];
 
       if (!eventListeners || eventListeners.length === 0) {
@@ -2177,27 +2171,49 @@ var EventBus = /*#__PURE__*/function () {
 
       var args = Array.prototype.slice.call(arguments, 1);
       var externalListeners;
-      eventListeners.slice(0).forEach(function (_ref4) {
-        var listener = _ref4.listener,
-            external = _ref4.external,
-            once = _ref4.once;
 
-        if (once) {
-          _this._off(eventName, listener);
-        }
+      var _iterator = _createForOfIteratorHelper(eventListeners.slice(0)),
+          _step;
 
-        if (external) {
-          (externalListeners || (externalListeners = [])).push(listener);
-          return;
-        }
+      try {
+        for (_iterator.s(); !(_step = _iterator.n()).done;) {
+          var _step$value = _step.value,
+              _listener = _step$value.listener,
+              external = _step$value.external,
+              once = _step$value.once;
 
-        listener.apply(null, args);
-      });
+          if (once) {
+            this._off(eventName, _listener);
+          }
+
+          if (external) {
+            (externalListeners || (externalListeners = [])).push(_listener);
+            continue;
+          }
+
+          _listener.apply(null, args);
+        }
+      } catch (err) {
+        _iterator.e(err);
+      } finally {
+        _iterator.f();
+      }
 
       if (externalListeners) {
-        externalListeners.forEach(function (listener) {
-          listener.apply(null, args);
-        });
+        var _iterator2 = _createForOfIteratorHelper(externalListeners),
+            _step2;
+
+        try {
+          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
+            var listener = _step2.value;
+            listener.apply(null, args);
+          }
+        } catch (err) {
+          _iterator2.e(err);
+        } finally {
+          _iterator2.f();
+        }
+
         externalListeners = null;
       }
     }
@@ -2244,10 +2260,10 @@ function clamp(v, min, max) {
 
 var ProgressBar = /*#__PURE__*/function () {
   function ProgressBar(id) {
-    var _ref5 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
-        height = _ref5.height,
-        width = _ref5.width,
-        units = _ref5.units;
+    var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+        height = _ref4.height,
+        width = _ref4.width,
+        units = _ref4.units;
 
     _classCallCheck(this, ProgressBar);
 
@@ -3985,11 +4001,11 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArra
 
 function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
 
-function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
-function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
 
 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
 
@@ -4246,7 +4262,7 @@ var PDFFindController = /*#__PURE__*/function () {
       this._pageDiffs = [];
       this._matchesCountTotal = 0;
       this._pagesToSearch = null;
-      this._pendingFindMatches = Object.create(null);
+      this._pendingFindMatches = new Set();
       this._resumePageIdx = null;
       this._dirtyMatch = false;
       clearTimeout(this._findTimeout);
@@ -4565,14 +4581,14 @@ var PDFFindController = /*#__PURE__*/function () {
         this._updateAllPages();
 
         for (var i = 0; i < numPages; i++) {
-          if (this._pendingFindMatches[i] === true) {
+          if (this._pendingFindMatches.has(i)) {
             continue;
           }
 
-          this._pendingFindMatches[i] = true;
+          this._pendingFindMatches.add(i);
 
           this._extractTextPromises[i].then(function (pageIdx) {
-            delete _this3._pendingFindMatches[pageIdx];
+            _this3._pendingFindMatches["delete"](pageIdx);
 
             _this3._calculateMatch(pageIdx);
           });
@@ -4902,7 +4918,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -5620,10 +5636,9 @@ var PDFPageView = /*#__PURE__*/function () {
     this.textLayerFactory = options.textLayerFactory;
     this.annotationLayerFactory = options.annotationLayerFactory;
     this.xfaLayerFactory = options.xfaLayerFactory;
+    this.structTreeLayerFactory = options.structTreeLayerFactory;
     this.renderer = options.renderer || _ui_utils.RendererType.CANVAS;
-    this.enableWebGL = options.enableWebGL || false;
     this.l10n = options.l10n || _l10n_utils.NullL10n;
-    this.enableScripting = options.enableScripting === true;
     this.paintTask = null;
     this.paintedViewportMap = new WeakMap();
     this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
@@ -5633,6 +5648,7 @@ var PDFPageView = /*#__PURE__*/function () {
     this.textLayer = null;
     this.zoomLayer = null;
     this.xfaLayer = null;
+    this.structTreeLayer = null;
     var div = document.createElement("div");
     div.className = "page";
     div.style.width = Math.floor(this.viewport.width) + "px";
@@ -5933,6 +5949,12 @@ var PDFPageView = /*#__PURE__*/function () {
         this.annotationLayer.cancel();
         this.annotationLayer = null;
       }
+
+      if (this._onTextLayerRendered) {
+        this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
+
+        this._onTextLayerRendered = null;
+      }
     }
   }, {
     key: "cssTransform",
@@ -6164,7 +6186,8 @@ var PDFPageView = /*#__PURE__*/function () {
         return finishPaintTask(null).then(function () {
           if (textLayer) {
             var readableStream = pdfPage.streamTextContent({
-              normalizeWhitespace: true
+              normalizeWhitespace: true,
+              includeMarkedContent: true
             });
             textLayer.setTextContentStream(readableStream);
             textLayer.render();
@@ -6176,7 +6199,7 @@ var PDFPageView = /*#__PURE__*/function () {
 
       if (this.annotationLayerFactory) {
         if (!this.annotationLayer) {
-          this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(div, pdfPage, null, this.imageResourcesPath, this.renderInteractiveForms, this.l10n, this.enableScripting, null, null);
+          this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(div, pdfPage, null, this.imageResourcesPath, this.renderInteractiveForms, this.l10n, null, null, null);
         }
 
         this._renderAnnotationLayer();
@@ -6184,12 +6207,48 @@ var PDFPageView = /*#__PURE__*/function () {
 
       if (this.xfaLayerFactory) {
         if (!this.xfaLayer) {
-          this.xfaLayer = this.xfaLayerFactory.createXfaLayerBuilder(div, pdfPage);
+          this.xfaLayer = this.xfaLayerFactory.createXfaLayerBuilder(div, pdfPage, null);
         }
 
         this._renderXfaLayer();
       }
 
+      if (this.structTreeLayerFactory && this.textLayer && this.canvas) {
+        this._onTextLayerRendered = function (event) {
+          if (event.pageNumber !== _this2.id) {
+            return;
+          }
+
+          _this2.eventBus._off("textlayerrendered", _this2._onTextLayerRendered);
+
+          _this2._onTextLayerRendered = null;
+
+          if (!_this2.canvas) {
+            return;
+          }
+
+          _this2.pdfPage.getStructTree().then(function (tree) {
+            if (!tree) {
+              return;
+            }
+
+            if (!_this2.canvas) {
+              return;
+            }
+
+            var treeDom = _this2.structTreeLayer.render(tree);
+
+            treeDom.classList.add("structTree");
+
+            _this2.canvas.appendChild(treeDom);
+          });
+        };
+
+        this.eventBus._on("textlayerrendered", this._onTextLayerRendered);
+
+        this.structTreeLayer = this.structTreeLayerFactory.createStructTreeLayerBuilder(pdfPage);
+      }
+
       div.setAttribute("data-loaded", true);
       this.eventBus.dispatch("pagerender", {
         source: this,
@@ -6266,7 +6325,6 @@ var PDFPageView = /*#__PURE__*/function () {
         canvasContext: ctx,
         transform: transform,
         viewport: this.viewport,
-        enableWebGL: this.enableWebGL,
         renderInteractiveForms: this.renderInteractiveForms,
         optionalContentConfigPromise: this._optionalContentConfigPromise
       };
@@ -6534,13 +6592,21 @@ var _pdf_rendering_queue = __w_pdfjs_require__(17);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 
-function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
+function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
+
+function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
+
+function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
+
+function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
+
+function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
 
 function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
 
 function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
 
-function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
 
 function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
 
@@ -6550,7 +6616,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -7015,14 +7081,14 @@ var PDFScriptingManager = /*#__PURE__*/function () {
     key: "_updateFromSandbox",
     value: function () {
       var _updateFromSandbox2 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee7(detail) {
-        var isInPresentationMode, id, command, value, element, _this$_pdfDocument;
+        var isInPresentationMode, id, siblings, command, value, ids, _iterator3, _step3, elementId, element, _this$_pdfDocument;
 
         return _regenerator["default"].wrap(function _callee7$(_context7) {
           while (1) {
             switch (_context7.prev = _context7.next) {
               case 0:
                 isInPresentationMode = this._pdfViewer.isInPresentationMode || this._pdfViewer.isChangingPresentationMode;
-                id = detail.id, command = detail.command, value = detail.value;
+                id = detail.id, siblings = detail.siblings, command = detail.command, value = detail.value;
 
                 if (id) {
                   _context7.next = 25;
@@ -7093,18 +7159,31 @@ var PDFScriptingManager = /*#__PURE__*/function () {
                 return _context7.abrupt("return");
 
               case 28:
-                element = document.getElementById(id);
+                delete detail.id;
+                delete detail.siblings;
+                ids = siblings ? [id].concat(_toConsumableArray(siblings)) : [id];
+                _iterator3 = _createForOfIteratorHelper(ids);
 
-                if (element) {
-                  element.dispatchEvent(new CustomEvent("updatefromsandbox", {
-                    detail: detail
-                  }));
-                } else {
-                  delete detail.id;
-                  (_this$_pdfDocument = this._pdfDocument) === null || _this$_pdfDocument === void 0 ? void 0 : _this$_pdfDocument.annotationStorage.setValue(id, detail);
+                try {
+                  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
+                    elementId = _step3.value;
+                    element = document.getElementById(elementId);
+
+                    if (element) {
+                      element.dispatchEvent(new CustomEvent("updatefromsandbox", {
+                        detail: detail
+                      }));
+                    } else {
+                      (_this$_pdfDocument = this._pdfDocument) === null || _this$_pdfDocument === void 0 ? void 0 : _this$_pdfDocument.annotationStorage.setValue(elementId, detail);
+                    }
+                  }
+                } catch (err) {
+                  _iterator3.e(err);
+                } finally {
+                  _iterator3.f();
                 }
 
-              case 30:
+              case 33:
               case "end":
                 return _context7.stop();
             }
@@ -7351,7 +7430,7 @@ var PDFScriptingManager = /*#__PURE__*/function () {
       var _destroyScripting2 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee12() {
         var _this$_destroyCapabil3;
 
-        var _this$_destroyCapabil2, _iterator3, _step3, _step3$value, name, listener, _iterator4, _step4, _step4$value, _name2, _listener2;
+        var _this$_destroyCapabil2, _iterator4, _step4, _step4$value, name, listener, _iterator5, _step5, _step5$value, _name2, _listener2;
 
         return _regenerator["default"].wrap(function _callee12$(_context12) {
           while (1) {
@@ -7395,33 +7474,33 @@ var PDFScriptingManager = /*#__PURE__*/function () {
                 _context12.t0 = _context12["catch"](9);
 
               case 16:
-                _iterator3 = _createForOfIteratorHelper(this._internalEvents);
+                _iterator4 = _createForOfIteratorHelper(this._internalEvents);
 
                 try {
-                  for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
-                    _step3$value = _slicedToArray(_step3.value, 2), name = _step3$value[0], listener = _step3$value[1];
+                  for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
+                    _step4$value = _slicedToArray(_step4.value, 2), name = _step4$value[0], listener = _step4$value[1];
 
                     this._eventBus._off(name, listener);
                   }
                 } catch (err) {
-                  _iterator3.e(err);
+                  _iterator4.e(err);
                 } finally {
-                  _iterator3.f();
+                  _iterator4.f();
                 }
 
                 this._internalEvents.clear();
 
-                _iterator4 = _createForOfIteratorHelper(this._domEvents);
+                _iterator5 = _createForOfIteratorHelper(this._domEvents);
 
                 try {
-                  for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
-                    _step4$value = _slicedToArray(_step4.value, 2), _name2 = _step4$value[0], _listener2 = _step4$value[1];
+                  for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
+                    _step5$value = _slicedToArray(_step5.value, 2), _name2 = _step5$value[0], _listener2 = _step5$value[1];
                     window.removeEventListener(_name2, _listener2);
                   }
                 } catch (err) {
-                  _iterator4.e(err);
+                  _iterator5.e(err);
                 } finally {
-                  _iterator4.f();
+                  _iterator5.f();
                 }
 
                 this._domEvents.clear();
@@ -7475,7 +7554,7 @@ var _pdfjsLib = __w_pdfjs_require__(2);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 
-function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
+function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
 
 function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
 
@@ -7863,11 +7942,13 @@ var _pdf_page_view = __w_pdfjs_require__(16);
 
 var _pdf_link_service = __w_pdfjs_require__(6);
 
+var _struct_tree_layer_builder = __w_pdfjs_require__(22);
+
 var _text_layer_builder = __w_pdfjs_require__(8);
 
-var _xfa_layer_builder = __w_pdfjs_require__(22);
+var _xfa_layer_builder = __w_pdfjs_require__(23);
 
-function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
 
 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
 
@@ -7947,7 +8028,7 @@ var BaseViewer = /*#__PURE__*/function () {
       throw new Error("Cannot initialize BaseViewer.");
     }
 
-    var viewerVersion = '2.8.335';
+    var viewerVersion = '2.9.359';
 
     if (_pdfjsLib.version !== viewerVersion) {
       throw new Error("The API version \"".concat(_pdfjsLib.version, "\" does not match the Viewer version \"").concat(viewerVersion, "\"."));
@@ -7976,7 +8057,6 @@ var BaseViewer = /*#__PURE__*/function () {
     this.renderInteractiveForms = options.renderInteractiveForms !== false;
     this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
     this.renderer = options.renderer || _ui_utils.RendererType.CANVAS;
-    this.enableWebGL = options.enableWebGL || false;
     this.useOnlyCssZoom = options.useOnlyCssZoom || false;
     this.maxCanvasPixels = options.maxCanvasPixels;
     this.l10n = options.l10n || _l10n_utils.NullL10n;
@@ -8300,14 +8380,13 @@ var BaseViewer = /*#__PURE__*/function () {
             textLayerMode: _this2.textLayerMode,
             annotationLayerFactory: _this2,
             xfaLayerFactory: xfaLayerFactory,
+            structTreeLayerFactory: _this2,
             imageResourcesPath: _this2.imageResourcesPath,
             renderInteractiveForms: _this2.renderInteractiveForms,
             renderer: _this2.renderer,
-            enableWebGL: _this2.enableWebGL,
             useOnlyCssZoom: _this2.useOnlyCssZoom,
             maxCanvasPixels: _this2.maxCanvasPixels,
-            l10n: _this2.l10n,
-            enableScripting: _this2.enableScripting
+            l10n: _this2.l10n
           });
 
           _this2._pages.push(pageView);
@@ -8995,7 +9074,7 @@ var BaseViewer = /*#__PURE__*/function () {
       var imageResourcesPath = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "";
       var renderInteractiveForms = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
       var l10n = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : _l10n_utils.NullL10n;
-      var enableScripting = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
+      var enableScripting = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null;
       var hasJSActionsPromise = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
       var mouseState = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
       return new _annotation_layer_builder.AnnotationLayerBuilder({
@@ -9007,7 +9086,7 @@ var BaseViewer = /*#__PURE__*/function () {
         linkService: this.linkService,
         downloadManager: this.downloadManager,
         l10n: l10n,
-        enableScripting: enableScripting,
+        enableScripting: enableScripting !== null && enableScripting !== void 0 ? enableScripting : this.enableScripting,
         hasJSActionsPromise: hasJSActionsPromise || ((_this$pdfDocument2 = this.pdfDocument) === null || _this$pdfDocument2 === void 0 ? void 0 : _this$pdfDocument2.hasJSActions()),
         mouseState: mouseState || ((_this$_scriptingManag = this._scriptingManager) === null || _this$_scriptingManag === void 0 ? void 0 : _this$_scriptingManag.mouseState)
       });
@@ -9015,8 +9094,19 @@ var BaseViewer = /*#__PURE__*/function () {
   }, {
     key: "createXfaLayerBuilder",
     value: function createXfaLayerBuilder(pageDiv, pdfPage) {
+      var _this$pdfDocument3;
+
+      var annotationStorage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
       return new _xfa_layer_builder.XfaLayerBuilder({
         pageDiv: pageDiv,
+        pdfPage: pdfPage,
+        annotationStorage: annotationStorage || ((_this$pdfDocument3 = this.pdfDocument) === null || _this$pdfDocument3 === void 0 ? void 0 : _this$pdfDocument3.annotationStorage)
+      });
+    }
+  }, {
+    key: "createStructTreeLayerBuilder",
+    value: function createStructTreeLayerBuilder(pdfPage) {
+      return new _struct_tree_layer_builder.StructTreeLayerBuilder({
         pdfPage: pdfPage
       });
     }
@@ -9420,6 +9510,169 @@ exports.BaseViewer = BaseViewer;
 
 /***/ }),
 /* 22 */
+/***/ ((__unused_webpack_module, exports) => {
+
+
+
+Object.defineProperty(exports, "__esModule", ({
+  value: true
+}));
+exports.StructTreeLayerBuilder = exports.DefaultStructTreeLayerFactory = void 0;
+
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+
+function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
+
+function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
+
+function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
+
+var PDF_ROLE_TO_HTML_ROLE = {
+  Document: null,
+  DocumentFragment: null,
+  Part: "group",
+  Sect: "group",
+  Div: "group",
+  Aside: "note",
+  NonStruct: "none",
+  P: null,
+  H: "heading",
+  Title: null,
+  FENote: "note",
+  Sub: "group",
+  Lbl: null,
+  Span: null,
+  Em: null,
+  Strong: null,
+  Link: "link",
+  Annot: "note",
+  Form: "form",
+  Ruby: null,
+  RB: null,
+  RT: null,
+  RP: null,
+  Warichu: null,
+  WT: null,
+  WP: null,
+  L: "list",
+  LI: "listitem",
+  LBody: null,
+  Table: "table",
+  TR: "row",
+  TH: "columnheader",
+  TD: "cell",
+  THead: "columnheader",
+  TBody: null,
+  TFoot: null,
+  Caption: null,
+  Figure: "figure",
+  Formula: null,
+  Artifact: null
+};
+var HEADING_PATTERN = /^H(\d+)$/;
+
+var StructTreeLayerBuilder = /*#__PURE__*/function () {
+  function StructTreeLayerBuilder(_ref) {
+    var pdfPage = _ref.pdfPage;
+
+    _classCallCheck(this, StructTreeLayerBuilder);
+
+    this.pdfPage = pdfPage;
+  }
+
+  _createClass(StructTreeLayerBuilder, [{
+    key: "render",
+    value: function render(structTree) {
+      return this._walk(structTree);
+    }
+  }, {
+    key: "_setAttributes",
+    value: function _setAttributes(structElement, htmlElement) {
+      if (structElement.alt !== undefined) {
+        htmlElement.setAttribute("aria-label", structElement.alt);
+      }
+
+      if (structElement.id !== undefined) {
+        htmlElement.setAttribute("aria-owns", structElement.id);
+      }
+    }
+  }, {
+    key: "_walk",
+    value: function _walk(node) {
+      if (!node) {
+        return null;
+      }
+
+      var element = document.createElement("span");
+
+      if ("role" in node) {
+        var role = node.role;
+        var match = role.match(HEADING_PATTERN);
+
+        if (match) {
+          element.setAttribute("role", "heading");
+          element.setAttribute("aria-level", match[1]);
+        } else if (PDF_ROLE_TO_HTML_ROLE[role]) {
+          element.setAttribute("role", PDF_ROLE_TO_HTML_ROLE[role]);
+        }
+      }
+
+      this._setAttributes(node, element);
+
+      if (node.children) {
+        if (node.children.length === 1 && "id" in node.children[0]) {
+          this._setAttributes(node.children[0], element);
+        } else {
+          var _iterator = _createForOfIteratorHelper(node.children),
+              _step;
+
+          try {
+            for (_iterator.s(); !(_step = _iterator.n()).done;) {
+              var kid = _step.value;
+              element.appendChild(this._walk(kid));
+            }
+          } catch (err) {
+            _iterator.e(err);
+          } finally {
+            _iterator.f();
+          }
+        }
+      }
+
+      return element;
+    }
+  }]);
+
+  return StructTreeLayerBuilder;
+}();
+
+exports.StructTreeLayerBuilder = StructTreeLayerBuilder;
+
+var DefaultStructTreeLayerFactory = /*#__PURE__*/function () {
+  function DefaultStructTreeLayerFactory() {
+    _classCallCheck(this, DefaultStructTreeLayerFactory);
+  }
+
+  _createClass(DefaultStructTreeLayerFactory, [{
+    key: "createStructTreeLayerBuilder",
+    value: function createStructTreeLayerBuilder(pdfPage) {
+      return new StructTreeLayerBuilder({
+        pdfPage: pdfPage
+      });
+    }
+  }]);
+
+  return DefaultStructTreeLayerFactory;
+}();
+
+exports.DefaultStructTreeLayerFactory = DefaultStructTreeLayerFactory;
+
+/***/ }),
+/* 23 */
 /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => {
 
 
@@ -9440,12 +9693,14 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
 var XfaLayerBuilder = /*#__PURE__*/function () {
   function XfaLayerBuilder(_ref) {
     var pageDiv = _ref.pageDiv,
-        pdfPage = _ref.pdfPage;
+        pdfPage = _ref.pdfPage,
+        annotationStorage = _ref.annotationStorage;
 
     _classCallCheck(this, XfaLayerBuilder);
 
     this.pageDiv = pageDiv;
     this.pdfPage = pdfPage;
+    this.annotationStorage = annotationStorage;
     this.div = null;
     this._cancelled = false;
   }
@@ -9467,7 +9722,8 @@ var XfaLayerBuilder = /*#__PURE__*/function () {
           }),
           div: _this.div,
           xfa: xfa,
-          page: _this.pdfPage
+          page: _this.pdfPage,
+          annotationStorage: _this.annotationStorage
         };
 
         if (_this.div) {
@@ -9481,6 +9737,8 @@ var XfaLayerBuilder = /*#__PURE__*/function () {
 
           _pdfjsLib.XfaLayer.render(parameters);
         }
+      })["catch"](function (error) {
+        console.error(error);
       });
     }
   }, {
@@ -9512,9 +9770,11 @@ var DefaultXfaLayerFactory = /*#__PURE__*/function () {
   _createClass(DefaultXfaLayerFactory, [{
     key: "createXfaLayerBuilder",
     value: function createXfaLayerBuilder(pageDiv, pdfPage) {
+      var annotationStorage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
       return new XfaLayerBuilder({
         pageDiv: pageDiv,
-        pdfPage: pdfPage
+        pdfPage: pdfPage,
+        annotationStorage: annotationStorage
       });
     }
   }]);
@@ -9525,7 +9785,7 @@ var DefaultXfaLayerFactory = /*#__PURE__*/function () {
 exports.DefaultXfaLayerFactory = DefaultXfaLayerFactory;
 
 /***/ }),
-/* 23 */
+/* 24 */
 /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => {
 
 
@@ -9543,7 +9803,7 @@ var _base_viewer = __w_pdfjs_require__(21);
 
 var _pdfjsLib = __w_pdfjs_require__(2);
 
-function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
+function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
 
 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
 
@@ -9850,10 +10110,10 @@ var _pdf_scripting_manager = __w_pdfjs_require__(18);
 
 var _pdf_single_page_viewer = __w_pdfjs_require__(20);
 
-var _pdf_viewer = __w_pdfjs_require__(23);
+var _pdf_viewer = __w_pdfjs_require__(24);
 
-var pdfjsVersion = '2.8.335';
-var pdfjsBuild = '228adbf67';
+var pdfjsVersion = '2.9.359';
+var pdfjsBuild = 'e667c8cbc';
 })();
 
 /******/ 	return __webpack_exports__;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
legacy/web/pdf_viewer.js.map


+ 211 - 59
lib/core/annotation.js

@@ -29,16 +29,20 @@ exports.MarkupAnnotation = exports.AnnotationFactory = exports.AnnotationBorderS
 
 var _util = require("../shared/util.js");
 
-var _obj = require("./obj.js");
-
 var _core_utils = require("./core_utils.js");
 
 var _default_appearance = require("./default_appearance.js");
 
 var _primitives = require("./primitives.js");
 
+var _catalog = require("./catalog.js");
+
 var _colorspace = require("./colorspace.js");
 
+var _file_spec = require("./file_spec.js");
+
+var _object_loader = require("./object_loader.js");
+
 var _operator_list = require("./operator_list.js");
 
 var _stream = require("./stream.js");
@@ -47,12 +51,10 @@ var _writer = require("./writer.js");
 
 class AnnotationFactory {
   static create(xref, ref, pdfManager, idFactory, collectFields) {
-    return pdfManager.ensureCatalog("acroForm").then(acroForm => {
-      return pdfManager.ensure(this, "_create", [xref, ref, pdfManager, idFactory, acroForm, collectFields]);
-    });
+    return Promise.all([pdfManager.ensureCatalog("acroForm"), collectFields ? this._getPageIndex(xref, ref, pdfManager) : -1]).then(([acroForm, pageIndex]) => pdfManager.ensure(this, "_create", [xref, ref, pdfManager, idFactory, acroForm, collectFields, pageIndex]));
   }
 
-  static _create(xref, ref, pdfManager, idFactory, acroForm, collectFields) {
+  static _create(xref, ref, pdfManager, idFactory, acroForm, collectFields, pageIndex = -1) {
     const dict = xref.fetchIfRef(ref);
 
     if (!(0, _primitives.isDict)(dict)) {
@@ -70,7 +72,8 @@ class AnnotationFactory {
       id,
       pdfManager,
       acroForm: acroForm instanceof _primitives.Dict ? acroForm : _primitives.Dict.empty,
-      collectFields
+      collectFields,
+      pageIndex
     };
 
     switch (subtype) {
@@ -96,6 +99,9 @@ class AnnotationFactory {
 
           case "Ch":
             return new ChoiceWidgetAnnotation(parameters);
+
+          case "Sig":
+            return new SignatureWidgetAnnotation(parameters);
         }
 
         (0, _util.warn)(`Unimplemented widget field type "${fieldType}", ` + "falling back to base field type.");
@@ -159,6 +165,28 @@ class AnnotationFactory {
     }
   }
 
+  static async _getPageIndex(xref, ref, pdfManager) {
+    try {
+      const annotDict = await xref.fetchIfRefAsync(ref);
+
+      if (!(0, _primitives.isDict)(annotDict)) {
+        return -1;
+      }
+
+      const pageRef = annotDict.getRaw("P");
+
+      if (!(0, _primitives.isRef)(pageRef)) {
+        return -1;
+      }
+
+      const pageIndex = await pdfManager.ensureCatalog("getPageIndex", [pageRef]);
+      return pageIndex;
+    } catch (ex) {
+      (0, _util.warn)(`_getPageIndex: "${ex}".`);
+      return -1;
+    }
+  }
+
 }
 
 exports.AnnotationFactory = AnnotationFactory;
@@ -302,6 +330,7 @@ class Annotation {
 
       this.data.actions = (0, _core_utils.collectActions)(params.xref, dict, _util.AnnotationActionEventType);
       this.data.fieldName = this._constructFieldName(dict);
+      this.data.pageIndex = params.pageIndex;
     }
 
     this._fallbackFontDict = null;
@@ -319,14 +348,24 @@ class Annotation {
     return this._hasFlag(flags, _util.AnnotationFlag.PRINT) && !this._hasFlag(flags, _util.AnnotationFlag.INVISIBLE);
   }
 
-  isHidden(annotationStorage) {
+  mustBeViewed(annotationStorage) {
     const storageEntry = annotationStorage && annotationStorage.get(this.data.id);
 
     if (storageEntry && storageEntry.hidden !== undefined) {
-      return storageEntry.hidden;
+      return !storageEntry.hidden;
     }
 
-    return this._hasFlag(this.flags, _util.AnnotationFlag.HIDDEN);
+    return this.viewable && !this._hasFlag(this.flags, _util.AnnotationFlag.HIDDEN);
+  }
+
+  mustBePrinted(annotationStorage) {
+    const storageEntry = annotationStorage && annotationStorage.get(this.data.id);
+
+    if (storageEntry && storageEntry.print !== undefined) {
+      return storageEntry.print;
+    }
+
+    return this.printable;
   }
 
   get viewable() {
@@ -448,7 +487,7 @@ class Annotation {
         return undefined;
       }
 
-      const objectLoader = new _obj.ObjectLoader(resources, keys, resources.xref);
+      const objectLoader = new _object_loader.ObjectLoader(resources, keys, resources.xref);
       return objectLoader.load().then(function () {
         return resources;
       });
@@ -495,7 +534,8 @@ class Annotation {
         actions: this.data.actions,
         name: this.data.fieldName,
         type: "",
-        kidIds: this.data.kidIds
+        kidIds: this.data.kidIds,
+        page: this.data.pageIndex
       };
     }
 
@@ -719,6 +759,8 @@ class MarkupAnnotation extends Annotation {
     strokeColor,
     fillColor,
     blendMode,
+    strokeAlpha,
+    fillAlpha,
     pointsCallback
   }) {
     let minX = Number.MAX_VALUE;
@@ -778,6 +820,14 @@ class MarkupAnnotation extends Annotation {
       gsDict.set("BM", _primitives.Name.get(blendMode));
     }
 
+    if (typeof strokeAlpha === "number") {
+      gsDict.set("CA", strokeAlpha);
+    }
+
+    if (typeof fillAlpha === "number") {
+      gsDict.set("ca", fillAlpha);
+    }
+
     const stateDict = new _primitives.Dict(xref);
     stateDict.set("GS0", gsDict);
     const resources = new _primitives.Dict(xref);
@@ -864,12 +914,6 @@ class WidgetAnnotation extends Annotation {
 
     data.readOnly = this.hasFieldFlag(_util.AnnotationFieldFlag.READONLY);
     data.hidden = this._hasFlag(data.annotationFlags, _util.AnnotationFlag.HIDDEN);
-
-    if (data.fieldType === "Sig") {
-      data.fieldValue = null;
-      this.setFlags(_util.AnnotationFlag.HIDDEN);
-      data.hidden = true;
-    }
   }
 
   _decodeFormValue(formValue) {
@@ -889,7 +933,7 @@ class WidgetAnnotation extends Annotation {
   }
 
   getOperatorList(evaluator, task, renderForms, annotationStorage) {
-    if (renderForms) {
+    if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
       return Promise.resolve(new _operator_list.OperatorList());
     }
 
@@ -983,9 +1027,7 @@ class WidgetAnnotation extends Annotation {
     bufferOriginal.push("\nendobj\n");
     const bufferNew = [`${newRef.num} ${newRef.gen} obj\n`];
     (0, _writer.writeDict)(appearanceDict, bufferNew, newTransform);
-    bufferNew.push(" stream\n");
-    bufferNew.push(appearance);
-    bufferNew.push("\nendstream\nendobj\n");
+    bufferNew.push(" stream\n", appearance, "\nendstream\nendobj\n");
     return [{
       ref: this.ref,
       data: bufferOriginal.join(""),
@@ -1182,14 +1224,6 @@ class WidgetAnnotation extends Annotation {
   }
 
   getFieldObject() {
-    if (this.data.fieldType === "Sig") {
-      return {
-        id: this.data.id,
-        value: null,
-        type: "signature"
-      };
-    }
-
     return null;
   }
 
@@ -1335,6 +1369,7 @@ class TextWidgetAnnotation extends WidgetAnnotation {
       name: this.data.fieldName,
       rect: this.data.rect,
       actions: this.data.actions,
+      page: this.data.pageIndex,
       type: "text"
     };
   }
@@ -1628,7 +1663,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
 
     this.data.isTooltipOnly = !params.dict.has("A") && !params.dict.has("AA");
 
-    _obj.Catalog.parseDestDictionary({
+    _catalog.Catalog.parseDestDictionary({
       destDict: params.dict,
       resultObj: this.data,
       docBaseUrl: params.pdfManager.docBaseUrl
@@ -1657,6 +1692,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
       rect: this.data.rect,
       hidden: this.data.hidden,
       actions: this.data.actions,
+      page: this.data.pageIndex,
       type
     };
   }
@@ -1720,12 +1756,30 @@ class ChoiceWidgetAnnotation extends WidgetAnnotation {
       hidden: this.data.hidden,
       actions: this.data.actions,
       items: this.data.options,
+      page: this.data.pageIndex,
       type
     };
   }
 
 }
 
+class SignatureWidgetAnnotation extends WidgetAnnotation {
+  constructor(params) {
+    super(params);
+    this.data.fieldValue = null;
+  }
+
+  getFieldObject() {
+    return {
+      id: this.data.id,
+      value: null,
+      page: this.data.pageIndex,
+      type: "signature"
+    };
+  }
+
+}
+
 class TextAnnotation extends MarkupAnnotation {
   constructor(parameters) {
     const DEFAULT_ICON_SIZE = 22;
@@ -1762,7 +1816,7 @@ class LinkAnnotation extends Annotation {
       this.data.quadPoints = quadPoints;
     }
 
-    _obj.Catalog.parseDestDictionary({
+    _catalog.Catalog.parseDestDictionary({
       destDict: params.dict,
       resultObj: this.data,
       docBaseUrl: params.pdfManager.docBaseUrl
@@ -1845,20 +1899,33 @@ class LineAnnotation extends MarkupAnnotation {
 
     if (!this.appearance) {
       const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
-      const borderWidth = this.borderStyle.width;
+      const strokeAlpha = parameters.dict.get("CA");
+      let fillColor = null,
+          interiorColor = parameters.dict.getArray("IC");
+
+      if (interiorColor) {
+        interiorColor = getRgbColor(interiorColor);
+        fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
+      }
+
+      const fillAlpha = fillColor ? strokeAlpha : null;
+      const borderWidth = this.borderStyle.width || 1,
+            borderAdjust = 2 * borderWidth;
+      const bbox = [this.data.lineCoordinates[0] - borderAdjust, this.data.lineCoordinates[1] - borderAdjust, this.data.lineCoordinates[2] + borderAdjust, this.data.lineCoordinates[3] + borderAdjust];
 
-      if ((0, _util.isArrayEqual)(this.rectangle, [0, 0, 0, 0])) {
-        this.rectangle = [this.data.lineCoordinates[0] - 2 * borderWidth, this.data.lineCoordinates[1] - 2 * borderWidth, this.data.lineCoordinates[2] + 2 * borderWidth, this.data.lineCoordinates[3] + 2 * borderWidth];
+      if (!_util.Util.intersect(this.rectangle, bbox)) {
+        this.rectangle = bbox;
       }
 
       this._setDefaultAppearance({
         xref: parameters.xref,
         extra: `${borderWidth} w`,
         strokeColor,
+        fillColor,
+        strokeAlpha,
+        fillAlpha,
         pointsCallback: (buffer, points) => {
-          buffer.push(`${lineCoordinates[0]} ${lineCoordinates[1]} m`);
-          buffer.push(`${lineCoordinates[2]} ${lineCoordinates[3]} l`);
-          buffer.push("S");
+          buffer.push(`${lineCoordinates[0]} ${lineCoordinates[1]} m`, `${lineCoordinates[2]} ${lineCoordinates[3]} l`, "S");
           return [points[0].x - borderWidth, points[1].x + borderWidth, points[3].y - borderWidth, points[1].y + borderWidth];
         }
       });
@@ -1874,19 +1941,24 @@ class SquareAnnotation extends MarkupAnnotation {
 
     if (!this.appearance) {
       const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
-      let fillColor = null;
-      let interiorColor = parameters.dict.getArray("IC");
+      const strokeAlpha = parameters.dict.get("CA");
+      let fillColor = null,
+          interiorColor = parameters.dict.getArray("IC");
 
       if (interiorColor) {
         interiorColor = getRgbColor(interiorColor);
         fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
       }
 
+      const fillAlpha = fillColor ? strokeAlpha : null;
+
       this._setDefaultAppearance({
         xref: parameters.xref,
         extra: `${this.borderStyle.width} w`,
         strokeColor,
         fillColor,
+        strokeAlpha,
+        fillAlpha,
         pointsCallback: (buffer, points) => {
           const x = points[2].x + this.borderStyle.width / 2;
           const y = points[2].y + this.borderStyle.width / 2;
@@ -1915,6 +1987,7 @@ class CircleAnnotation extends MarkupAnnotation {
 
     if (!this.appearance) {
       const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+      const strokeAlpha = parameters.dict.get("CA");
       let fillColor = null;
       let interiorColor = parameters.dict.getArray("IC");
 
@@ -1923,6 +1996,7 @@ class CircleAnnotation extends MarkupAnnotation {
         fillColor = interiorColor ? Array.from(interiorColor).map(c => c / 255) : null;
       }
 
+      const fillAlpha = fillColor ? strokeAlpha : null;
       const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));
 
       this._setDefaultAppearance({
@@ -1930,6 +2004,8 @@ class CircleAnnotation extends MarkupAnnotation {
         extra: `${this.borderStyle.width} w`,
         strokeColor,
         fillColor,
+        strokeAlpha,
+        fillAlpha,
         pointsCallback: (buffer, points) => {
           const x0 = points[0].x + this.borderStyle.width / 2;
           const y0 = points[0].y - this.borderStyle.width / 2;
@@ -1939,12 +2015,7 @@ class CircleAnnotation extends MarkupAnnotation {
           const yMid = y0 + (y1 - y0) / 2;
           const xOffset = (x1 - x0) / 2 * controlPointsDistance;
           const yOffset = (y1 - y0) / 2 * controlPointsDistance;
-          buffer.push(`${xMid} ${y1} m`);
-          buffer.push(`${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`);
-          buffer.push(`${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`);
-          buffer.push(`${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`);
-          buffer.push(`${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`);
-          buffer.push("h");
+          buffer.push(`${xMid} ${y1} m`, `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`, `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`, `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`, `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`, "h");
 
           if (fillColor) {
             buffer.push("B");
@@ -1977,6 +2048,42 @@ class PolylineAnnotation extends MarkupAnnotation {
         y: rawVertices[i + 1]
       });
     }
+
+    if (!this.appearance) {
+      const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+      const strokeAlpha = parameters.dict.get("CA");
+      const borderWidth = this.borderStyle.width || 1,
+            borderAdjust = 2 * borderWidth;
+      const bbox = [Infinity, Infinity, -Infinity, -Infinity];
+
+      for (const vertex of this.data.vertices) {
+        bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
+        bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
+        bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
+        bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
+      }
+
+      if (!_util.Util.intersect(this.rectangle, bbox)) {
+        this.rectangle = bbox;
+      }
+
+      this._setDefaultAppearance({
+        xref: parameters.xref,
+        extra: `${borderWidth} w`,
+        strokeColor,
+        strokeAlpha,
+        pointsCallback: (buffer, points) => {
+          const vertices = this.data.vertices;
+
+          for (let i = 0, ii = vertices.length; i < ii; i++) {
+            buffer.push(`${vertices[i].x} ${vertices[i].y} ${i === 0 ? "m" : "l"}`);
+          }
+
+          buffer.push("S");
+          return [points[0].x, points[1].x, points[3].y, points[1].y];
+        }
+      });
+    }
   }
 
 }
@@ -2020,6 +2127,45 @@ class InkAnnotation extends MarkupAnnotation {
         });
       }
     }
+
+    if (!this.appearance) {
+      const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+      const strokeAlpha = parameters.dict.get("CA");
+      const borderWidth = this.borderStyle.width || 1,
+            borderAdjust = 2 * borderWidth;
+      const bbox = [Infinity, Infinity, -Infinity, -Infinity];
+
+      for (const inkLists of this.data.inkLists) {
+        for (const vertex of inkLists) {
+          bbox[0] = Math.min(bbox[0], vertex.x - borderAdjust);
+          bbox[1] = Math.min(bbox[1], vertex.y - borderAdjust);
+          bbox[2] = Math.max(bbox[2], vertex.x + borderAdjust);
+          bbox[3] = Math.max(bbox[3], vertex.y + borderAdjust);
+        }
+      }
+
+      if (!_util.Util.intersect(this.rectangle, bbox)) {
+        this.rectangle = bbox;
+      }
+
+      this._setDefaultAppearance({
+        xref: parameters.xref,
+        extra: `${borderWidth} w`,
+        strokeColor,
+        strokeAlpha,
+        pointsCallback: (buffer, points) => {
+          for (const inkList of this.data.inkLists) {
+            for (let i = 0, ii = inkList.length; i < ii; i++) {
+              buffer.push(`${inkList[i].x} ${inkList[i].y} ${i === 0 ? "m" : "l"}`);
+            }
+
+            buffer.push("S");
+          }
+
+          return [points[0].x, points[1].x, points[3].y, points[1].y];
+        }
+      });
+    }
   }
 
 }
@@ -2031,19 +2177,23 @@ class HighlightAnnotation extends MarkupAnnotation {
     const quadPoints = this.data.quadPoints = getQuadPoints(parameters.dict, null);
 
     if (quadPoints) {
-      if (!this.appearance) {
+      const resources = this.appearance && this.appearance.dict.get("Resources");
+
+      if (!this.appearance || !(resources && resources.has("ExtGState"))) {
+        if (this.appearance) {
+          (0, _util.warn)("HighlightAnnotation - ignoring built-in appearance stream.");
+        }
+
         const fillColor = this.color ? Array.from(this.color).map(c => c / 255) : [1, 1, 0];
+        const fillAlpha = parameters.dict.get("CA");
 
         this._setDefaultAppearance({
           xref: parameters.xref,
           fillColor,
           blendMode: "Multiply",
+          fillAlpha,
           pointsCallback: (buffer, points) => {
-            buffer.push(`${points[0].x} ${points[0].y} m`);
-            buffer.push(`${points[1].x} ${points[1].y} l`);
-            buffer.push(`${points[3].x} ${points[3].y} l`);
-            buffer.push(`${points[2].x} ${points[2].y} l`);
-            buffer.push("f");
+            buffer.push(`${points[0].x} ${points[0].y} m`, `${points[1].x} ${points[1].y} l`, `${points[3].x} ${points[3].y} l`, `${points[2].x} ${points[2].y} l`, "f");
             return [points[0].x, points[1].x, points[3].y, points[1].y];
           }
         });
@@ -2064,15 +2214,15 @@ class UnderlineAnnotation extends MarkupAnnotation {
     if (quadPoints) {
       if (!this.appearance) {
         const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+        const strokeAlpha = parameters.dict.get("CA");
 
         this._setDefaultAppearance({
           xref: parameters.xref,
           extra: "[] 0 d 1 w",
           strokeColor,
+          strokeAlpha,
           pointsCallback: (buffer, points) => {
-            buffer.push(`${points[2].x} ${points[2].y} m`);
-            buffer.push(`${points[3].x} ${points[3].y} l`);
-            buffer.push("S");
+            buffer.push(`${points[2].x} ${points[2].y} m`, `${points[3].x} ${points[3].y} l`, "S");
             return [points[0].x, points[1].x, points[3].y, points[1].y];
           }
         });
@@ -2093,11 +2243,13 @@ class SquigglyAnnotation extends MarkupAnnotation {
     if (quadPoints) {
       if (!this.appearance) {
         const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+        const strokeAlpha = parameters.dict.get("CA");
 
         this._setDefaultAppearance({
           xref: parameters.xref,
           extra: "[] 0 d 1 w",
           strokeColor,
+          strokeAlpha,
           pointsCallback: (buffer, points) => {
             const dy = (points[0].y - points[2].y) / 6;
             let shift = dy;
@@ -2133,15 +2285,15 @@ class StrikeOutAnnotation extends MarkupAnnotation {
     if (quadPoints) {
       if (!this.appearance) {
         const strokeColor = this.color ? Array.from(this.color).map(c => c / 255) : [0, 0, 0];
+        const strokeAlpha = parameters.dict.get("CA");
 
         this._setDefaultAppearance({
           xref: parameters.xref,
           extra: "[] 0 d 1 w",
           strokeColor,
+          strokeAlpha,
           pointsCallback: (buffer, points) => {
-            buffer.push(`${(points[0].x + points[2].x) / 2}` + ` ${(points[0].y + points[2].y) / 2} m`);
-            buffer.push(`${(points[1].x + points[3].x) / 2}` + ` ${(points[1].y + points[3].y) / 2} l`);
-            buffer.push("S");
+            buffer.push(`${(points[0].x + points[2].x) / 2} ` + `${(points[0].y + points[2].y) / 2} m`, `${(points[1].x + points[3].x) / 2} ` + `${(points[1].y + points[3].y) / 2} l`, "S");
             return [points[0].x, points[1].x, points[3].y, points[1].y];
           }
         });
@@ -2164,7 +2316,7 @@ class StampAnnotation extends MarkupAnnotation {
 class FileAttachmentAnnotation extends MarkupAnnotation {
   constructor(parameters) {
     super(parameters);
-    const file = new _obj.FileSpec(parameters.dict.get("FS"), parameters.xref);
+    const file = new _file_spec.FileSpec(parameters.dict.get("FS"), parameters.xref);
     this.data.annotationType = _util.AnnotationType.FILEATTACHMENT;
     this.data.file = file.serializable;
   }

+ 116 - 0
lib/core/ascii_85_stream.js

@@ -0,0 +1,116 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.Ascii85Stream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+var _core_utils = require("./core_utils.js");
+
+class Ascii85Stream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength) {
+    if (maybeLength) {
+      maybeLength = 0.8 * maybeLength;
+    }
+
+    super(maybeLength);
+    this.str = str;
+    this.dict = str.dict;
+    this.input = new Uint8Array(5);
+  }
+
+  readBlock() {
+    const TILDA_CHAR = 0x7e;
+    const Z_LOWER_CHAR = 0x7a;
+    const EOF = -1;
+    const str = this.str;
+    let c = str.getByte();
+
+    while ((0, _core_utils.isWhiteSpace)(c)) {
+      c = str.getByte();
+    }
+
+    if (c === EOF || c === TILDA_CHAR) {
+      this.eof = true;
+      return;
+    }
+
+    const bufferLength = this.bufferLength;
+    let buffer, i;
+
+    if (c === Z_LOWER_CHAR) {
+      buffer = this.ensureBuffer(bufferLength + 4);
+
+      for (i = 0; i < 4; ++i) {
+        buffer[bufferLength + i] = 0;
+      }
+
+      this.bufferLength += 4;
+    } else {
+      const input = this.input;
+      input[0] = c;
+
+      for (i = 1; i < 5; ++i) {
+        c = str.getByte();
+
+        while ((0, _core_utils.isWhiteSpace)(c)) {
+          c = str.getByte();
+        }
+
+        input[i] = c;
+
+        if (c === EOF || c === TILDA_CHAR) {
+          break;
+        }
+      }
+
+      buffer = this.ensureBuffer(bufferLength + i - 1);
+      this.bufferLength += i - 1;
+
+      if (i < 5) {
+        for (; i < 5; ++i) {
+          input[i] = 0x21 + 84;
+        }
+
+        this.eof = true;
+      }
+
+      let t = 0;
+
+      for (i = 0; i < 5; ++i) {
+        t = t * 85 + (input[i] - 0x21);
+      }
+
+      for (i = 3; i >= 0; --i) {
+        buffer[bufferLength + i] = t & 0xff;
+        t >>= 8;
+      }
+    }
+  }
+
+}
+
+exports.Ascii85Stream = Ascii85Stream;

+ 90 - 0
lib/core/ascii_hex_stream.js

@@ -0,0 +1,90 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.AsciiHexStream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+class AsciiHexStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength) {
+    if (maybeLength) {
+      maybeLength = 0.5 * maybeLength;
+    }
+
+    super(maybeLength);
+    this.str = str;
+    this.dict = str.dict;
+    this.firstDigit = -1;
+  }
+
+  readBlock() {
+    const UPSTREAM_BLOCK_SIZE = 8000;
+    const bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE);
+
+    if (!bytes.length) {
+      this.eof = true;
+      return;
+    }
+
+    const maxDecodeLength = bytes.length + 1 >> 1;
+    const buffer = this.ensureBuffer(this.bufferLength + maxDecodeLength);
+    let bufferLength = this.bufferLength;
+    let firstDigit = this.firstDigit;
+
+    for (const ch of bytes) {
+      let digit;
+
+      if (ch >= 0x30 && ch <= 0x39) {
+        digit = ch & 0x0f;
+      } else if (ch >= 0x41 && ch <= 0x46 || ch >= 0x61 && ch <= 0x66) {
+        digit = (ch & 0x0f) + 9;
+      } else if (ch === 0x3e) {
+        this.eof = true;
+        break;
+      } else {
+        continue;
+      }
+
+      if (firstDigit < 0) {
+        firstDigit = digit;
+      } else {
+        buffer[bufferLength++] = firstDigit << 4 | digit;
+        firstDigit = -1;
+      }
+    }
+
+    if (firstDigit >= 0 && this.eof) {
+      buffer[bufferLength++] = firstDigit << 4;
+      firstDigit = -1;
+    }
+
+    this.firstDigit = firstDigit;
+    this.bufferLength = bufferLength;
+  }
+
+}
+
+exports.AsciiHexStream = AsciiHexStream;

+ 123 - 0
lib/core/base_stream.js

@@ -0,0 +1,123 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.BaseStream = void 0;
+
+var _util = require("../shared/util.js");
+
+class BaseStream {
+  constructor() {
+    if (this.constructor === BaseStream) {
+      (0, _util.unreachable)("Cannot initialize BaseStream.");
+    }
+  }
+
+  get length() {
+    (0, _util.unreachable)("Abstract getter `length` accessed");
+  }
+
+  get isEmpty() {
+    (0, _util.unreachable)("Abstract getter `isEmpty` accessed");
+  }
+
+  get isDataLoaded() {
+    return (0, _util.shadow)(this, "isDataLoaded", true);
+  }
+
+  getByte() {
+    (0, _util.unreachable)("Abstract method `getByte` called");
+  }
+
+  getBytes(length, forceClamped = false) {
+    (0, _util.unreachable)("Abstract method `getBytes` called");
+  }
+
+  peekByte() {
+    const peekedByte = this.getByte();
+
+    if (peekedByte !== -1) {
+      this.pos--;
+    }
+
+    return peekedByte;
+  }
+
+  peekBytes(length, forceClamped = false) {
+    const bytes = this.getBytes(length, forceClamped);
+    this.pos -= bytes.length;
+    return bytes;
+  }
+
+  getUint16() {
+    const b0 = this.getByte();
+    const b1 = this.getByte();
+
+    if (b0 === -1 || b1 === -1) {
+      return -1;
+    }
+
+    return (b0 << 8) + b1;
+  }
+
+  getInt32() {
+    const b0 = this.getByte();
+    const b1 = this.getByte();
+    const b2 = this.getByte();
+    const b3 = this.getByte();
+    return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
+  }
+
+  getByteRange(begin, end) {
+    (0, _util.unreachable)("Abstract method `getByteRange` called");
+  }
+
+  getString(length) {
+    return (0, _util.bytesToString)(this.getBytes(length, false));
+  }
+
+  skip(n) {
+    this.pos += n || 1;
+  }
+
+  reset() {
+    (0, _util.unreachable)("Abstract method `reset` called");
+  }
+
+  moveStart() {
+    (0, _util.unreachable)("Abstract method `moveStart` called");
+  }
+
+  makeSubStream(start, length, dict = null) {
+    (0, _util.unreachable)("Abstract method `makeSubStream` called");
+  }
+
+  getBaseStreams() {
+    return null;
+  }
+
+}
+
+exports.BaseStream = BaseStream;

+ 32 - 32
lib/core/bidi.js

@@ -28,8 +28,8 @@ exports.bidi = bidi;
 
 var _util = require("../shared/util.js");
 
-var baseTypes = ["BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", "B", "B", "S", "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ES", "CS", "ES", "CS", "CS", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "BN", "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", "BN", "ON", "ON", "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L"];
-var arabicTypes = ["AN", "AN", "AN", "AN", "AN", "AN", "ON", "ON", "AL", "ET", "ET", "AL", "CS", "AL", "ON", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", "AL", "AL", "AL", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "NSM", "NSM", "ON", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "AL", "AL", "AL", "AL", "AL", "AL"];
+const baseTypes = ["BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", "B", "B", "S", "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ES", "CS", "ES", "CS", "CS", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "CS", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", "BN", "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", "BN", "ON", "ON", "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", "ON", "ON", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", "L"];
+const arabicTypes = ["AN", "AN", "AN", "AN", "AN", "AN", "ON", "ON", "AL", "ET", "ET", "AL", "CS", "AL", "ON", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", "AL", "AL", "AL", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AN", "ON", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "NSM", "NSM", "ON", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "AL", "AL", "AL", "AL", "AL", "AL"];
 
 function isOdd(i) {
   return (i & 1) !== 0;
@@ -40,7 +40,9 @@ function isEven(i) {
 }
 
 function findUnequal(arr, start, value) {
-  for (var j = start, jj = arr.length; j < jj; ++j) {
+  let j, jj;
+
+  for (j = start, jj = arr.length; j < jj; ++j) {
     if (arr[j] !== value) {
       return j;
     }
@@ -50,14 +52,14 @@ function findUnequal(arr, start, value) {
 }
 
 function setValues(arr, start, end, value) {
-  for (var j = start; j < end; ++j) {
+  for (let j = start; j < end; ++j) {
     arr[j] = value;
   }
 }
 
 function reverseValues(arr, start, end) {
-  for (var i = start, j = end - 1; i < j; ++i, --j) {
-    var temp = arr[i];
+  for (let i = start, j = end - 1; i < j; ++i, --j) {
+    const temp = arr[i];
     arr[i] = arr[j];
     arr[j] = temp;
   }
@@ -78,12 +80,12 @@ function createBidiText(str, isLTR, vertical = false) {
   };
 }
 
-var chars = [];
-var types = [];
+const chars = [];
+const types = [];
 
 function bidi(str, startLevel, vertical) {
-  var isLTR = true;
-  var strLength = str.length;
+  let isLTR = true;
+  const strLength = str.length;
 
   if (strLength === 0 || vertical) {
     return createBidiText(str, isLTR, vertical);
@@ -91,13 +93,13 @@ function bidi(str, startLevel, vertical) {
 
   chars.length = strLength;
   types.length = strLength;
-  var numBidi = 0;
-  var i, ii;
+  let numBidi = 0;
+  let i, ii;
 
   for (i = 0; i < strLength; ++i) {
     chars[i] = str.charAt(i);
-    var charCode = str.charCodeAt(i);
-    var charType = "L";
+    const charCode = str.charCodeAt(i);
+    let charType = "L";
 
     if (charCode <= 0x00ff) {
       charType = baseTypes[charCode];
@@ -135,16 +137,16 @@ function bidi(str, startLevel, vertical) {
     }
   }
 
-  var levels = [];
+  const levels = [];
 
   for (i = 0; i < strLength; ++i) {
     levels[i] = startLevel;
   }
 
-  var e = isOdd(startLevel) ? "R" : "L";
-  var sor = e;
-  var eor = sor;
-  var lastType = sor;
+  const e = isOdd(startLevel) ? "R" : "L";
+  const sor = e;
+  const eor = sor;
+  let lastType = sor;
 
   for (i = 0; i < strLength; ++i) {
     if (types[i] === "NSM") {
@@ -155,7 +157,7 @@ function bidi(str, startLevel, vertical) {
   }
 
   lastType = sor;
-  var t;
+  let t;
 
   for (i = 0; i < strLength; ++i) {
     t = types[i];
@@ -187,9 +189,7 @@ function bidi(str, startLevel, vertical) {
 
   for (i = 0; i < strLength; ++i) {
     if (types[i] === "EN") {
-      var j;
-
-      for (j = i - 1; j >= 0; --j) {
+      for (let j = i - 1; j >= 0; --j) {
         if (types[j] !== "ET") {
           break;
         }
@@ -197,7 +197,7 @@ function bidi(str, startLevel, vertical) {
         types[j] = "EN";
       }
 
-      for (j = i + 1; j < strLength; ++j) {
+      for (let j = i + 1; j < strLength; ++j) {
         if (types[j] !== "ET") {
           break;
         }
@@ -229,14 +229,14 @@ function bidi(str, startLevel, vertical) {
 
   for (i = 0; i < strLength; ++i) {
     if (types[i] === "ON") {
-      var end = findUnequal(types, i + 1, "ON");
-      var before = sor;
+      const end = findUnequal(types, i + 1, "ON");
+      let before = sor;
 
       if (i > 0) {
         before = types[i - 1];
       }
 
-      var after = eor;
+      let after = eor;
 
       if (end + 1 < strLength) {
         after = types[end + 1];
@@ -280,9 +280,9 @@ function bidi(str, startLevel, vertical) {
     }
   }
 
-  var highestLevel = -1;
-  var lowestOddLevel = 99;
-  var level;
+  let highestLevel = -1;
+  let lowestOddLevel = 99;
+  let level;
 
   for (i = 0, ii = levels.length; i < ii; ++i) {
     level = levels[i];
@@ -297,7 +297,7 @@ function bidi(str, startLevel, vertical) {
   }
 
   for (level = highestLevel; level >= lowestOddLevel; --level) {
-    var start = -1;
+    let start = -1;
 
     for (i = 0, ii = levels.length; i < ii; ++i) {
       if (levels[i] < level) {
@@ -316,7 +316,7 @@ function bidi(str, startLevel, vertical) {
   }
 
   for (i = 0, ii = chars.length; i < ii; ++i) {
-    var ch = chars[i];
+    const ch = chars[i];
 
     if (ch === "<" || ch === ">") {
       chars[i] = "";

+ 119 - 1228
lib/core/obj.js → lib/core/catalog.js

@@ -24,26 +24,32 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.XRef = exports.ObjectLoader = exports.FileSpec = exports.Catalog = void 0;
-
-var _util = require("../shared/util.js");
+exports.Catalog = void 0;
 
 var _primitives = require("./primitives.js");
 
 var _core_utils = require("./core_utils.js");
 
-var _parser = require("./parser.js");
+var _util = require("../shared/util.js");
 
-var _crypto = require("./crypto.js");
+var _name_number_tree = require("./name_number_tree.js");
 
 var _colorspace = require("./colorspace.js");
 
+var _file_spec = require("./file_spec.js");
+
 var _image_utils = require("./image_utils.js");
 
 var _metadata_parser = require("./metadata_parser.js");
 
+var _struct_tree = require("./struct_tree.js");
+
 function fetchDestination(dest) {
-  return (0, _primitives.isDict)(dest) ? dest.get("D") : dest;
+  if (dest instanceof _primitives.Dict) {
+    dest = dest.get("D");
+  }
+
+  return Array.isArray(dest) ? dest : null;
 }
 
 class Catalog {
@@ -60,6 +66,7 @@ class Catalog {
     this.builtInCMapCache = new Map();
     this.globalImageCache = new _image_utils.GlobalImageCache();
     this.pageKidsCountCache = new _primitives.RefSetCache();
+    this.pageIndexCache = new _primitives.RefSetCache();
     this.nonBlendModesSet = new _primitives.RefSet();
   }
 
@@ -130,7 +137,7 @@ class Catalog {
 
       if ((0, _primitives.isName)(type, "Metadata") && (0, _primitives.isName)(subtype, "XML")) {
         try {
-          const data = (0, _util.stringToUTF8String)((0, _util.bytesToString)(stream.getBytes()));
+          const data = (0, _util.stringToUTF8String)(stream.getString());
 
           if (data) {
             metadata = new _metadata_parser.MetadataParser(data).serializable;
@@ -194,6 +201,34 @@ class Catalog {
     return markInfo;
   }
 
+  get structTreeRoot() {
+    let structTree = null;
+
+    try {
+      structTree = this._readStructTreeRoot();
+    } catch (ex) {
+      if (ex instanceof _core_utils.MissingDataException) {
+        throw ex;
+      }
+
+      (0, _util.warn)("Unable read to structTreeRoot info.");
+    }
+
+    return (0, _util.shadow)(this, "structTreeRoot", structTree);
+  }
+
+  _readStructTreeRoot() {
+    const obj = this._catDict.get("StructTreeRoot");
+
+    if (!(0, _primitives.isDict)(obj)) {
+      return null;
+    }
+
+    const root = new _struct_tree.StructTreeRoot(obj);
+    root.init();
+    return root;
+  }
+
   get toplevelPagesDict() {
     const pagesObj = this._catDict.get("Pages");
 
@@ -530,16 +565,20 @@ class Catalog {
     const obj = this._readDests(),
           dests = Object.create(null);
 
-    if (obj instanceof NameTree) {
-      const names = obj.getAll();
+    if (obj instanceof _name_number_tree.NameTree) {
+      for (const [key, value] of obj.getAll()) {
+        const dest = fetchDestination(value);
 
-      for (const name in names) {
-        dests[name] = fetchDestination(names[name]);
+        if (dest) {
+          dests[key] = dest;
+        }
       }
     } else if (obj instanceof _primitives.Dict) {
       obj.forEach(function (key, value) {
-        if (value) {
-          dests[key] = fetchDestination(value);
+        const dest = fetchDestination(value);
+
+        if (dest) {
+          dests[key] = dest;
         }
       });
     }
@@ -547,11 +586,28 @@ class Catalog {
     return (0, _util.shadow)(this, "destinations", dests);
   }
 
-  getDestination(destinationId) {
+  getDestination(id) {
     const obj = this._readDests();
 
-    if (obj instanceof NameTree || obj instanceof _primitives.Dict) {
-      return fetchDestination(obj.get(destinationId) || null);
+    if (obj instanceof _name_number_tree.NameTree) {
+      const dest = fetchDestination(obj.get(id));
+
+      if (dest) {
+        return dest;
+      }
+
+      const allDest = this.destinations[id];
+
+      if (allDest) {
+        (0, _util.warn)(`Found "${id}" at an incorrect position in the NameTree.`);
+        return allDest;
+      }
+    } else if (obj instanceof _primitives.Dict) {
+      const dest = fetchDestination(obj.get(id));
+
+      if (dest) {
+        return dest;
+      }
     }
 
     return null;
@@ -561,7 +617,7 @@ class Catalog {
     const obj = this._catDict.get("Names");
 
     if (obj && obj.has("Dests")) {
-      return new NameTree(obj.getRaw("Dests"), this.xref);
+      return new _name_number_tree.NameTree(obj.getRaw("Dests"), this.xref);
     } else if (this._catDict.has("Dests")) {
       return this._catDict.get("Dests");
     }
@@ -595,15 +651,15 @@ class Catalog {
     const pageLabels = new Array(this.numPages);
     let style = null,
         prefix = "";
-    const numberTree = new NumberTree(obj, this.xref);
+    const numberTree = new _name_number_tree.NumberTree(obj, this.xref);
     const nums = numberTree.getAll();
     let currentLabel = "",
         currentIndex = 1;
 
     for (let i = 0, ii = this.numPages; i < ii; i++) {
-      if (i in nums) {
-        const labelDict = nums[i];
+      const labelDict = nums.get(i);
 
+      if (labelDict !== undefined) {
         if (!(0, _primitives.isDict)(labelDict)) {
           throw new _util.FormatError("PageLabel is not a dictionary.");
         }
@@ -928,18 +984,17 @@ class Catalog {
 
     let attachments = null;
 
-    if (obj && obj.has("EmbeddedFiles")) {
-      const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
-      const names = nameTree.getAll();
+    if (obj instanceof _primitives.Dict && obj.has("EmbeddedFiles")) {
+      const nameTree = new _name_number_tree.NameTree(obj.getRaw("EmbeddedFiles"), this.xref);
 
-      for (const name in names) {
-        const fs = new FileSpec(names[name], this.xref);
+      for (const [key, value] of nameTree.getAll()) {
+        const fs = new _file_spec.FileSpec(value, this.xref);
 
         if (!attachments) {
           attachments = Object.create(null);
         }
 
-        attachments[(0, _util.stringToPDFString)(name)] = fs.serializable;
+        attachments[(0, _util.stringToPDFString)(key)] = fs.serializable;
       }
     }
 
@@ -952,43 +1007,40 @@ class Catalog {
     let javaScript = null;
 
     function appendIfJavaScriptDict(name, jsDict) {
-      const type = jsDict.get("S");
+      if (!(jsDict instanceof _primitives.Dict)) {
+        return;
+      }
 
-      if (!(0, _primitives.isName)(type, "JavaScript")) {
+      if (!(0, _primitives.isName)(jsDict.get("S"), "JavaScript")) {
         return;
       }
 
       let js = jsDict.get("JS");
 
       if ((0, _primitives.isStream)(js)) {
-        js = (0, _util.bytesToString)(js.getBytes());
-      } else if (!(0, _util.isString)(js)) {
+        js = js.getString();
+      } else if (typeof js !== "string") {
         return;
       }
 
       if (javaScript === null) {
-        javaScript = Object.create(null);
+        javaScript = new Map();
       }
 
-      javaScript[name] = (0, _util.stringToPDFString)(js);
+      javaScript.set(name, (0, _util.stringToPDFString)(js));
     }
 
-    if (obj && obj.has("JavaScript")) {
-      const nameTree = new NameTree(obj.getRaw("JavaScript"), this.xref);
-      const names = nameTree.getAll();
-
-      for (const name in names) {
-        const jsDict = names[name];
+    if (obj instanceof _primitives.Dict && obj.has("JavaScript")) {
+      const nameTree = new _name_number_tree.NameTree(obj.getRaw("JavaScript"), this.xref);
 
-        if ((0, _primitives.isDict)(jsDict)) {
-          appendIfJavaScriptDict(name, jsDict);
-        }
+      for (const [key, value] of nameTree.getAll()) {
+        appendIfJavaScriptDict(key, value);
       }
     }
 
     const openAction = this._catDict.get("OpenAction");
 
-    if ((0, _primitives.isDict)(openAction) && (0, _primitives.isName)(openAction.get("S"), "JavaScript")) {
+    if (openAction) {
       appendIfJavaScriptDict("OpenAction", openAction);
     }
 
@@ -998,20 +1050,20 @@ class Catalog {
   get javaScript() {
     const javaScript = this._collectJavaScript();
 
-    return (0, _util.shadow)(this, "javaScript", javaScript ? Object.values(javaScript) : null);
+    return (0, _util.shadow)(this, "javaScript", javaScript ? [...javaScript.values()] : null);
   }
 
   get jsActions() {
-    const js = this._collectJavaScript();
+    const javaScript = this._collectJavaScript();
 
     let actions = (0, _core_utils.collectActions)(this.xref, this._catDict, _util.DocumentActionEventType);
 
-    if (!actions && js) {
-      actions = Object.create(null);
-    }
+    if (javaScript) {
+      if (!actions) {
+        actions = Object.create(null);
+      }
 
-    if (actions && js) {
-      for (const [key, val] of Object.entries(js)) {
+      for (const [key, val] of javaScript) {
         if (key in actions) {
           actions[key].push(val);
         } else {
@@ -1042,6 +1094,7 @@ class Catalog {
     (0, _primitives.clearPrimitiveCaches)();
     this.globalImageCache.clear(manuallyTriggered);
     this.pageKidsCountCache.clear();
+    this.pageIndexCache.clear();
     this.nonBlendModesSet.clear();
     const promises = [];
     this.fontCache.forEach(function (promise) {
@@ -1158,6 +1211,12 @@ class Catalog {
   }
 
   getPageIndex(pageRef) {
+    const cachedPageIndex = this.pageIndexCache.get(pageRef);
+
+    if (cachedPageIndex !== undefined) {
+      return Promise.resolve(cachedPageIndex);
+    }
+
     const xref = this.xref;
 
     function pagesBeforeRef(kidRef) {
@@ -1233,17 +1292,16 @@ class Catalog {
 
     let total = 0;
 
-    function next(ref) {
-      return pagesBeforeRef(ref).then(function (args) {
-        if (!args) {
-          return total;
-        }
+    const next = ref => pagesBeforeRef(ref).then(args => {
+      if (!args) {
+        this.pageIndexCache.put(pageRef, total);
+        return total;
+      }
 
-        const [count, parentRef] = args;
-        total += count;
-        return next(parentRef);
-      });
-    }
+      const [count, parentRef] = args;
+      total += count;
+      return next(parentRef);
+    });
 
     return next(pageRef);
   }
@@ -1372,7 +1430,7 @@ class Catalog {
           let js;
 
           if ((0, _primitives.isStream)(jsAction)) {
-            js = (0, _util.bytesToString)(jsAction.getBytes());
+            js = jsAction.getString();
           } else if ((0, _util.isString)(jsAction)) {
             js = jsAction;
           }
@@ -1429,1171 +1487,4 @@ class Catalog {
 
 }
 
-exports.Catalog = Catalog;
-
-var XRef = function XRefClosure() {
-  function XRef(stream, pdfManager) {
-    this.stream = stream;
-    this.pdfManager = pdfManager;
-    this.entries = [];
-    this.xrefstms = Object.create(null);
-    this._cacheMap = new Map();
-    this.stats = {
-      streamTypes: Object.create(null),
-      fontTypes: Object.create(null)
-    };
-    this._newRefNum = null;
-  }
-
-  XRef.prototype = {
-    getNewRef: function XRef_getNewRef() {
-      if (this._newRefNum === null) {
-        this._newRefNum = this.entries.length;
-      }
-
-      return _primitives.Ref.get(this._newRefNum++, 0);
-    },
-    resetNewRef: function XRef_resetNewRef() {
-      this._newRefNum = null;
-    },
-    setStartXRef: function XRef_setStartXRef(startXRef) {
-      this.startXRefQueue = [startXRef];
-    },
-    parse: function XRef_parse(recoveryMode) {
-      var trailerDict;
-
-      if (!recoveryMode) {
-        trailerDict = this.readXRef();
-      } else {
-        (0, _util.warn)("Indexing all PDF objects");
-        trailerDict = this.indexObjects();
-      }
-
-      trailerDict.assignXref(this);
-      this.trailer = trailerDict;
-      let encrypt;
-
-      try {
-        encrypt = trailerDict.get("Encrypt");
-      } catch (ex) {
-        if (ex instanceof _core_utils.MissingDataException) {
-          throw ex;
-        }
-
-        (0, _util.warn)(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
-      }
-
-      if ((0, _primitives.isDict)(encrypt)) {
-        var ids = trailerDict.get("ID");
-        var fileId = ids && ids.length ? ids[0] : "";
-        encrypt.suppressEncryption = true;
-        this.encrypt = new _crypto.CipherTransformFactory(encrypt, fileId, this.pdfManager.password);
-      }
-
-      let root;
-
-      try {
-        root = trailerDict.get("Root");
-      } catch (ex) {
-        if (ex instanceof _core_utils.MissingDataException) {
-          throw ex;
-        }
-
-        (0, _util.warn)(`XRef.parse - Invalid "Root" reference: "${ex}".`);
-      }
-
-      if ((0, _primitives.isDict)(root) && root.has("Pages")) {
-        this.root = root;
-      } else {
-        if (!recoveryMode) {
-          throw new _core_utils.XRefParseException();
-        }
-
-        throw new _util.FormatError("Invalid root reference");
-      }
-    },
-    processXRefTable: function XRef_processXRefTable(parser) {
-      if (!("tableState" in this)) {
-        this.tableState = {
-          entryNum: 0,
-          streamPos: parser.lexer.stream.pos,
-          parserBuf1: parser.buf1,
-          parserBuf2: parser.buf2
-        };
-      }
-
-      var obj = this.readXRefTable(parser);
-
-      if (!(0, _primitives.isCmd)(obj, "trailer")) {
-        throw new _util.FormatError("Invalid XRef table: could not find trailer dictionary");
-      }
-
-      var dict = parser.getObj();
-
-      if (!(0, _primitives.isDict)(dict) && dict.dict) {
-        dict = dict.dict;
-      }
-
-      if (!(0, _primitives.isDict)(dict)) {
-        throw new _util.FormatError("Invalid XRef table: could not parse trailer dictionary");
-      }
-
-      delete this.tableState;
-      return dict;
-    },
-    readXRefTable: function XRef_readXRefTable(parser) {
-      var stream = parser.lexer.stream;
-      var tableState = this.tableState;
-      stream.pos = tableState.streamPos;
-      parser.buf1 = tableState.parserBuf1;
-      parser.buf2 = tableState.parserBuf2;
-      var obj;
-
-      while (true) {
-        if (!("firstEntryNum" in tableState) || !("entryCount" in tableState)) {
-          if ((0, _primitives.isCmd)(obj = parser.getObj(), "trailer")) {
-            break;
-          }
-
-          tableState.firstEntryNum = obj;
-          tableState.entryCount = parser.getObj();
-        }
-
-        var first = tableState.firstEntryNum;
-        var count = tableState.entryCount;
-
-        if (!Number.isInteger(first) || !Number.isInteger(count)) {
-          throw new _util.FormatError("Invalid XRef table: wrong types in subsection header");
-        }
-
-        for (var i = tableState.entryNum; i < count; i++) {
-          tableState.streamPos = stream.pos;
-          tableState.entryNum = i;
-          tableState.parserBuf1 = parser.buf1;
-          tableState.parserBuf2 = parser.buf2;
-          var entry = {};
-          entry.offset = parser.getObj();
-          entry.gen = parser.getObj();
-          var type = parser.getObj();
-
-          if (type instanceof _primitives.Cmd) {
-            switch (type.cmd) {
-              case "f":
-                entry.free = true;
-                break;
-
-              case "n":
-                entry.uncompressed = true;
-                break;
-            }
-          }
-
-          if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {
-            throw new _util.FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`);
-          }
-
-          if (i === 0 && entry.free && first === 1) {
-            first = 0;
-          }
-
-          if (!this.entries[i + first]) {
-            this.entries[i + first] = entry;
-          }
-        }
-
-        tableState.entryNum = 0;
-        tableState.streamPos = stream.pos;
-        tableState.parserBuf1 = parser.buf1;
-        tableState.parserBuf2 = parser.buf2;
-        delete tableState.firstEntryNum;
-        delete tableState.entryCount;
-      }
-
-      if (this.entries[0] && !this.entries[0].free) {
-        throw new _util.FormatError("Invalid XRef table: unexpected first object");
-      }
-
-      return obj;
-    },
-    processXRefStream: function XRef_processXRefStream(stream) {
-      if (!("streamState" in this)) {
-        var streamParameters = stream.dict;
-        var byteWidths = streamParameters.get("W");
-        var range = streamParameters.get("Index");
-
-        if (!range) {
-          range = [0, streamParameters.get("Size")];
-        }
-
-        this.streamState = {
-          entryRanges: range,
-          byteWidths,
-          entryNum: 0,
-          streamPos: stream.pos
-        };
-      }
-
-      this.readXRefStream(stream);
-      delete this.streamState;
-      return stream.dict;
-    },
-    readXRefStream: function XRef_readXRefStream(stream) {
-      var i, j;
-      var streamState = this.streamState;
-      stream.pos = streamState.streamPos;
-      var byteWidths = streamState.byteWidths;
-      var typeFieldWidth = byteWidths[0];
-      var offsetFieldWidth = byteWidths[1];
-      var generationFieldWidth = byteWidths[2];
-      var entryRanges = streamState.entryRanges;
-
-      while (entryRanges.length > 0) {
-        var first = entryRanges[0];
-        var n = entryRanges[1];
-
-        if (!Number.isInteger(first) || !Number.isInteger(n)) {
-          throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
-        }
-
-        if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {
-          throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
-        }
-
-        for (i = streamState.entryNum; i < n; ++i) {
-          streamState.entryNum = i;
-          streamState.streamPos = stream.pos;
-          var type = 0,
-              offset = 0,
-              generation = 0;
-
-          for (j = 0; j < typeFieldWidth; ++j) {
-            type = type << 8 | stream.getByte();
-          }
-
-          if (typeFieldWidth === 0) {
-            type = 1;
-          }
-
-          for (j = 0; j < offsetFieldWidth; ++j) {
-            offset = offset << 8 | stream.getByte();
-          }
-
-          for (j = 0; j < generationFieldWidth; ++j) {
-            generation = generation << 8 | stream.getByte();
-          }
-
-          var entry = {};
-          entry.offset = offset;
-          entry.gen = generation;
-
-          switch (type) {
-            case 0:
-              entry.free = true;
-              break;
-
-            case 1:
-              entry.uncompressed = true;
-              break;
-
-            case 2:
-              break;
-
-            default:
-              throw new _util.FormatError(`Invalid XRef entry type: ${type}`);
-          }
-
-          if (!this.entries[first + i]) {
-            this.entries[first + i] = entry;
-          }
-        }
-
-        streamState.entryNum = 0;
-        streamState.streamPos = stream.pos;
-        entryRanges.splice(0, 2);
-      }
-    },
-    indexObjects: function XRef_indexObjects() {
-      var TAB = 0x9,
-          LF = 0xa,
-          CR = 0xd,
-          SPACE = 0x20;
-      var PERCENT = 0x25,
-          LT = 0x3c;
-
-      function readToken(data, offset) {
-        var token = "",
-            ch = data[offset];
-
-        while (ch !== LF && ch !== CR && ch !== LT) {
-          if (++offset >= data.length) {
-            break;
-          }
-
-          token += String.fromCharCode(ch);
-          ch = data[offset];
-        }
-
-        return token;
-      }
-
-      function skipUntil(data, offset, what) {
-        var length = what.length,
-            dataLength = data.length;
-        var skipped = 0;
-
-        while (offset < dataLength) {
-          var i = 0;
-
-          while (i < length && data[offset + i] === what[i]) {
-            ++i;
-          }
-
-          if (i >= length) {
-            break;
-          }
-
-          offset++;
-          skipped++;
-        }
-
-        return skipped;
-      }
-
-      var objRegExp = /^(\d+)\s+(\d+)\s+obj\b/;
-      const endobjRegExp = /\bendobj[\b\s]$/;
-      const nestedObjRegExp = /\s+(\d+\s+\d+\s+obj[\b\s<])$/;
-      const CHECK_CONTENT_LENGTH = 25;
-      var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
-      var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);
-      const objBytes = new Uint8Array([111, 98, 106]);
-      var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
-      this.entries.length = 0;
-      var stream = this.stream;
-      stream.pos = 0;
-      var buffer = stream.getBytes();
-      var position = stream.start,
-          length = buffer.length;
-      var trailers = [],
-          xrefStms = [];
-
-      while (position < length) {
-        var ch = buffer[position];
-
-        if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {
-          ++position;
-          continue;
-        }
-
-        if (ch === PERCENT) {
-          do {
-            ++position;
-
-            if (position >= length) {
-              break;
-            }
-
-            ch = buffer[position];
-          } while (ch !== LF && ch !== CR);
-
-          continue;
-        }
-
-        var token = readToken(buffer, position);
-        var m;
-
-        if (token.startsWith("xref") && (token.length === 4 || /\s/.test(token[4]))) {
-          position += skipUntil(buffer, position, trailerBytes);
-          trailers.push(position);
-          position += skipUntil(buffer, position, startxrefBytes);
-        } else if (m = objRegExp.exec(token)) {
-          const num = m[1] | 0,
-                gen = m[2] | 0;
-
-          if (!this.entries[num] || this.entries[num].gen === gen) {
-            this.entries[num] = {
-              offset: position - stream.start,
-              gen,
-              uncompressed: true
-            };
-          }
-
-          let contentLength,
-              startPos = position + token.length;
-
-          while (startPos < buffer.length) {
-            const endPos = startPos + skipUntil(buffer, startPos, objBytes) + 4;
-            contentLength = endPos - position;
-            const checkPos = Math.max(endPos - CHECK_CONTENT_LENGTH, startPos);
-            const tokenStr = (0, _util.bytesToString)(buffer.subarray(checkPos, endPos));
-
-            if (endobjRegExp.test(tokenStr)) {
-              break;
-            } else {
-              const objToken = nestedObjRegExp.exec(tokenStr);
-
-              if (objToken && objToken[1]) {
-                (0, _util.warn)('indexObjects: Found new "obj" inside of another "obj", ' + 'caused by missing "endobj" -- trying to recover.');
-                contentLength -= objToken[1].length;
-                break;
-              }
-            }
-
-            startPos = endPos;
-          }
-
-          const content = buffer.subarray(position, position + contentLength);
-          var xrefTagOffset = skipUntil(content, 0, xrefBytes);
-
-          if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {
-            xrefStms.push(position - stream.start);
-            this.xrefstms[position - stream.start] = 1;
-          }
-
-          position += contentLength;
-        } else if (token.startsWith("trailer") && (token.length === 7 || /\s/.test(token[7]))) {
-          trailers.push(position);
-          position += skipUntil(buffer, position, startxrefBytes);
-        } else {
-          position += token.length + 1;
-        }
-      }
-
-      for (let i = 0, ii = xrefStms.length; i < ii; ++i) {
-        this.startXRefQueue.push(xrefStms[i]);
-        this.readXRef(true);
-      }
-
-      let trailerDict;
-
-      for (let i = 0, ii = trailers.length; i < ii; ++i) {
-        stream.pos = trailers[i];
-        const parser = new _parser.Parser({
-          lexer: new _parser.Lexer(stream),
-          xref: this,
-          allowStreams: true,
-          recoveryMode: true
-        });
-        var obj = parser.getObj();
-
-        if (!(0, _primitives.isCmd)(obj, "trailer")) {
-          continue;
-        }
-
-        const dict = parser.getObj();
-
-        if (!(0, _primitives.isDict)(dict)) {
-          continue;
-        }
-
-        try {
-          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) {
-          continue;
-        }
-
-        if (dict.has("ID")) {
-          return dict;
-        }
-
-        trailerDict = dict;
-      }
-
-      if (trailerDict) {
-        return trailerDict;
-      }
-
-      throw new _util.InvalidPDFException("Invalid PDF structure.");
-    },
-    readXRef: function XRef_readXRef(recoveryMode) {
-      var stream = this.stream;
-      const startXRefParsedCache = Object.create(null);
-
-      try {
-        while (this.startXRefQueue.length) {
-          var startXRef = this.startXRefQueue[0];
-
-          if (startXRefParsedCache[startXRef]) {
-            (0, _util.warn)("readXRef - skipping XRef table since it was already parsed.");
-            this.startXRefQueue.shift();
-            continue;
-          }
-
-          startXRefParsedCache[startXRef] = true;
-          stream.pos = startXRef + stream.start;
-          const parser = new _parser.Parser({
-            lexer: new _parser.Lexer(stream),
-            xref: this,
-            allowStreams: true
-          });
-          var obj = parser.getObj();
-          var dict;
-
-          if ((0, _primitives.isCmd)(obj, "xref")) {
-            dict = this.processXRefTable(parser);
-
-            if (!this.topDict) {
-              this.topDict = dict;
-            }
-
-            obj = dict.get("XRefStm");
-
-            if (Number.isInteger(obj)) {
-              var pos = obj;
-
-              if (!(pos in this.xrefstms)) {
-                this.xrefstms[pos] = 1;
-                this.startXRefQueue.push(pos);
-              }
-            }
-          } else if (Number.isInteger(obj)) {
-            if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), "obj") || !(0, _primitives.isStream)(obj = parser.getObj())) {
-              throw new _util.FormatError("Invalid XRef stream");
-            }
-
-            dict = this.processXRefStream(obj);
-
-            if (!this.topDict) {
-              this.topDict = dict;
-            }
-
-            if (!dict) {
-              throw new _util.FormatError("Failed to read XRef stream");
-            }
-          } else {
-            throw new _util.FormatError("Invalid XRef stream header");
-          }
-
-          obj = dict.get("Prev");
-
-          if (Number.isInteger(obj)) {
-            this.startXRefQueue.push(obj);
-          } else if ((0, _primitives.isRef)(obj)) {
-            this.startXRefQueue.push(obj.num);
-          }
-
-          this.startXRefQueue.shift();
-        }
-
-        return this.topDict;
-      } catch (e) {
-        if (e instanceof _core_utils.MissingDataException) {
-          throw e;
-        }
-
-        (0, _util.info)("(while reading XRef): " + e);
-      }
-
-      if (recoveryMode) {
-        return undefined;
-      }
-
-      throw new _core_utils.XRefParseException();
-    },
-    getEntry: function XRef_getEntry(i) {
-      var xrefEntry = this.entries[i];
-
-      if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
-        return xrefEntry;
-      }
-
-      return null;
-    },
-    fetchIfRef: function XRef_fetchIfRef(obj, suppressEncryption) {
-      if (obj instanceof _primitives.Ref) {
-        return this.fetch(obj, suppressEncryption);
-      }
-
-      return obj;
-    },
-    fetch: function XRef_fetch(ref, suppressEncryption) {
-      if (!(ref instanceof _primitives.Ref)) {
-        throw new Error("ref object is not a reference");
-      }
-
-      const num = ref.num;
-
-      const cacheEntry = this._cacheMap.get(num);
-
-      if (cacheEntry !== undefined) {
-        if (cacheEntry instanceof _primitives.Dict && !cacheEntry.objId) {
-          cacheEntry.objId = ref.toString();
-        }
-
-        return cacheEntry;
-      }
-
-      let xrefEntry = this.getEntry(num);
-
-      if (xrefEntry === null) {
-        this._cacheMap.set(num, xrefEntry);
-
-        return xrefEntry;
-      }
-
-      if (xrefEntry.uncompressed) {
-        xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
-      } else {
-        xrefEntry = this.fetchCompressed(ref, xrefEntry, suppressEncryption);
-      }
-
-      if ((0, _primitives.isDict)(xrefEntry)) {
-        xrefEntry.objId = ref.toString();
-      } else if ((0, _primitives.isStream)(xrefEntry)) {
-        xrefEntry.dict.objId = ref.toString();
-      }
-
-      return xrefEntry;
-    },
-
-    fetchUncompressed(ref, xrefEntry, suppressEncryption = false) {
-      var gen = ref.gen;
-      var num = ref.num;
-
-      if (xrefEntry.gen !== gen) {
-        throw new _core_utils.XRefEntryException(`Inconsistent generation in XRef: ${ref}`);
-      }
-
-      var stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start);
-      const parser = new _parser.Parser({
-        lexer: new _parser.Lexer(stream),
-        xref: this,
-        allowStreams: true
-      });
-      var obj1 = parser.getObj();
-      var obj2 = parser.getObj();
-      var obj3 = parser.getObj();
-
-      if (obj1 !== num || obj2 !== gen || !(obj3 instanceof _primitives.Cmd)) {
-        throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
-      }
-
-      if (obj3.cmd !== "obj") {
-        if (obj3.cmd.startsWith("obj")) {
-          num = parseInt(obj3.cmd.substring(3), 10);
-
-          if (!Number.isNaN(num)) {
-            return num;
-          }
-        }
-
-        throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
-      }
-
-      if (this.encrypt && !suppressEncryption) {
-        xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen));
-      } else {
-        xrefEntry = parser.getObj();
-      }
-
-      if (!(0, _primitives.isStream)(xrefEntry)) {
-        this._cacheMap.set(num, xrefEntry);
-      }
-
-      return xrefEntry;
-    },
-
-    fetchCompressed(ref, xrefEntry, suppressEncryption = false) {
-      const tableOffset = xrefEntry.offset;
-      const stream = this.fetch(_primitives.Ref.get(tableOffset, 0));
-
-      if (!(0, _primitives.isStream)(stream)) {
-        throw new _util.FormatError("bad ObjStm stream");
-      }
-
-      const first = stream.dict.get("First");
-      const n = stream.dict.get("N");
-
-      if (!Number.isInteger(first) || !Number.isInteger(n)) {
-        throw new _util.FormatError("invalid first and n parameters for ObjStm stream");
-      }
-
-      let parser = new _parser.Parser({
-        lexer: new _parser.Lexer(stream),
-        xref: this,
-        allowStreams: true
-      });
-      const nums = new Array(n);
-      const offsets = new Array(n);
-
-      for (let i = 0; i < n; ++i) {
-        const num = parser.getObj();
-
-        if (!Number.isInteger(num)) {
-          throw new _util.FormatError(`invalid object number in the ObjStm stream: ${num}`);
-        }
-
-        const offset = parser.getObj();
-
-        if (!Number.isInteger(offset)) {
-          throw new _util.FormatError(`invalid object offset in the ObjStm stream: ${offset}`);
-        }
-
-        nums[i] = num;
-        offsets[i] = offset;
-      }
-
-      const start = (stream.start || 0) + first;
-      const entries = new Array(n);
-
-      for (let i = 0; i < n; ++i) {
-        const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined;
-
-        if (length < 0) {
-          throw new _util.FormatError("Invalid offset in the ObjStm stream.");
-        }
-
-        parser = new _parser.Parser({
-          lexer: new _parser.Lexer(stream.makeSubStream(start + offsets[i], length, stream.dict)),
-          xref: this,
-          allowStreams: true
-        });
-        const obj = parser.getObj();
-        entries[i] = obj;
-
-        if ((0, _primitives.isStream)(obj)) {
-          continue;
-        }
-
-        const num = nums[i],
-              entry = this.entries[num];
-
-        if (entry && entry.offset === tableOffset && entry.gen === i) {
-          this._cacheMap.set(num, obj);
-        }
-      }
-
-      xrefEntry = entries[xrefEntry.gen];
-
-      if (xrefEntry === undefined) {
-        throw new _core_utils.XRefEntryException(`Bad (compressed) XRef entry: ${ref}`);
-      }
-
-      return xrefEntry;
-    },
-
-    async fetchIfRefAsync(obj, suppressEncryption) {
-      if (obj instanceof _primitives.Ref) {
-        return this.fetchAsync(obj, suppressEncryption);
-      }
-
-      return obj;
-    },
-
-    async fetchAsync(ref, suppressEncryption) {
-      try {
-        return this.fetch(ref, suppressEncryption);
-      } catch (ex) {
-        if (!(ex instanceof _core_utils.MissingDataException)) {
-          throw ex;
-        }
-
-        await this.pdfManager.requestRange(ex.begin, ex.end);
-        return this.fetchAsync(ref, suppressEncryption);
-      }
-    },
-
-    getCatalogObj: function XRef_getCatalogObj() {
-      return this.root;
-    }
-  };
-  return XRef;
-}();
-
-exports.XRef = XRef;
-
-class NameOrNumberTree {
-  constructor(root, xref, type) {
-    if (this.constructor === NameOrNumberTree) {
-      (0, _util.unreachable)("Cannot initialize NameOrNumberTree.");
-    }
-
-    this.root = root;
-    this.xref = xref;
-    this._type = type;
-  }
-
-  getAll() {
-    const dict = Object.create(null);
-
-    if (!this.root) {
-      return dict;
-    }
-
-    const xref = this.xref;
-    const processed = new _primitives.RefSet();
-    processed.put(this.root);
-    const queue = [this.root];
-
-    while (queue.length > 0) {
-      const obj = xref.fetchIfRef(queue.shift());
-
-      if (!(0, _primitives.isDict)(obj)) {
-        continue;
-      }
-
-      if (obj.has("Kids")) {
-        const kids = obj.get("Kids");
-
-        for (let i = 0, ii = kids.length; i < ii; i++) {
-          const kid = kids[i];
-
-          if (processed.has(kid)) {
-            throw new _util.FormatError(`Duplicate entry in "${this._type}" tree.`);
-          }
-
-          queue.push(kid);
-          processed.put(kid);
-        }
-
-        continue;
-      }
-
-      const entries = obj.get(this._type);
-
-      if (Array.isArray(entries)) {
-        for (let i = 0, ii = entries.length; i < ii; i += 2) {
-          dict[xref.fetchIfRef(entries[i])] = xref.fetchIfRef(entries[i + 1]);
-        }
-      }
-    }
-
-    return dict;
-  }
-
-  get(key) {
-    if (!this.root) {
-      return null;
-    }
-
-    const xref = this.xref;
-    let kidsOrEntries = xref.fetchIfRef(this.root);
-    let loopCount = 0;
-    const MAX_LEVELS = 10;
-
-    while (kidsOrEntries.has("Kids")) {
-      if (++loopCount > MAX_LEVELS) {
-        (0, _util.warn)(`Search depth limit reached for "${this._type}" tree.`);
-        return null;
-      }
-
-      const kids = kidsOrEntries.get("Kids");
-
-      if (!Array.isArray(kids)) {
-        return null;
-      }
-
-      let l = 0,
-          r = kids.length - 1;
-
-      while (l <= r) {
-        const m = l + r >> 1;
-        const kid = xref.fetchIfRef(kids[m]);
-        const limits = kid.get("Limits");
-
-        if (key < xref.fetchIfRef(limits[0])) {
-          r = m - 1;
-        } else if (key > xref.fetchIfRef(limits[1])) {
-          l = m + 1;
-        } else {
-          kidsOrEntries = xref.fetchIfRef(kids[m]);
-          break;
-        }
-      }
-
-      if (l > r) {
-        return null;
-      }
-    }
-
-    const entries = kidsOrEntries.get(this._type);
-
-    if (Array.isArray(entries)) {
-      let l = 0,
-          r = entries.length - 2;
-
-      while (l <= r) {
-        const tmp = l + r >> 1,
-              m = tmp + (tmp & 1);
-        const currentKey = xref.fetchIfRef(entries[m]);
-
-        if (key < currentKey) {
-          r = m - 2;
-        } else if (key > currentKey) {
-          l = m + 2;
-        } else {
-          return xref.fetchIfRef(entries[m + 1]);
-        }
-      }
-
-      (0, _util.info)(`Falling back to an exhaustive search, for key "${key}", ` + `in "${this._type}" tree.`);
-
-      for (let m = 0, mm = entries.length; m < mm; m += 2) {
-        const currentKey = xref.fetchIfRef(entries[m]);
-
-        if (currentKey === key) {
-          (0, _util.warn)(`The "${key}" key was found at an incorrect, ` + `i.e. out-of-order, position in "${this._type}" tree.`);
-          return xref.fetchIfRef(entries[m + 1]);
-        }
-      }
-    }
-
-    return null;
-  }
-
-}
-
-class NameTree extends NameOrNumberTree {
-  constructor(root, xref) {
-    super(root, xref, "Names");
-  }
-
-}
-
-class NumberTree extends NameOrNumberTree {
-  constructor(root, xref) {
-    super(root, xref, "Nums");
-  }
-
-}
-
-var FileSpec = function FileSpecClosure() {
-  function FileSpec(root, xref) {
-    if (!root || !(0, _primitives.isDict)(root)) {
-      return;
-    }
-
-    this.xref = xref;
-    this.root = root;
-
-    if (root.has("FS")) {
-      this.fs = root.get("FS");
-    }
-
-    this.description = root.has("Desc") ? (0, _util.stringToPDFString)(root.get("Desc")) : "";
-
-    if (root.has("RF")) {
-      (0, _util.warn)("Related file specifications are not supported");
-    }
-
-    this.contentAvailable = true;
-
-    if (!root.has("EF")) {
-      this.contentAvailable = false;
-      (0, _util.warn)("Non-embedded file specifications are not supported");
-    }
-  }
-
-  function pickPlatformItem(dict) {
-    if (dict.has("UF")) {
-      return dict.get("UF");
-    } else if (dict.has("F")) {
-      return dict.get("F");
-    } else if (dict.has("Unix")) {
-      return dict.get("Unix");
-    } else if (dict.has("Mac")) {
-      return dict.get("Mac");
-    } else if (dict.has("DOS")) {
-      return dict.get("DOS");
-    }
-
-    return null;
-  }
-
-  FileSpec.prototype = {
-    get filename() {
-      if (!this._filename && this.root) {
-        var filename = pickPlatformItem(this.root) || "unnamed";
-        this._filename = (0, _util.stringToPDFString)(filename).replace(/\\\\/g, "\\").replace(/\\\//g, "/").replace(/\\/g, "/");
-      }
-
-      return this._filename;
-    },
-
-    get content() {
-      if (!this.contentAvailable) {
-        return null;
-      }
-
-      if (!this.contentRef && this.root) {
-        this.contentRef = pickPlatformItem(this.root.get("EF"));
-      }
-
-      var content = null;
-
-      if (this.contentRef) {
-        var xref = this.xref;
-        var fileObj = xref.fetchIfRef(this.contentRef);
-
-        if (fileObj && (0, _primitives.isStream)(fileObj)) {
-          content = fileObj.getBytes();
-        } else {
-          (0, _util.warn)("Embedded file specification points to non-existing/invalid " + "content");
-        }
-      } else {
-        (0, _util.warn)("Embedded file specification does not have a content");
-      }
-
-      return content;
-    },
-
-    get serializable() {
-      return {
-        filename: this.filename,
-        content: this.content
-      };
-    }
-
-  };
-  return FileSpec;
-}();
-
-exports.FileSpec = FileSpec;
-
-const ObjectLoader = function () {
-  function mayHaveChildren(value) {
-    return value instanceof _primitives.Ref || value instanceof _primitives.Dict || Array.isArray(value) || (0, _primitives.isStream)(value);
-  }
-
-  function addChildren(node, nodesToVisit) {
-    if (node instanceof _primitives.Dict) {
-      node = node.getRawValues();
-    } else if ((0, _primitives.isStream)(node)) {
-      node = node.dict.getRawValues();
-    } else if (!Array.isArray(node)) {
-      return;
-    }
-
-    for (const rawValue of node) {
-      if (mayHaveChildren(rawValue)) {
-        nodesToVisit.push(rawValue);
-      }
-    }
-  }
-
-  function ObjectLoader(dict, keys, xref) {
-    this.dict = dict;
-    this.keys = keys;
-    this.xref = xref;
-    this.refSet = null;
-  }
-
-  ObjectLoader.prototype = {
-    async load() {
-      if (!this.xref.stream.allChunksLoaded || this.xref.stream.allChunksLoaded()) {
-        return undefined;
-      }
-
-      const {
-        keys,
-        dict
-      } = this;
-      this.refSet = new _primitives.RefSet();
-      const nodesToVisit = [];
-
-      for (let i = 0, ii = keys.length; i < ii; i++) {
-        const rawValue = dict.getRaw(keys[i]);
-
-        if (rawValue !== undefined) {
-          nodesToVisit.push(rawValue);
-        }
-      }
-
-      return this._walk(nodesToVisit);
-    },
-
-    async _walk(nodesToVisit) {
-      const nodesToRevisit = [];
-      const pendingRequests = [];
-
-      while (nodesToVisit.length) {
-        let currentNode = nodesToVisit.pop();
-
-        if (currentNode instanceof _primitives.Ref) {
-          if (this.refSet.has(currentNode)) {
-            continue;
-          }
-
-          try {
-            this.refSet.put(currentNode);
-            currentNode = this.xref.fetch(currentNode);
-          } catch (ex) {
-            if (!(ex instanceof _core_utils.MissingDataException)) {
-              (0, _util.warn)(`ObjectLoader._walk - requesting all data: "${ex}".`);
-              this.refSet = null;
-              const {
-                manager
-              } = this.xref.stream;
-              return manager.requestAllChunks();
-            }
-
-            nodesToRevisit.push(currentNode);
-            pendingRequests.push({
-              begin: ex.begin,
-              end: ex.end
-            });
-          }
-        }
-
-        if (currentNode && currentNode.getBaseStreams) {
-          const baseStreams = currentNode.getBaseStreams();
-          let foundMissingData = false;
-
-          for (let i = 0, ii = baseStreams.length; i < ii; i++) {
-            const stream = baseStreams[i];
-
-            if (stream.allChunksLoaded && !stream.allChunksLoaded()) {
-              foundMissingData = true;
-              pendingRequests.push({
-                begin: stream.start,
-                end: stream.end
-              });
-            }
-          }
-
-          if (foundMissingData) {
-            nodesToRevisit.push(currentNode);
-          }
-        }
-
-        addChildren(currentNode, nodesToVisit);
-      }
-
-      if (pendingRequests.length) {
-        await this.xref.stream.manager.requestRanges(pendingRequests);
-
-        for (let i = 0, ii = nodesToRevisit.length; i < ii; i++) {
-          const node = nodesToRevisit[i];
-
-          if (node instanceof _primitives.Ref) {
-            this.refSet.remove(node);
-          }
-        }
-
-        return this._walk(nodesToRevisit);
-      }
-
-      this.refSet = null;
-      return undefined;
-    }
-
-  };
-  return ObjectLoader;
-}();
-
-exports.ObjectLoader = ObjectLoader;
+exports.Catalog = Catalog;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 12
lib/core/ccitt.js


+ 7 - 11
lib/core/ccitt_stream.js

@@ -30,10 +30,11 @@ var _primitives = require("./primitives.js");
 
 var _ccitt = require("./ccitt.js");
 
-var _stream = require("./stream.js");
+var _decode_stream = require("./decode_stream.js");
 
-const CCITTFaxStream = function CCITTFaxStreamClosure() {
-  function CCITTFaxStream(str, maybeLength, params) {
+class CCITTFaxStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength, params) {
+    super(maybeLength);
     this.str = str;
     this.dict = str.dict;
 
@@ -56,13 +57,9 @@ const CCITTFaxStream = function CCITTFaxStreamClosure() {
       EndOfBlock: params.get("EndOfBlock"),
       BlackIs1: params.get("BlackIs1")
     });
-
-    _stream.DecodeStream.call(this, maybeLength);
   }
 
-  CCITTFaxStream.prototype = Object.create(_stream.DecodeStream.prototype);
-
-  CCITTFaxStream.prototype.readBlock = function () {
+  readBlock() {
     while (!this.eof) {
       const c = this.ccittFaxDecoder.readNextChar();
 
@@ -74,9 +71,8 @@ const CCITTFaxStream = function CCITTFaxStreamClosure() {
       this.ensureBuffer(this.bufferLength + 1);
       this.buffer[this.bufferLength++] = c;
     }
-  };
+  }
 
-  return CCITTFaxStream;
-}();
+}
 
 exports.CCITTFaxStream = CCITTFaxStream;

+ 131 - 0
lib/core/cff_font.js

@@ -0,0 +1,131 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.CFFFont = void 0;
+
+var _cff_parser = require("./cff_parser.js");
+
+var _fonts_utils = require("./fonts_utils.js");
+
+var _util = require("../shared/util.js");
+
+class CFFFont {
+  constructor(file, properties) {
+    this.properties = properties;
+    const parser = new _cff_parser.CFFParser(file, properties, _fonts_utils.SEAC_ANALYSIS_ENABLED);
+    this.cff = parser.parse();
+    this.cff.duplicateFirstGlyph();
+    const compiler = new _cff_parser.CFFCompiler(this.cff);
+    this.seacs = this.cff.seacs;
+
+    try {
+      this.data = compiler.compile();
+    } catch (e) {
+      (0, _util.warn)("Failed to compile font " + properties.loadedName);
+      this.data = file;
+    }
+
+    this._createBuiltInEncoding();
+  }
+
+  get numGlyphs() {
+    return this.cff.charStrings.count;
+  }
+
+  getCharset() {
+    return this.cff.charset.charset;
+  }
+
+  getGlyphMapping() {
+    const cff = this.cff;
+    const properties = this.properties;
+    const charsets = cff.charset.charset;
+    let charCodeToGlyphId;
+    let glyphId;
+
+    if (properties.composite) {
+      charCodeToGlyphId = Object.create(null);
+      let charCode;
+
+      if (cff.isCIDFont) {
+        for (glyphId = 0; glyphId < charsets.length; glyphId++) {
+          const cid = charsets[glyphId];
+          charCode = properties.cMap.charCodeOf(cid);
+          charCodeToGlyphId[charCode] = glyphId;
+        }
+      } else {
+        for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) {
+          charCode = properties.cMap.charCodeOf(glyphId);
+          charCodeToGlyphId[charCode] = glyphId;
+        }
+      }
+
+      return charCodeToGlyphId;
+    }
+
+    const encoding = cff.encoding ? cff.encoding.encoding : null;
+    charCodeToGlyphId = (0, _fonts_utils.type1FontGlyphMapping)(properties, encoding, charsets);
+    return charCodeToGlyphId;
+  }
+
+  hasGlyphId(id) {
+    return this.cff.hasGlyphId(id);
+  }
+
+  _createBuiltInEncoding() {
+    const {
+      charset,
+      encoding
+    } = this.cff;
+
+    if (!charset || !encoding) {
+      return;
+    }
+
+    const charsets = charset.charset,
+          encodings = encoding.encoding;
+    const map = [];
+
+    for (const charCode in encodings) {
+      const glyphId = encodings[charCode];
+
+      if (glyphId >= 0) {
+        const glyphName = charsets[glyphId];
+
+        if (glyphName) {
+          map[charCode] = glyphName;
+        }
+      }
+    }
+
+    if (map.length > 0) {
+      this.properties.builtInEncoding = map;
+    }
+  }
+
+}
+
+exports.CFFFont = CFFFont;

+ 1 - 1
lib/core/cff_parser.js

@@ -641,7 +641,7 @@ const CFFParser = function CFFParserClosure() {
               (0, _util.warn)("Found too many parameters for stack-clearing command");
             }
 
-            if (stackSize > 0 && stack[stackSize - 1] >= 0) {
+            if (stackSize > 0) {
               state.width = stack[stackSize - 1];
             }
           }

+ 20 - 77
lib/core/chunked_stream.js

@@ -30,12 +30,11 @@ var _util = require("../shared/util.js");
 
 var _core_utils = require("./core_utils.js");
 
-class ChunkedStream {
+var _stream = require("./stream.js");
+
+class ChunkedStream extends _stream.Stream {
   constructor(length, chunkSize, manager) {
-    this.bytes = new Uint8Array(length);
-    this.start = 0;
-    this.pos = 0;
-    this.end = length;
+    super(new Uint8Array(length), 0, length, null);
     this.chunkSize = chunkSize;
     this._loadedChunks = new Set();
     this.numChunks = Math.ceil(length / chunkSize);
@@ -56,15 +55,11 @@ class ChunkedStream {
     return chunks;
   }
 
-  getBaseStreams() {
-    return [this];
-  }
-
   get numChunksLoaded() {
     return this._loadedChunks.size;
   }
 
-  allChunksLoaded() {
+  get isDataLoaded() {
     return this.numChunksLoaded === this.numChunks;
   }
 
@@ -159,14 +154,6 @@ class ChunkedStream {
     return this._loadedChunks.has(chunk);
   }
 
-  get length() {
-    return this.end - this.start;
-  }
-
-  get isEmpty() {
-    return this.length === 0;
-  }
-
   getByte() {
     const pos = this.pos;
 
@@ -181,25 +168,6 @@ class ChunkedStream {
     return this.bytes[this.pos++];
   }
 
-  getUint16() {
-    const b0 = this.getByte();
-    const b1 = this.getByte();
-
-    if (b0 === -1 || b1 === -1) {
-      return -1;
-    }
-
-    return (b0 << 8) + b1;
-  }
-
-  getInt32() {
-    const b0 = this.getByte();
-    const b1 = this.getByte();
-    const b2 = this.getByte();
-    const b3 = this.getByte();
-    return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
-  }
-
   getBytes(length, forceClamped = false) {
     const bytes = this.bytes;
     const pos = this.pos;
@@ -229,22 +197,6 @@ class ChunkedStream {
     return forceClamped ? new Uint8ClampedArray(subarray) : subarray;
   }
 
-  peekByte() {
-    const peekedByte = this.getByte();
-
-    if (peekedByte !== -1) {
-      this.pos--;
-    }
-
-    return peekedByte;
-  }
-
-  peekBytes(length, forceClamped = false) {
-    const bytes = this.getBytes(length, forceClamped);
-    this.pos -= bytes.length;
-    return bytes;
-  }
-
   getByteRange(begin, end) {
     if (begin < 0) {
       begin = 0;
@@ -261,23 +213,7 @@ class ChunkedStream {
     return this.bytes.subarray(begin, end);
   }
 
-  skip(n) {
-    if (!n) {
-      n = 1;
-    }
-
-    this.pos += n;
-  }
-
-  reset() {
-    this.pos = this.start;
-  }
-
-  moveStart() {
-    this.start = this.pos;
-  }
-
-  makeSubStream(start, length, dict) {
+  makeSubStream(start, length, dict = null) {
     if (length) {
       if (start + length > this.progressiveDataLength) {
         this.ensureRange(start, start + length);
@@ -307,14 +243,17 @@ class ChunkedStream {
       return missingChunks;
     };
 
-    ChunkedStreamSubstream.prototype.allChunksLoaded = function () {
-      if (this.numChunksLoaded === this.numChunks) {
-        return true;
-      }
+    Object.defineProperty(ChunkedStreamSubstream.prototype, "isDataLoaded", {
+      get() {
+        if (this.numChunksLoaded === this.numChunks) {
+          return true;
+        }
 
-      return this.getMissingChunks().length === 0;
-    };
+        return this.getMissingChunks().length === 0;
+      },
 
+      configurable: true
+    });
     const subStream = new ChunkedStreamSubstream();
     subStream.pos = subStream.start = start;
     subStream.end = start + length || this.end;
@@ -322,6 +261,10 @@ class ChunkedStream {
     return subStream;
   }
 
+  getBaseStreams() {
+    return [this];
+  }
+
 }
 
 exports.ChunkedStream = ChunkedStream;
@@ -551,7 +494,7 @@ class ChunkedStreamManager {
       this.stream.onReceiveData(begin, chunk);
     }
 
-    if (this.stream.allChunksLoaded()) {
+    if (this.stream.isDataLoaded) {
       this._loadedStreamCapability.resolve(this.stream);
     }
 

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
lib/core/cmap.js


+ 38 - 1
lib/core/core_utils.js

@@ -37,6 +37,7 @@ exports.readInt8 = readInt8;
 exports.readUint16 = readUint16;
 exports.readUint32 = readUint32;
 exports.toRomanNumerals = toRomanNumerals;
+exports.validateCSSFont = validateCSSFont;
 exports.XRefParseException = exports.XRefEntryException = exports.MissingDataException = void 0;
 
 var _util = require("../shared/util.js");
@@ -249,7 +250,7 @@ function _collectJS(entry, xref, list, parents) {
       let code;
 
       if ((0, _primitives.isStream)(js)) {
-        code = (0, _util.bytesToString)(js.getBytes());
+        code = js.getString();
       } else {
         code = js;
       }
@@ -370,4 +371,40 @@ function encodeToXmlString(str) {
   }
 
   return buffer.join("");
+}
+
+function validateCSSFont(cssFontInfo) {
+  const DEFAULT_CSS_FONT_OBLIQUE = "14";
+  const DEFAULT_CSS_FONT_WEIGHT = "400";
+  const CSS_FONT_WEIGHT_VALUES = new Set(["100", "200", "300", "400", "500", "600", "700", "800", "900", "1000", "normal", "bold", "bolder", "lighter"]);
+  const {
+    fontFamily,
+    fontWeight,
+    italicAngle
+  } = cssFontInfo;
+
+  if (/^".*"$/.test(fontFamily)) {
+    if (/[^\\]"/.test(fontFamily.slice(1, fontFamily.length - 1))) {
+      (0, _util.warn)(`XFA - FontFamily contains some unescaped ": ${fontFamily}.`);
+      return false;
+    }
+  } else if (/^'.*'$/.test(fontFamily)) {
+    if (/[^\\]'/.test(fontFamily.slice(1, fontFamily.length - 1))) {
+      (0, _util.warn)(`XFA - FontFamily contains some unescaped ': ${fontFamily}.`);
+      return false;
+    }
+  } else {
+    for (const ident of fontFamily.split(/[ \t]+/)) {
+      if (/^([0-9]|(-([0-9]|-)))/.test(ident) || !/^[a-zA-Z0-9\-_\\]+$/.test(ident)) {
+        (0, _util.warn)(`XFA - FontFamily contains some invalid <custom-ident>: ${fontFamily}.`);
+        return false;
+      }
+    }
+  }
+
+  const weight = fontWeight ? fontWeight.toString() : "";
+  cssFontInfo.fontWeight = CSS_FONT_WEIGHT_VALUES.has(weight) ? weight : DEFAULT_CSS_FONT_WEIGHT;
+  const angle = parseFloat(italicAngle);
+  cssFontInfo.italicAngle = isNaN(angle) || angle < -90 || angle > 90 ? DEFAULT_CSS_FONT_OBLIQUE : italicAngle.toString();
+  return true;
 }

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 40 - 46
lib/core/crypto.js


+ 195 - 0
lib/core/decode_stream.js

@@ -0,0 +1,195 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.StreamsSequenceStream = exports.DecodeStream = void 0;
+
+var _base_stream = require("./base_stream.js");
+
+var _stream = require("./stream.js");
+
+const emptyBuffer = new Uint8Array(0);
+
+class DecodeStream extends _base_stream.BaseStream {
+  constructor(maybeMinBufferLength) {
+    super();
+    this._rawMinBufferLength = maybeMinBufferLength || 0;
+    this.pos = 0;
+    this.bufferLength = 0;
+    this.eof = false;
+    this.buffer = emptyBuffer;
+    this.minBufferLength = 512;
+
+    if (maybeMinBufferLength) {
+      while (this.minBufferLength < maybeMinBufferLength) {
+        this.minBufferLength *= 2;
+      }
+    }
+  }
+
+  get isEmpty() {
+    while (!this.eof && this.bufferLength === 0) {
+      this.readBlock();
+    }
+
+    return this.bufferLength === 0;
+  }
+
+  ensureBuffer(requested) {
+    const buffer = this.buffer;
+
+    if (requested <= buffer.byteLength) {
+      return buffer;
+    }
+
+    let size = this.minBufferLength;
+
+    while (size < requested) {
+      size *= 2;
+    }
+
+    const buffer2 = new Uint8Array(size);
+    buffer2.set(buffer);
+    return this.buffer = buffer2;
+  }
+
+  getByte() {
+    const pos = this.pos;
+
+    while (this.bufferLength <= pos) {
+      if (this.eof) {
+        return -1;
+      }
+
+      this.readBlock();
+    }
+
+    return this.buffer[this.pos++];
+  }
+
+  getBytes(length, forceClamped = false) {
+    const pos = this.pos;
+    let end;
+
+    if (length) {
+      this.ensureBuffer(pos + length);
+      end = pos + length;
+
+      while (!this.eof && this.bufferLength < end) {
+        this.readBlock();
+      }
+
+      const bufEnd = this.bufferLength;
+
+      if (end > bufEnd) {
+        end = bufEnd;
+      }
+    } else {
+      while (!this.eof) {
+        this.readBlock();
+      }
+
+      end = this.bufferLength;
+    }
+
+    this.pos = end;
+    const subarray = this.buffer.subarray(pos, end);
+    return forceClamped && !(subarray instanceof Uint8ClampedArray) ? new Uint8ClampedArray(subarray) : subarray;
+  }
+
+  reset() {
+    this.pos = 0;
+  }
+
+  makeSubStream(start, length, dict = null) {
+    if (length === undefined) {
+      while (!this.eof) {
+        this.readBlock();
+      }
+    } else {
+      const end = start + length;
+
+      while (this.bufferLength <= end && !this.eof) {
+        this.readBlock();
+      }
+    }
+
+    return new _stream.Stream(this.buffer, start, length, dict);
+  }
+
+  getBaseStreams() {
+    return this.str ? this.str.getBaseStreams() : null;
+  }
+
+}
+
+exports.DecodeStream = DecodeStream;
+
+class StreamsSequenceStream extends DecodeStream {
+  constructor(streams) {
+    let maybeLength = 0;
+
+    for (const stream of streams) {
+      maybeLength += stream instanceof DecodeStream ? stream._rawMinBufferLength : stream.length;
+    }
+
+    super(maybeLength);
+    this.streams = streams;
+  }
+
+  readBlock() {
+    const streams = this.streams;
+
+    if (streams.length === 0) {
+      this.eof = true;
+      return;
+    }
+
+    const stream = streams.shift();
+    const chunk = stream.getBytes();
+    const bufferLength = this.bufferLength;
+    const newLength = bufferLength + chunk.length;
+    const buffer = this.ensureBuffer(newLength);
+    buffer.set(chunk, bufferLength);
+    this.bufferLength = newLength;
+  }
+
+  getBaseStreams() {
+    const baseStreamsBuf = [];
+
+    for (const stream of this.streams) {
+      const baseStreams = stream.getBaseStreams();
+
+      if (baseStreams) {
+        baseStreamsBuf.push(...baseStreams);
+      }
+    }
+
+    return baseStreamsBuf.length > 0 ? baseStreamsBuf : null;
+  }
+
+}
+
+exports.StreamsSequenceStream = StreamsSequenceStream;

+ 75 - 0
lib/core/decrypt_stream.js

@@ -0,0 +1,75 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.DecryptStream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+const chunkSize = 512;
+
+class DecryptStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength, decrypt) {
+    super(maybeLength);
+    this.str = str;
+    this.dict = str.dict;
+    this.decrypt = decrypt;
+    this.nextChunk = null;
+    this.initialized = false;
+  }
+
+  readBlock() {
+    let chunk;
+
+    if (this.initialized) {
+      chunk = this.nextChunk;
+    } else {
+      chunk = this.str.getBytes(chunkSize);
+      this.initialized = true;
+    }
+
+    if (!chunk || chunk.length === 0) {
+      this.eof = true;
+      return;
+    }
+
+    this.nextChunk = this.str.getBytes(chunkSize);
+    const hasMoreData = this.nextChunk && this.nextChunk.length > 0;
+    const decrypt = this.decrypt;
+    chunk = decrypt(chunk, !hasMoreData);
+    let bufferLength = this.bufferLength;
+    const n = chunk.length,
+          buffer = this.ensureBuffer(bufferLength + n);
+
+    for (let i = 0; i < n; i++) {
+      buffer[bufferLength++] = chunk[i];
+    }
+
+    this.bufferLength = bufferLength;
+  }
+
+}
+
+exports.DecryptStream = DecryptStream;

+ 156 - 41
lib/core/document.js

@@ -28,8 +28,6 @@ exports.PDFDocument = exports.Page = void 0;
 
 var _util = require("../shared/util.js");
 
-var _obj = require("./obj.js");
-
 var _primitives = require("./primitives.js");
 
 var _core_utils = require("./core_utils.js");
@@ -38,23 +36,31 @@ var _stream = require("./stream.js");
 
 var _annotation = require("./annotation.js");
 
+var _base_stream = require("./base_stream.js");
+
 var _crypto = require("./crypto.js");
 
+var _catalog = require("./catalog.js");
+
 var _parser = require("./parser.js");
 
+var _object_loader = require("./object_loader.js");
+
 var _operator_list = require("./operator_list.js");
 
 var _evaluator = require("./evaluator.js");
 
+var _decode_stream = require("./decode_stream.js");
+
+var _struct_tree = require("./struct_tree.js");
+
 var _factory = require("./xfa/factory.js");
 
+var _xref = require("./xref.js");
+
 const DEFAULT_USER_UNIT = 1.0;
 const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
 
-function isAnnotationRenderable(annotation, intent) {
-  return intent === "display" && annotation.viewable || intent === "print" && annotation.printable;
-}
-
 class Page {
   constructor({
     pdfManager,
@@ -89,6 +95,10 @@ class Page {
         return `p${pageIndex}_${++idCounters.obj}`;
       }
 
+      static getPageObjId() {
+        return `page${ref.toString()}`;
+      }
+
     };
   }
 
@@ -115,7 +125,7 @@ class Page {
   }
 
   get content() {
-    return this.pageDict.get("Contents");
+    return this.pageDict.getArray("Contents");
   }
 
   get resources() {
@@ -199,25 +209,17 @@ class Page {
   }
 
   getContentStream() {
-    const content = this.content;
-    let stream;
-
-    if (Array.isArray(content)) {
-      const xref = this.xref;
-      const streams = [];
-
-      for (const subStream of content) {
-        streams.push(xref.fetchIfRef(subStream));
+    return this.pdfManager.ensure(this, "content").then(content => {
+      if (content instanceof _base_stream.BaseStream) {
+        return content;
       }
 
-      stream = new _stream.StreamsSequenceStream(streams);
-    } else if ((0, _primitives.isStream)(content)) {
-      stream = content;
-    } else {
-      stream = new _stream.NullStream();
-    }
+      if (Array.isArray(content)) {
+        return new _decode_stream.StreamsSequenceStream(content);
+      }
 
-    return stream;
+      return new _stream.NullStream();
+    });
   }
 
   get xfaData() {
@@ -243,7 +245,7 @@ class Page {
       const newRefsPromises = [];
 
       for (const annotation of annotations) {
-        if (!isAnnotationRenderable(annotation, "print")) {
+        if (!annotation.mustBePrinted(annotationStorage)) {
           continue;
         }
 
@@ -263,7 +265,7 @@ class Page {
     }
 
     return this.resourcesPromise.then(() => {
-      const objectLoader = new _obj.ObjectLoader(this.resources, keys, this.xref);
+      const objectLoader = new _object_loader.ObjectLoader(this.resources, keys, this.xref);
       return objectLoader.load();
     });
   }
@@ -276,8 +278,8 @@ class Page {
     renderInteractiveForms,
     annotationStorage
   }) {
-    const contentStreamPromise = this.pdfManager.ensure(this, "getContentStream");
-    const resourcesPromise = this.loadResources(["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"]);
+    const contentStreamPromise = this.getContentStream();
+    const resourcesPromise = this.loadResources(["ColorSpace", "ExtGState", "Font", "Pattern", "Properties", "Shading", "XObject"]);
     const partialEvaluator = new _evaluator.PartialEvaluator({
       xref: this.xref,
       handler,
@@ -316,7 +318,7 @@ class Page {
       const opListPromises = [];
 
       for (const annotation of annotations) {
-        if (isAnnotationRenderable(annotation, intent) && !annotation.isHidden(annotationStorage)) {
+        if (intent === "display" && annotation.mustBeViewed(annotationStorage) || intent === "print" && annotation.mustBePrinted(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;
@@ -344,11 +346,12 @@ class Page {
     handler,
     task,
     normalizeWhitespace,
+    includeMarkedContent,
     sink,
     combineTextItems
   }) {
-    const contentStreamPromise = this.pdfManager.ensure(this, "getContentStream");
-    const resourcesPromise = this.loadResources(["ExtGState", "XObject", "Font"]);
+    const contentStreamPromise = this.getContentStream();
+    const resourcesPromise = this.loadResources(["ExtGState", "Font", "Properties", "XObject"]);
     const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
     return dataPromises.then(([contentStream]) => {
       const partialEvaluator = new _evaluator.PartialEvaluator({
@@ -366,18 +369,36 @@ class Page {
         task,
         resources: this.resources,
         normalizeWhitespace,
+        includeMarkedContent,
         combineTextItems,
         sink
       });
     });
   }
 
+  async getStructTree() {
+    const structTreeRoot = await this.pdfManager.ensureCatalog("structTreeRoot");
+
+    if (!structTreeRoot) {
+      return null;
+    }
+
+    const structTree = await this.pdfManager.ensure(this, "_parseStructTree", [structTreeRoot]);
+    return structTree.serializable;
+  }
+
+  _parseStructTree(structTreeRoot) {
+    const tree = new _struct_tree.StructTreePage(structTreeRoot, this.pageDict);
+    tree.parse();
+    return tree;
+  }
+
   getAnnotationsData(intent) {
     return this._parsedAnnotations.then(function (annotations) {
       const annotationsData = [];
 
       for (let i = 0, ii = annotations.length; i < ii; i++) {
-        if (!intent || isAnnotationRenderable(annotations[i], intent)) {
+        if (!intent || intent === "display" && annotations[i].viewable || intent === "print" && annotations[i].printable) {
           annotationsData.push(annotations[i].data);
         }
       }
@@ -492,7 +513,7 @@ class PDFDocument {
 
     this.pdfManager = pdfManager;
     this.stream = stream;
-    this.xref = new _obj.XRef(stream, pdfManager);
+    this.xref = new _xref.XRef(stream, pdfManager);
     this._pagePromises = [];
     this._version = null;
     const idCounters = {
@@ -511,12 +532,16 @@ class PDFDocument {
         (0, _util.unreachable)("Abstract method `createObjId` called.");
       }
 
+      static getPageObjId() {
+        (0, _util.unreachable)("Abstract method `getPageObjId` called.");
+      }
+
     };
   }
 
   parse(recoveryMode) {
     this.xref.parse(recoveryMode);
-    this.catalog = new _obj.Catalog(this.pdfManager, this.xref);
+    this.catalog = new _catalog.Catalog(this.pdfManager, this.xref);
 
     if (this.catalog.version) {
       this._version = this.catalog.version;
@@ -683,7 +708,7 @@ class PDFDocument {
 
     if ((0, _primitives.isStream)(xfa) && !xfa.isEmpty) {
       try {
-        entries["xdp:xdp"] = (0, _util.stringToUTF8String)((0, _util.bytesToString)(xfa.getBytes()));
+        entries["xdp:xdp"] = (0, _util.stringToUTF8String)(xfa.getString());
         return entries;
       } catch (_) {
         (0, _util.warn)("XFA - Invalid utf-8 string.");
@@ -717,7 +742,7 @@ class PDFDocument {
       }
 
       try {
-        entries[name] = (0, _util.stringToUTF8String)((0, _util.bytesToString)(data.getBytes()));
+        entries[name] = (0, _util.stringToUTF8String)(data.getString());
       } catch (_) {
         (0, _util.warn)("XFA - Invalid utf-8 string.");
         return null;
@@ -740,11 +765,85 @@ class PDFDocument {
     return this.xfaFactory !== null;
   }
 
+  async loadXfaFonts(handler, task) {
+    const acroForm = await this.pdfManager.ensureCatalog("acroForm");
+
+    if (!acroForm) {
+      return;
+    }
+
+    const resources = await acroForm.getAsync("DR");
+
+    if (!(resources instanceof _primitives.Dict)) {
+      return;
+    }
+
+    const objectLoader = new _object_loader.ObjectLoader(resources, ["Font"], this.xref);
+    await objectLoader.load();
+    const fontRes = resources.get("Font");
+
+    if (!(fontRes instanceof _primitives.Dict)) {
+      return;
+    }
+
+    const partialEvaluator = new _evaluator.PartialEvaluator({
+      xref: this.xref,
+      handler,
+      pageIndex: -1,
+      idFactory: this._globalIdFactory,
+      fontCache: this.catalog.fontCache,
+      builtInCMapCache: this.catalog.builtInCMapCache
+    });
+    const operatorList = new _operator_list.OperatorList();
+    const initialState = {
+      font: null,
+
+      clone() {
+        return this;
+      }
+
+    };
+    const fonts = new Map();
+    fontRes.forEach((fontName, font) => {
+      fonts.set(fontName, font);
+    });
+    const promises = [];
+
+    for (const [fontName, font] of fonts) {
+      const descriptor = font.get("FontDescriptor");
+
+      if (!(descriptor instanceof _primitives.Dict)) {
+        continue;
+      }
+
+      const fontFamily = descriptor.get("FontFamily");
+      const fontWeight = descriptor.get("FontWeight");
+      const italicAngle = -descriptor.get("ItalicAngle");
+      const cssFontInfo = {
+        fontFamily,
+        fontWeight,
+        italicAngle
+      };
+
+      if (!(0, _core_utils.validateCSSFont)(cssFontInfo)) {
+        continue;
+      }
+
+      promises.push(partialEvaluator.handleSetFont(resources, [_primitives.Name.get(fontName), 1], null, operatorList, task, initialState, null, cssFontInfo).catch(function (reason) {
+        (0, _util.warn)(`loadXfaFonts: "${reason}".`);
+        return null;
+      }));
+    }
+
+    await Promise.all(promises);
+  }
+
   get formInfo() {
     const formInfo = {
       hasFields: false,
       hasAcroForm: false,
-      hasXfa: false
+      hasXfa: false,
+      hasSignatures: false
     };
     const acroForm = this.catalog.acroForm;
 
@@ -759,10 +858,12 @@ class PDFDocument {
       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 hasSignatures = !!(sigFlags & 0x1);
 
-      const hasOnlyDocumentSignatures = !!(sigFlags & 0x1) && this._hasOnlyDocumentSignatures(fields);
+      const hasOnlyDocumentSignatures = hasSignatures && this._hasOnlyDocumentSignatures(fields);
 
       formInfo.hasAcroForm = hasFields && !hasOnlyDocumentSignatures;
+      formInfo.hasSignatures = hasSignatures;
     } catch (ex) {
       if (ex instanceof _core_utils.MissingDataException) {
         throw ex;
@@ -798,7 +899,8 @@ class PDFDocument {
       IsLinearized: !!this.linearization,
       IsAcroFormPresent: this.formInfo.hasAcroForm,
       IsXFAPresent: this.formInfo.hasXfa,
-      IsCollectionPresent: !!this.catalog.collection
+      IsCollectionPresent: !!this.catalog.collection,
+      IsSignaturesPresent: this.formInfo.hasSignatures
     };
     let infoDict;
 
@@ -1011,9 +1113,22 @@ class PDFDocument {
   }
 
   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;
-    }));
+    const promise = this.pdfManager.ensureDoc("_parseHasJSActions");
+    return (0, _util.shadow)(this, "hasJSActions", promise);
+  }
+
+  async _parseHasJSActions() {
+    const [catalogJsActions, fieldObjects] = await Promise.all([this.pdfManager.ensureCatalog("jsActions"), this.pdfManager.ensureDoc("fieldObjects")]);
+
+    if (catalogJsActions) {
+      return true;
+    }
+
+    if (fieldObjects) {
+      return Object.values(fieldObjects).some(fieldObject => fieldObject.some(object => object.actions !== null));
+    }
+
+    return false;
   }
 
   get calculationOrderIds() {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 446 - 201
lib/core/evaluator.js


+ 120 - 0
lib/core/file_spec.js

@@ -0,0 +1,120 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.FileSpec = void 0;
+
+var _primitives = require("./primitives.js");
+
+var _util = require("../shared/util.js");
+
+function pickPlatformItem(dict) {
+  if (dict.has("UF")) {
+    return dict.get("UF");
+  } else if (dict.has("F")) {
+    return dict.get("F");
+  } else if (dict.has("Unix")) {
+    return dict.get("Unix");
+  } else if (dict.has("Mac")) {
+    return dict.get("Mac");
+  } else if (dict.has("DOS")) {
+    return dict.get("DOS");
+  }
+
+  return null;
+}
+
+class FileSpec {
+  constructor(root, xref) {
+    if (!root || !(0, _primitives.isDict)(root)) {
+      return;
+    }
+
+    this.xref = xref;
+    this.root = root;
+
+    if (root.has("FS")) {
+      this.fs = root.get("FS");
+    }
+
+    this.description = root.has("Desc") ? (0, _util.stringToPDFString)(root.get("Desc")) : "";
+
+    if (root.has("RF")) {
+      (0, _util.warn)("Related file specifications are not supported");
+    }
+
+    this.contentAvailable = true;
+
+    if (!root.has("EF")) {
+      this.contentAvailable = false;
+      (0, _util.warn)("Non-embedded file specifications are not supported");
+    }
+  }
+
+  get filename() {
+    if (!this._filename && this.root) {
+      const filename = pickPlatformItem(this.root) || "unnamed";
+      this._filename = (0, _util.stringToPDFString)(filename).replace(/\\\\/g, "\\").replace(/\\\//g, "/").replace(/\\/g, "/");
+    }
+
+    return this._filename;
+  }
+
+  get content() {
+    if (!this.contentAvailable) {
+      return null;
+    }
+
+    if (!this.contentRef && this.root) {
+      this.contentRef = pickPlatformItem(this.root.get("EF"));
+    }
+
+    let content = null;
+
+    if (this.contentRef) {
+      const fileObj = this.xref.fetchIfRef(this.contentRef);
+
+      if (fileObj && (0, _primitives.isStream)(fileObj)) {
+        content = fileObj.getBytes();
+      } else {
+        (0, _util.warn)("Embedded file specification points to non-existing/invalid content");
+      }
+    } else {
+      (0, _util.warn)("Embedded file specification does not have a content");
+    }
+
+    return content;
+  }
+
+  get serializable() {
+    return {
+      filename: this.filename,
+      content: this.content
+    };
+  }
+
+}
+
+exports.FileSpec = FileSpec;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 35 - 0
lib/core/flate_stream.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 438 - 530
lib/core/font_renderer.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 13 - 20
lib/core/fonts.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 53 - 0
lib/core/fonts_utils.js


+ 157 - 154
lib/core/function.js

@@ -152,27 +152,27 @@ function toNumberArray(arr) {
   return arr;
 }
 
-var PDFFunction = function PDFFunctionClosure() {
+const PDFFunction = function PDFFunctionClosure() {
   const CONSTRUCT_SAMPLED = 0;
   const CONSTRUCT_INTERPOLATED = 2;
   const CONSTRUCT_STICHED = 3;
   const CONSTRUCT_POSTSCRIPT = 4;
   return {
     getSampleArray(size, outputSize, bps, stream) {
-      var i, ii;
-      var length = 1;
+      let i, ii;
+      let length = 1;
 
       for (i = 0, ii = size.length; i < ii; i++) {
         length *= size[i];
       }
 
       length *= outputSize;
-      var array = new Array(length);
-      var codeSize = 0;
-      var codeBuf = 0;
-      var sampleMul = 1.0 / (2.0 ** bps - 1);
-      var strBytes = stream.getBytes((length * bps + 7) / 8);
-      var strIdx = 0;
+      const array = new Array(length);
+      let codeSize = 0;
+      let codeBuf = 0;
+      const sampleMul = 1.0 / (2.0 ** bps - 1);
+      const strBytes = stream.getBytes((length * bps + 7) / 8);
+      let strIdx = 0;
 
       for (i = 0; i < length; i++) {
         while (codeSize < bps) {
@@ -194,15 +194,15 @@ var PDFFunction = function PDFFunctionClosure() {
       isEvalSupported,
       fn
     }) {
-      var dict = fn.dict;
+      let dict = fn.dict;
 
       if (!dict) {
         dict = fn;
       }
 
-      var types = [this.constructSampled, null, this.constructInterpolated, this.constructStiched, this.constructPostScript];
-      var typeNum = dict.get("FunctionType");
-      var typeFn = types[typeNum];
+      const types = [this.constructSampled, null, this.constructInterpolated, this.constructStiched, this.constructPostScript];
+      const typeNum = dict.get("FunctionType");
+      const typeFn = types[typeNum];
 
       if (!typeFn) {
         throw new _util.FormatError("Unknown type of function");
@@ -221,7 +221,7 @@ var PDFFunction = function PDFFunctionClosure() {
       isEvalSupported,
       IR
     }) {
-      var type = IR[0];
+      const type = IR[0];
 
       switch (type) {
         case CONSTRUCT_SAMPLED:
@@ -284,9 +284,9 @@ var PDFFunction = function PDFFunctionClosure() {
         });
       }
 
-      var fnArray = [];
+      const fnArray = [];
 
-      for (var j = 0, jj = fnObj.length; j < jj; j++) {
+      for (let j = 0, jj = fnObj.length; j < jj; j++) {
         fnArray.push(this.parse({
           xref,
           isEvalSupported,
@@ -295,7 +295,7 @@ var PDFFunction = function PDFFunctionClosure() {
       }
 
       return function (src, srcOffset, dest, destOffset) {
-        for (var i = 0, ii = fnArray.length; i < ii; i++) {
+        for (let i = 0, ii = fnArray.length; i < ii; i++) {
           fnArray[i](src, srcOffset, dest, destOffset + i);
         }
       };
@@ -308,11 +308,11 @@ var PDFFunction = function PDFFunctionClosure() {
       dict
     }) {
       function toMultiArray(arr) {
-        var inputLength = arr.length;
-        var out = [];
-        var index = 0;
+        const inputLength = arr.length;
+        const out = [];
+        let index = 0;
 
-        for (var i = 0; i < inputLength; i += 2) {
+        for (let i = 0; i < inputLength; i += 2) {
           out[index] = [arr[i], arr[i + 1]];
           ++index;
         }
@@ -320,38 +320,38 @@ var PDFFunction = function PDFFunctionClosure() {
         return out;
       }
 
-      var domain = toNumberArray(dict.getArray("Domain"));
-      var range = toNumberArray(dict.getArray("Range"));
+      let domain = toNumberArray(dict.getArray("Domain"));
+      let range = toNumberArray(dict.getArray("Range"));
 
       if (!domain || !range) {
         throw new _util.FormatError("No domain or range");
       }
 
-      var inputSize = domain.length / 2;
-      var outputSize = range.length / 2;
+      const inputSize = domain.length / 2;
+      const outputSize = range.length / 2;
       domain = toMultiArray(domain);
       range = toMultiArray(range);
-      var size = toNumberArray(dict.getArray("Size"));
-      var bps = dict.get("BitsPerSample");
-      var order = dict.get("Order") || 1;
+      const size = toNumberArray(dict.getArray("Size"));
+      const bps = dict.get("BitsPerSample");
+      const order = dict.get("Order") || 1;
 
       if (order !== 1) {
         (0, _util.info)("No support for cubic spline interpolation: " + order);
       }
 
-      var encode = toNumberArray(dict.getArray("Encode"));
+      let encode = toNumberArray(dict.getArray("Encode"));
 
       if (!encode) {
         encode = [];
 
-        for (var i = 0; i < inputSize; ++i) {
+        for (let i = 0; i < inputSize; ++i) {
           encode.push([0, size[i] - 1]);
         }
       } else {
         encode = toMultiArray(encode);
       }
 
-      var decode = toNumberArray(dict.getArray("Decode"));
+      let decode = toNumberArray(dict.getArray("Decode"));
 
       if (!decode) {
         decode = range;
@@ -359,7 +359,7 @@ var PDFFunction = function PDFFunctionClosure() {
         decode = toMultiArray(decode);
       }
 
-      var samples = this.getSampleArray(size, outputSize, bps, fn);
+      const samples = this.getSampleArray(size, outputSize, bps, fn);
       return [CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, outputSize, 2 ** bps - 1, range];
     },
 
@@ -373,38 +373,38 @@ var PDFFunction = function PDFFunctionClosure() {
       }
 
       return function constructSampledFromIRResult(src, srcOffset, dest, destOffset) {
-        var m = IR[1];
-        var domain = IR[2];
-        var encode = IR[3];
-        var decode = IR[4];
-        var samples = IR[5];
-        var size = IR[6];
-        var n = IR[7];
-        var range = IR[9];
-        var cubeVertices = 1 << m;
-        var cubeN = new Float64Array(cubeVertices);
-        var cubeVertex = new Uint32Array(cubeVertices);
-        var i, j;
+        const m = IR[1];
+        const domain = IR[2];
+        const encode = IR[3];
+        const decode = IR[4];
+        const samples = IR[5];
+        const size = IR[6];
+        const n = IR[7];
+        const range = IR[9];
+        const cubeVertices = 1 << m;
+        const cubeN = new Float64Array(cubeVertices);
+        const cubeVertex = new Uint32Array(cubeVertices);
+        let i, j;
 
         for (j = 0; j < cubeVertices; j++) {
           cubeN[j] = 1;
         }
 
-        var k = n,
+        let k = n,
             pos = 1;
 
         for (i = 0; i < m; ++i) {
-          var domain_2i = domain[i][0];
-          var domain_2i_1 = domain[i][1];
-          var xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);
-          var e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);
-          var size_i = size[i];
+          const domain_2i = domain[i][0];
+          const domain_2i_1 = domain[i][1];
+          const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);
+          let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);
+          const size_i = size[i];
           e = Math.min(Math.max(e, 0), size_i - 1);
-          var e0 = e < size_i - 1 ? Math.floor(e) : e - 1;
-          var n0 = e0 + 1 - e;
-          var n1 = e - e0;
-          var offset0 = e0 * k;
-          var offset1 = offset0 + k;
+          const e0 = e < size_i - 1 ? Math.floor(e) : e - 1;
+          const n0 = e0 + 1 - e;
+          const n1 = e - e0;
+          const offset0 = e0 * k;
+          const offset1 = offset0 + k;
 
           for (j = 0; j < cubeVertices; j++) {
             if (j & pos) {
@@ -421,7 +421,7 @@ var PDFFunction = function PDFFunctionClosure() {
         }
 
         for (j = 0; j < n; ++j) {
-          var rj = 0;
+          let rj = 0;
 
           for (i = 0; i < cubeVertices; i++) {
             rj += samples[cubeVertex[i] + j] * cubeN[i];
@@ -439,13 +439,13 @@ var PDFFunction = function PDFFunctionClosure() {
       fn,
       dict
     }) {
-      var c0 = toNumberArray(dict.getArray("C0")) || [0];
-      var c1 = toNumberArray(dict.getArray("C1")) || [1];
-      var n = dict.get("N");
-      var length = c0.length;
-      var diff = [];
+      const c0 = toNumberArray(dict.getArray("C0")) || [0];
+      const c1 = toNumberArray(dict.getArray("C1")) || [1];
+      const n = dict.get("N");
+      const length = c0.length;
+      const diff = [];
 
-      for (var i = 0; i < length; ++i) {
+      for (let i = 0; i < length; ++i) {
         diff.push(c1[i] - c0[i]);
       }
 
@@ -457,14 +457,14 @@ var PDFFunction = function PDFFunctionClosure() {
       isEvalSupported,
       IR
     }) {
-      var c0 = IR[1];
-      var diff = IR[2];
-      var n = IR[3];
-      var length = diff.length;
+      const c0 = IR[1];
+      const diff = IR[2];
+      const n = IR[3];
+      const length = diff.length;
       return function constructInterpolatedFromIRResult(src, srcOffset, dest, destOffset) {
-        var x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
+        const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
 
-        for (var j = 0; j < length; ++j) {
+        for (let j = 0; j < length; ++j) {
           dest[destOffset + j] = c0[j] + x * diff[j];
         }
       };
@@ -476,22 +476,22 @@ var PDFFunction = function PDFFunctionClosure() {
       fn,
       dict
     }) {
-      var domain = toNumberArray(dict.getArray("Domain"));
+      const domain = toNumberArray(dict.getArray("Domain"));
 
       if (!domain) {
         throw new _util.FormatError("No domain");
       }
 
-      var inputSize = domain.length / 2;
+      const inputSize = domain.length / 2;
 
       if (inputSize !== 1) {
         throw new _util.FormatError("Bad domain for stiched function");
       }
 
-      var fnRefs = dict.get("Functions");
-      var fns = [];
+      const fnRefs = dict.get("Functions");
+      const fns = [];
 
-      for (var i = 0, ii = fnRefs.length; i < ii; ++i) {
+      for (let i = 0, ii = fnRefs.length; i < ii; ++i) {
         fns.push(this.parse({
           xref,
           isEvalSupported,
@@ -499,8 +499,8 @@ var PDFFunction = function PDFFunctionClosure() {
         }));
       }
 
-      var bounds = toNumberArray(dict.getArray("Bounds"));
-      var encode = toNumberArray(dict.getArray("Encode"));
+      const bounds = toNumberArray(dict.getArray("Bounds"));
+      const encode = toNumberArray(dict.getArray("Encode"));
       return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
     },
 
@@ -509,13 +509,13 @@ var PDFFunction = function PDFFunctionClosure() {
       isEvalSupported,
       IR
     }) {
-      var domain = IR[1];
-      var bounds = IR[2];
-      var encode = IR[3];
-      var fns = IR[4];
-      var tmpBuf = new Float32Array(1);
+      const domain = IR[1];
+      const bounds = IR[2];
+      const encode = IR[3];
+      const fns = IR[4];
+      const tmpBuf = new Float32Array(1);
       return function constructStichedFromIRResult(src, srcOffset, dest, destOffset) {
-        var clip = function constructStichedFromIRClip(v, min, max) {
+        const clip = function constructStichedFromIRClip(v, min, max) {
           if (v > max) {
             v = max;
           } else if (v < min) {
@@ -525,28 +525,30 @@ var PDFFunction = function PDFFunctionClosure() {
           return v;
         };
 
-        var v = clip(src[srcOffset], domain[0], domain[1]);
+        const v = clip(src[srcOffset], domain[0], domain[1]);
+        const length = bounds.length;
+        let i;
 
-        for (var i = 0, ii = bounds.length; i < ii; ++i) {
+        for (i = 0; i < length; ++i) {
           if (v < bounds[i]) {
             break;
           }
         }
 
-        var dmin = domain[0];
+        let dmin = domain[0];
 
         if (i > 0) {
           dmin = bounds[i - 1];
         }
 
-        var dmax = domain[1];
+        let dmax = domain[1];
 
         if (i < bounds.length) {
           dmax = bounds[i];
         }
 
-        var rmin = encode[2 * i];
-        var rmax = encode[2 * i + 1];
+        const rmin = encode[2 * i];
+        const rmax = encode[2 * i + 1];
         tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
         fns[i](tmpBuf, 0, dest, destOffset);
       };
@@ -558,8 +560,8 @@ var PDFFunction = function PDFFunctionClosure() {
       fn,
       dict
     }) {
-      var domain = toNumberArray(dict.getArray("Domain"));
-      var range = toNumberArray(dict.getArray("Range"));
+      const domain = toNumberArray(dict.getArray("Domain"));
+      const range = toNumberArray(dict.getArray("Range"));
 
       if (!domain) {
         throw new _util.FormatError("No domain.");
@@ -569,9 +571,9 @@ var PDFFunction = function PDFFunctionClosure() {
         throw new _util.FormatError("No range.");
       }
 
-      var lexer = new _ps_parser.PostScriptLexer(fn);
-      var parser = new _ps_parser.PostScriptParser(lexer);
-      var code = parser.parse();
+      const lexer = new _ps_parser.PostScriptLexer(fn);
+      const parser = new _ps_parser.PostScriptParser(lexer);
+      const code = parser.parse();
       return [CONSTRUCT_POSTSCRIPT, domain, range, code];
     },
 
@@ -580,9 +582,9 @@ var PDFFunction = function PDFFunctionClosure() {
       isEvalSupported,
       IR
     }) {
-      var domain = IR[1];
-      var range = IR[2];
-      var code = IR[3];
+      const domain = IR[1];
+      const range = IR[2];
+      const code = IR[3];
 
       if (isEvalSupported && _util.IsEvalSupportedCached.value) {
         const compiled = new PostScriptCompiler().compile(code, domain, range);
@@ -593,17 +595,17 @@ var PDFFunction = function PDFFunctionClosure() {
       }
 
       (0, _util.info)("Unable to compile PS function");
-      var numOutputs = range.length >> 1;
-      var numInputs = domain.length >> 1;
-      var evaluator = new PostScriptEvaluator(code);
-      var cache = Object.create(null);
-      var MAX_CACHE_SIZE = 2048 * 4;
-      var cache_available = MAX_CACHE_SIZE;
-      var tmpBuf = new Float32Array(numInputs);
+      const numOutputs = range.length >> 1;
+      const numInputs = domain.length >> 1;
+      const evaluator = new PostScriptEvaluator(code);
+      const cache = Object.create(null);
+      const MAX_CACHE_SIZE = 2048 * 4;
+      let cache_available = MAX_CACHE_SIZE;
+      const tmpBuf = new Float32Array(numInputs);
       return function constructPostScriptFromIRResult(src, srcOffset, dest, destOffset) {
-        var i, value;
-        var key = "";
-        var input = tmpBuf;
+        let i, value;
+        let key = "";
+        const input = tmpBuf;
 
         for (i = 0; i < numInputs; i++) {
           value = src[srcOffset + i];
@@ -611,20 +613,20 @@ var PDFFunction = function PDFFunctionClosure() {
           key += value + "_";
         }
 
-        var cachedValue = cache[key];
+        const cachedValue = cache[key];
 
         if (cachedValue !== undefined) {
           dest.set(cachedValue, destOffset);
           return;
         }
 
-        var output = new Float32Array(numOutputs);
-        var stack = evaluator.execute(input);
-        var stackIndex = stack.length - numOutputs;
+        const output = new Float32Array(numOutputs);
+        const stack = evaluator.execute(input);
+        const stackIndex = stack.length - numOutputs;
 
         for (i = 0; i < numOutputs; i++) {
           value = stack[stackIndex + i];
-          var bound = range[i * 2];
+          let bound = range[i * 2];
 
           if (value < bound) {
             value = bound;
@@ -652,7 +654,7 @@ var PDFFunction = function PDFFunctionClosure() {
 }();
 
 function isPDFFunction(v) {
-  var fnDict;
+  let fnDict;
 
   if (typeof v !== "object") {
     return false;
@@ -667,8 +669,8 @@ function isPDFFunction(v) {
   return fnDict.has("FunctionType");
 }
 
-var PostScriptStack = function PostScriptStackClosure() {
-  var MAX_STACK_SIZE = 100;
+const PostScriptStack = function PostScriptStackClosure() {
+  const MAX_STACK_SIZE = 100;
 
   class PostScriptStack {
     constructor(initialStack) {
@@ -696,9 +698,9 @@ var PostScriptStack = function PostScriptStackClosure() {
         throw new Error("PostScript function stack overflow.");
       }
 
-      var stack = this.stack;
+      const stack = this.stack;
 
-      for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
+      for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
         stack.push(stack[i]);
       }
     }
@@ -708,28 +710,25 @@ var PostScriptStack = function PostScriptStackClosure() {
     }
 
     roll(n, p) {
-      var stack = this.stack;
-      var l = stack.length - n;
-      var r = stack.length - 1,
-          c = l + (p - Math.floor(p / n) * n),
-          i,
-          j,
-          t;
-
-      for (i = l, j = r; i < j; i++, j--) {
-        t = stack[i];
+      const stack = this.stack;
+      const l = stack.length - n;
+      const r = stack.length - 1;
+      const c = l + (p - Math.floor(p / n) * n);
+
+      for (let i = l, j = r; i < j; i++, j--) {
+        const t = stack[i];
         stack[i] = stack[j];
         stack[j] = t;
       }
 
-      for (i = l, j = c - 1; i < j; i++, j--) {
-        t = stack[i];
+      for (let i = l, j = c - 1; i < j; i++, j--) {
+        const t = stack[i];
         stack[i] = stack[j];
         stack[j] = t;
       }
 
-      for (i = c, j = r; i < j; i++, j--) {
-        t = stack[i];
+      for (let i = c, j = r; i < j; i++, j--) {
+        const t = stack[i];
         stack[i] = stack[j];
         stack[j] = t;
       }
@@ -746,11 +745,11 @@ class PostScriptEvaluator {
   }
 
   execute(initialStack) {
-    var stack = new PostScriptStack(initialStack);
-    var counter = 0;
-    var operators = this.operators;
-    var length = operators.length;
-    var operator, a, b;
+    const stack = new PostScriptStack(initialStack);
+    let counter = 0;
+    const operators = this.operators;
+    const length = operators.length;
+    let operator, a, b;
 
     while (counter < length) {
       operator = operators[counter++];
@@ -1030,7 +1029,7 @@ class PostScriptEvaluator {
 
 exports.PostScriptEvaluator = PostScriptEvaluator;
 
-var PostScriptCompiler = function PostScriptCompilerClosure() {
+const PostScriptCompiler = function PostScriptCompilerClosure() {
   class AstNode {
     constructor(type) {
       this.type = type;
@@ -1207,8 +1206,8 @@ var PostScriptCompiler = function PostScriptCompilerClosure() {
       }
     }
 
-    var min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
-    var max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
+    const min = Math.min(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
+    const max = Math.max(num1.min * num2.min, num1.min * num2.max, num1.max * num2.min, num1.max * num2.max);
     return new AstBinaryOperation("*", num1, num2, min, max);
   }
 
@@ -1240,13 +1239,13 @@ var PostScriptCompiler = function PostScriptCompilerClosure() {
 
   class PostScriptCompiler {
     compile(code, domain, range) {
-      var stack = [];
-      var instructions = [];
-      var inputSize = domain.length >> 1,
-          outputSize = range.length >> 1;
-      var lastRegister = 0;
-      var n, j;
-      var num1, num2, ast1, ast2, tmpVar, item;
+      const stack = [];
+      const instructions = [];
+      const inputSize = domain.length >> 1,
+            outputSize = range.length >> 1;
+      let lastRegister = 0;
+      let n, j;
+      let num1, num2, ast1, ast2, tmpVar, item;
 
       for (let i = 0; i < inputSize; i++) {
         stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
@@ -1408,18 +1407,21 @@ var PostScriptCompiler = function PostScriptCompilerClosure() {
         return null;
       }
 
-      var result = [];
-      instructions.forEach(function (instruction) {
-        var statementBuilder = new ExpressionBuilderVisitor();
+      const result = [];
+
+      for (const instruction of instructions) {
+        const statementBuilder = new ExpressionBuilderVisitor();
         instruction.visit(statementBuilder);
         result.push(statementBuilder.toString());
-      });
-      stack.forEach(function (expr, i) {
-        var statementBuilder = new ExpressionBuilderVisitor();
+      }
+
+      for (let i = 0, ii = stack.length; i < ii; i++) {
+        const expr = stack[i],
+              statementBuilder = new ExpressionBuilderVisitor();
         expr.visit(statementBuilder);
-        var min = range[i * 2],
-            max = range[i * 2 + 1];
-        var out = [statementBuilder.toString()];
+        const min = range[i * 2],
+              max = range[i * 2 + 1];
+        const out = [statementBuilder.toString()];
 
         if (min > expr.min) {
           out.unshift("Math.max(", min, ", ");
@@ -1434,7 +1436,8 @@ var PostScriptCompiler = function PostScriptCompilerClosure() {
         out.unshift("dest[destOffset + ", i, "] = ");
         out.push(";");
         result.push(out.join(""));
-      });
+      }
+
       return result.join("\n");
     }
 

+ 84 - 84
lib/core/image.js

@@ -32,7 +32,7 @@ var _primitives = require("./primitives.js");
 
 var _colorspace = require("./colorspace.js");
 
-var _stream = require("./stream.js");
+var _decode_stream = require("./decode_stream.js");
 
 var _jpeg_stream = require("./jpeg_stream.js");
 
@@ -51,7 +51,7 @@ function decodeAndClamp(value, addend, coefficient, max) {
 }
 
 function resizeImageMask(src, bpc, w1, h1, w2, h2) {
-  var length = w2 * h2;
+  const length = w2 * h2;
   let dest;
 
   if (bpc <= 8) {
@@ -62,15 +62,15 @@ function resizeImageMask(src, bpc, w1, h1, w2, h2) {
     dest = new Uint32Array(length);
   }
 
-  var xRatio = w1 / w2;
-  var yRatio = h1 / h2;
-  var i,
+  const xRatio = w1 / w2;
+  const yRatio = h1 / h2;
+  let i,
       j,
       py,
       newIndex = 0,
       oldIndex;
-  var xScaled = new Uint16Array(w2);
-  var w1Scanline = w1;
+  const xScaled = new Uint16Array(w2);
+  const w1Scanline = w1;
 
   for (i = 0; i < w2; i++) {
     xScaled[i] = Math.floor(i * xRatio);
@@ -101,13 +101,13 @@ class PDFImage {
     localColorSpaceCache
   }) {
     this.image = image;
-    var dict = image.dict;
+    const dict = image.dict;
     const filter = dict.get("Filter");
 
     if ((0, _primitives.isName)(filter)) {
       switch (filter.name) {
         case "JPXDecode":
-          var jpxImage = new _jpx.JpxImage();
+          const jpxImage = new _jpx.JpxImage();
           jpxImage.parseImageProperties(image.stream);
           image.stream.reset();
           image.width = jpxImage.width;
@@ -141,7 +141,7 @@ class PDFImage {
     this.interpolate = dict.get("Interpolate", "I") || false;
     this.imageMask = dict.get("ImageMask", "IM") || false;
     this.matte = dict.get("Matte") || false;
-    var bitsPerComponent = image.bitsPerComponent;
+    let bitsPerComponent = image.bitsPerComponent;
 
     if (!bitsPerComponent) {
       bitsPerComponent = dict.get("BitsPerComponent", "BPC");
@@ -196,14 +196,14 @@ class PDFImage {
 
     if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !_colorspace.ColorSpace.isDefaultDecode(this.decode, 1))) {
       this.needsDecode = true;
-      var max = (1 << bitsPerComponent) - 1;
+      const max = (1 << bitsPerComponent) - 1;
       this.decodeCoefficients = [];
       this.decodeAddends = [];
       const isIndexed = this.colorSpace && this.colorSpace.name === "Indexed";
 
-      for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
-        var dmin = this.decode[i];
-        var dmax = this.decode[i + 1];
+      for (let i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
+        const dmin = this.decode[i];
+        const dmax = this.decode[i + 1];
         this.decodeCoefficients[j] = isIndexed ? (dmax - dmin) / max : dmax - dmin;
         this.decodeAddends[j] = isIndexed ? dmin : max * dmin;
       }
@@ -220,8 +220,8 @@ class PDFImage {
       });
     } else if (mask) {
       if ((0, _primitives.isStream)(mask)) {
-        var maskDict = mask.dict,
-            imageMask = maskDict.get("ImageMask", "IM");
+        const maskDict = mask.dict,
+              imageMask = maskDict.get("ImageMask", "IM");
 
         if (!imageMask) {
           (0, _util.warn)("Ignoring /Mask in image without /ImageMask.");
@@ -285,10 +285,10 @@ class PDFImage {
     imageIsFromDecodeStream,
     inverseDecode
   }) {
-    var computedLength = (width + 7 >> 3) * height;
-    var actualLength = imgArray.byteLength;
-    var haveFullData = computedLength === actualLength;
-    var data, i;
+    const computedLength = (width + 7 >> 3) * height;
+    const actualLength = imgArray.byteLength;
+    const haveFullData = computedLength === actualLength;
+    let data, i;
 
     if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
       data = imgArray;
@@ -326,12 +326,12 @@ class PDFImage {
   }
 
   decodeBuffer(buffer) {
-    var bpc = this.bpc;
-    var numComps = this.numComps;
-    var decodeAddends = this.decodeAddends;
-    var decodeCoefficients = this.decodeCoefficients;
-    var max = (1 << bpc) - 1;
-    var i, ii;
+    const bpc = this.bpc;
+    const numComps = this.numComps;
+    const decodeAddends = this.decodeAddends;
+    const decodeCoefficients = this.decodeCoefficients;
+    const max = (1 << bpc) - 1;
+    let i, ii;
 
     if (bpc === 1) {
       for (i = 0, ii = buffer.length; i < ii; i++) {
@@ -341,10 +341,10 @@ class PDFImage {
       return;
     }
 
-    var index = 0;
+    let index = 0;
 
     for (i = 0, ii = this.width * this.height; i < ii; i++) {
-      for (var j = 0; j < numComps; j++) {
+      for (let j = 0; j < numComps; j++) {
         buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max);
         index++;
       }
@@ -352,17 +352,17 @@ class PDFImage {
   }
 
   getComponents(buffer) {
-    var bpc = this.bpc;
+    const bpc = this.bpc;
 
     if (bpc === 8) {
       return buffer;
     }
 
-    var width = this.width;
-    var height = this.height;
-    var numComps = this.numComps;
-    var length = width * height * numComps;
-    var bufferPos = 0;
+    const width = this.width;
+    const height = this.height;
+    const numComps = this.numComps;
+    const length = width * height * numComps;
+    let bufferPos = 0;
     let output;
 
     if (bpc <= 8) {
@@ -373,16 +373,16 @@ class PDFImage {
       output = new Uint32Array(length);
     }
 
-    var rowComps = width * numComps;
-    var max = (1 << bpc) - 1;
-    var i = 0,
+    const rowComps = width * numComps;
+    const max = (1 << bpc) - 1;
+    let i = 0,
         ii,
         buf;
 
     if (bpc === 1) {
-      var mask, loop1End, loop2End;
+      let mask, loop1End, loop2End;
 
-      for (var j = 0; j < height; j++) {
+      for (let j = 0; j < height; j++) {
         loop1End = i + (rowComps & ~7);
         loop2End = i + rowComps;
 
@@ -410,7 +410,7 @@ class PDFImage {
         }
       }
     } else {
-      var bits = 0;
+      let bits = 0;
       buf = 0;
 
       for (i = 0, ii = length; i < ii; ++i) {
@@ -424,7 +424,7 @@ class PDFImage {
           bits += 8;
         }
 
-        var remainingBits = bits - bpc;
+        const remainingBits = bits - bpc;
         let value = buf >> remainingBits;
 
         if (value < 0) {
@@ -443,9 +443,9 @@ class PDFImage {
   }
 
   fillOpacity(rgbaBuf, width, height, actualHeight, image) {
-    var smask = this.smask;
-    var mask = this.mask;
-    var alphaBuf, sw, sh, i, ii, j;
+    const smask = this.smask;
+    const mask = this.mask;
+    let alphaBuf, sw, sh, i, ii, j;
 
     if (smask) {
       sw = smask.width;
@@ -473,15 +473,15 @@ class PDFImage {
         }
       } else if (Array.isArray(mask)) {
         alphaBuf = new Uint8ClampedArray(width * height);
-        var numComps = this.numComps;
+        const numComps = this.numComps;
 
         for (i = 0, ii = width * height; i < ii; ++i) {
-          var opacity = 0;
-          var imageOffset = i * numComps;
+          let opacity = 0;
+          const imageOffset = i * numComps;
 
           for (j = 0; j < numComps; ++j) {
-            var color = image[imageOffset + j];
-            var maskOffset = j * 2;
+            const color = image[imageOffset + j];
+            const maskOffset = j * 2;
 
             if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
               opacity = 255;
@@ -508,20 +508,20 @@ class PDFImage {
   }
 
   undoPreblend(buffer, width, height) {
-    var matte = this.smask && this.smask.matte;
+    const matte = this.smask && this.smask.matte;
 
     if (!matte) {
       return;
     }
 
-    var matteRgb = this.colorSpace.getRgb(matte, 0);
-    var matteR = matteRgb[0];
-    var matteG = matteRgb[1];
-    var matteB = matteRgb[2];
-    var length = width * height * 4;
+    const matteRgb = this.colorSpace.getRgb(matte, 0);
+    const matteR = matteRgb[0];
+    const matteG = matteRgb[1];
+    const matteB = matteRgb[2];
+    const length = width * height * 4;
 
-    for (var i = 0; i < length; i += 4) {
-      var alpha = buffer[i + 3];
+    for (let i = 0; i < length; i += 4) {
+      const alpha = buffer[i + 3];
 
       if (alpha === 0) {
         buffer[i] = 255;
@@ -530,7 +530,7 @@ class PDFImage {
         continue;
       }
 
-      var k = 255 / alpha;
+      const k = 255 / alpha;
       buffer[i] = (buffer[i] - matteR) * k + matteR;
       buffer[i + 1] = (buffer[i + 1] - matteG) * k + matteG;
       buffer[i + 2] = (buffer[i + 2] - matteB) * k + matteB;
@@ -538,23 +538,23 @@ class PDFImage {
   }
 
   createImageData(forceRGBA = false) {
-    var drawWidth = this.drawWidth;
-    var drawHeight = this.drawHeight;
-    var imgData = {
+    const drawWidth = this.drawWidth;
+    const drawHeight = this.drawHeight;
+    const imgData = {
       width: drawWidth,
       height: drawHeight,
       kind: 0,
       data: null
     };
-    var numComps = this.numComps;
-    var originalWidth = this.width;
-    var originalHeight = this.height;
-    var bpc = this.bpc;
-    var rowBytes = originalWidth * numComps * bpc + 7 >> 3;
-    var imgArray;
+    const numComps = this.numComps;
+    const originalWidth = this.width;
+    const originalHeight = this.height;
+    const bpc = this.bpc;
+    const rowBytes = originalWidth * numComps * bpc + 7 >> 3;
+    let imgArray;
 
     if (!forceRGBA) {
-      var kind;
+      let kind;
 
       if (this.colorSpace.name === "DeviceGray" && bpc === 1) {
         kind = _util.ImageKind.GRAYSCALE_1BPP;
@@ -566,19 +566,19 @@ class PDFImage {
         imgData.kind = kind;
         imgArray = this.getImageBytes(originalHeight * rowBytes);
 
-        if (this.image instanceof _stream.DecodeStream) {
+        if (this.image instanceof _decode_stream.DecodeStream) {
           imgData.data = imgArray;
         } else {
-          var newArray = new Uint8ClampedArray(imgArray.length);
+          const newArray = new Uint8ClampedArray(imgArray.length);
           newArray.set(imgArray);
           imgData.data = newArray;
         }
 
         if (this.needsDecode) {
           (0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP, "PDFImage.createImageData: The image must be grayscale.");
-          var buffer = imgData.data;
+          const buffer = imgData.data;
 
-          for (var i = 0, ii = buffer.length; i < ii; i++) {
+          for (let i = 0, ii = buffer.length; i < ii; i++) {
             buffer[i] ^= 0xff;
           }
         }
@@ -603,9 +603,9 @@ class PDFImage {
     }
 
     imgArray = this.getImageBytes(originalHeight * rowBytes);
-    var actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;
-    var comps = this.getComponents(imgArray);
-    var alpha01, maybeUndoPreblend;
+    const actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight;
+    const comps = this.getComponents(imgArray);
+    let alpha01, maybeUndoPreblend;
 
     if (!forceRGBA && !this.smask && !this.mask) {
       imgData.kind = _util.ImageKind.RGB_24BPP;
@@ -634,19 +634,19 @@ class PDFImage {
   }
 
   fillGrayBuffer(buffer) {
-    var numComps = this.numComps;
+    const numComps = this.numComps;
 
     if (numComps !== 1) {
       throw new _util.FormatError(`Reading gray scale from a color image: ${numComps}`);
     }
 
-    var width = this.width;
-    var height = this.height;
-    var bpc = this.bpc;
-    var rowBytes = width * numComps * bpc + 7 >> 3;
-    var imgArray = this.getImageBytes(height * rowBytes);
-    var comps = this.getComponents(imgArray);
-    var i, length;
+    const width = this.width;
+    const height = this.height;
+    const bpc = this.bpc;
+    const rowBytes = width * numComps * bpc + 7 >> 3;
+    const imgArray = this.getImageBytes(height * rowBytes);
+    const comps = this.getComponents(imgArray);
+    let i, length;
 
     if (bpc === 1) {
       length = width * height;
@@ -669,7 +669,7 @@ class PDFImage {
     }
 
     length = width * height;
-    var scale = 255 / ((1 << bpc) - 1);
+    const scale = 255 / ((1 << bpc) - 1);
 
     for (i = 0; i < length; ++i) {
       buffer[i] = scale * comps[i];

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1236 - 1242
lib/core/jbig2.js


+ 12 - 19
lib/core/jbig2_stream.js

@@ -28,34 +28,28 @@ exports.Jbig2Stream = void 0;
 
 var _primitives = require("./primitives.js");
 
-var _stream = require("./stream.js");
+var _decode_stream = require("./decode_stream.js");
 
 var _jbig = require("./jbig2.js");
 
 var _util = require("../shared/util.js");
 
-const Jbig2Stream = function Jbig2StreamClosure() {
-  function Jbig2Stream(stream, maybeLength, dict, params) {
+class Jbig2Stream extends _decode_stream.DecodeStream {
+  constructor(stream, maybeLength, params) {
+    super(maybeLength);
     this.stream = stream;
+    this.dict = stream.dict;
     this.maybeLength = maybeLength;
-    this.dict = dict;
     this.params = params;
-
-    _stream.DecodeStream.call(this, maybeLength);
   }
 
-  Jbig2Stream.prototype = Object.create(_stream.DecodeStream.prototype);
-  Object.defineProperty(Jbig2Stream.prototype, "bytes", {
-    get() {
-      return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
-    },
-
-    configurable: true
-  });
+  get bytes() {
+    return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
+  }
 
-  Jbig2Stream.prototype.ensureBuffer = function (requested) {};
+  ensureBuffer(requested) {}
 
-  Jbig2Stream.prototype.readBlock = function () {
+  readBlock() {
     if (this.eof) {
       return;
     }
@@ -91,9 +85,8 @@ const Jbig2Stream = function Jbig2StreamClosure() {
     this.buffer = data;
     this.bufferLength = dataLength;
     this.eof = true;
-  };
+  }
 
-  return Jbig2Stream;
-}();
+}
 
 exports.Jbig2Stream = Jbig2Stream;

+ 12 - 18
lib/core/jpeg_stream.js

@@ -26,7 +26,7 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.JpegStream = void 0;
 
-var _stream = require("./stream.js");
+var _decode_stream = require("./decode_stream.js");
 
 var _primitives = require("./primitives.js");
 
@@ -34,8 +34,8 @@ var _jpg = require("./jpg.js");
 
 var _util = require("../shared/util.js");
 
-const JpegStream = function JpegStreamClosure() {
-  function JpegStream(stream, maybeLength, dict, params) {
+class JpegStream extends _decode_stream.DecodeStream {
+  constructor(stream, maybeLength, params) {
     let ch;
 
     while ((ch = stream.getByte()) !== -1) {
@@ -45,25 +45,20 @@ const JpegStream = function JpegStreamClosure() {
       }
     }
 
+    super(maybeLength);
     this.stream = stream;
+    this.dict = stream.dict;
     this.maybeLength = maybeLength;
-    this.dict = dict;
     this.params = params;
-
-    _stream.DecodeStream.call(this, maybeLength);
   }
 
-  JpegStream.prototype = Object.create(_stream.DecodeStream.prototype);
-  Object.defineProperty(JpegStream.prototype, "bytes", {
-    get: function JpegStream_bytes() {
-      return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
-    },
-    configurable: true
-  });
+  get bytes() {
+    return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
+  }
 
-  JpegStream.prototype.ensureBuffer = function (requested) {};
+  ensureBuffer(requested) {}
 
-  JpegStream.prototype.readBlock = function () {
+  readBlock() {
     if (this.eof) {
       return;
     }
@@ -114,9 +109,8 @@ const JpegStream = function JpegStreamClosure() {
     this.buffer = data;
     this.bufferLength = data.length;
     this.eof = true;
-  };
+  }
 
-  return JpegStream;
-}();
+}
 
 exports.JpegStream = JpegStream;

+ 979 - 982
lib/core/jpg.js

@@ -47,68 +47,46 @@ class DNLMarkerError extends _util.BaseException {
 
 class EOIMarkerError extends _util.BaseException {}
 
-var JpegImage = function JpegImageClosure() {
-  var dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
-  var dctCos1 = 4017;
-  var dctSin1 = 799;
-  var dctCos3 = 3406;
-  var dctSin3 = 2276;
-  var dctCos6 = 1567;
-  var dctSin6 = 3784;
-  var dctSqrt2 = 5793;
-  var dctSqrt1d2 = 2896;
-
-  function JpegImage({
-    decodeTransform = null,
-    colorTransform = -1
-  } = {}) {
-    this._decodeTransform = decodeTransform;
-    this._colorTransform = colorTransform;
+const dctZigZag = new Uint8Array([0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63]);
+const dctCos1 = 4017;
+const dctSin1 = 799;
+const dctCos3 = 3406;
+const dctSin3 = 2276;
+const dctCos6 = 1567;
+const dctSin6 = 3784;
+const dctSqrt2 = 5793;
+const dctSqrt1d2 = 2896;
+
+function buildHuffmanTable(codeLengths, values) {
+  let k = 0,
+      i,
+      j,
+      length = 16;
+
+  while (length > 0 && !codeLengths[length - 1]) {
+    length--;
   }
 
-  function buildHuffmanTable(codeLengths, values) {
-    var k = 0,
-        code = [],
-        i,
-        j,
-        length = 16;
+  const code = [{
+    children: [],
+    index: 0
+  }];
+  let p = code[0],
+      q;
 
-    while (length > 0 && !codeLengths[length - 1]) {
-      length--;
-    }
-
-    code.push({
-      children: [],
-      index: 0
-    });
-    var p = code[0],
-        q;
+  for (i = 0; i < length; i++) {
+    for (j = 0; j < codeLengths[i]; j++) {
+      p = code.pop();
+      p.children[p.index] = values[k];
 
-    for (i = 0; i < length; i++) {
-      for (j = 0; j < codeLengths[i]; j++) {
+      while (p.index > 0) {
         p = code.pop();
-        p.children[p.index] = values[k];
-
-        while (p.index > 0) {
-          p = code.pop();
-        }
-
-        p.index++;
-        code.push(p);
-
-        while (code.length <= i) {
-          code.push(q = {
-            children: [],
-            index: 0
-          });
-          p.children[p.index] = q.children;
-          p = q;
-        }
-
-        k++;
       }
 
-      if (i + 1 < length) {
+      p.index++;
+      code.push(p);
+
+      while (code.length <= i) {
         code.push(q = {
           children: [],
           index: 0
@@ -116,1146 +94,1165 @@ var JpegImage = function JpegImageClosure() {
         p.children[p.index] = q.children;
         p = q;
       }
+
+      k++;
     }
 
-    return code[0].children;
+    if (i + 1 < length) {
+      code.push(q = {
+        children: [],
+        index: 0
+      });
+      p.children[p.index] = q.children;
+      p = q;
+    }
   }
 
-  function getBlockBufferOffset(component, row, col) {
-    return 64 * ((component.blocksPerLine + 1) * row + col);
-  }
+  return code[0].children;
+}
 
-  function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive, parseDNLMarker = false) {
-    var mcusPerLine = frame.mcusPerLine;
-    var progressive = frame.progressive;
-    const startOffset = offset;
-    let bitsData = 0,
-        bitsCount = 0;
-
-    function readBit() {
-      if (bitsCount > 0) {
-        bitsCount--;
-        return bitsData >> bitsCount & 1;
-      }
+function getBlockBufferOffset(component, row, col) {
+  return 64 * ((component.blocksPerLine + 1) * row + col);
+}
 
-      bitsData = data[offset++];
+function decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive, parseDNLMarker = false) {
+  const mcusPerLine = frame.mcusPerLine;
+  const progressive = frame.progressive;
+  const startOffset = offset;
+  let bitsData = 0,
+      bitsCount = 0;
 
-      if (bitsData === 0xff) {
-        var nextByte = data[offset++];
+  function readBit() {
+    if (bitsCount > 0) {
+      bitsCount--;
+      return bitsData >> bitsCount & 1;
+    }
 
-        if (nextByte) {
-          if (nextByte === 0xdc && parseDNLMarker) {
-            offset += 2;
-            const scanLines = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
+    bitsData = data[offset++];
 
-            if (scanLines > 0 && scanLines !== frame.scanLines) {
-              throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data", scanLines);
-            }
-          } else if (nextByte === 0xd9) {
-            if (parseDNLMarker) {
-              const maybeScanLines = blockRow * (frame.precision === 8 ? 8 : 0);
+    if (bitsData === 0xff) {
+      const nextByte = data[offset++];
 
-              if (maybeScanLines > 0 && Math.round(frame.scanLines / maybeScanLines) >= 10) {
-                throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, " + "possibly caused by incorrect `scanLines` parameter", maybeScanLines);
-              }
-            }
+      if (nextByte) {
+        if (nextByte === 0xdc && parseDNLMarker) {
+          offset += 2;
+          const scanLines = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
 
-            throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data");
+          if (scanLines > 0 && scanLines !== frame.scanLines) {
+            throw new DNLMarkerError("Found DNL marker (0xFFDC) while parsing scan data", scanLines);
+          }
+        } else if (nextByte === 0xd9) {
+          if (parseDNLMarker) {
+            const maybeScanLines = blockRow * (frame.precision === 8 ? 8 : 0);
+
+            if (maybeScanLines > 0 && Math.round(frame.scanLines / maybeScanLines) >= 10) {
+              throw new DNLMarkerError("Found EOI marker (0xFFD9) while parsing scan data, " + "possibly caused by incorrect `scanLines` parameter", maybeScanLines);
+            }
           }
 
-          throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
+          throw new EOIMarkerError("Found EOI marker (0xFFD9) while parsing scan data");
         }
-      }
 
-      bitsCount = 7;
-      return bitsData >>> 7;
+        throw new JpegError(`unexpected marker ${(bitsData << 8 | nextByte).toString(16)}`);
+      }
     }
 
-    function decodeHuffman(tree) {
-      var node = tree;
+    bitsCount = 7;
+    return bitsData >>> 7;
+  }
 
-      while (true) {
-        node = node[readBit()];
+  function decodeHuffman(tree) {
+    let node = tree;
 
-        switch (typeof node) {
-          case "number":
-            return node;
+    while (true) {
+      node = node[readBit()];
 
-          case "object":
-            continue;
-        }
+      switch (typeof node) {
+        case "number":
+          return node;
 
-        throw new JpegError("invalid huffman sequence");
+        case "object":
+          continue;
       }
-    }
 
-    function receive(length) {
-      var n = 0;
+      throw new JpegError("invalid huffman sequence");
+    }
+  }
 
-      while (length > 0) {
-        n = n << 1 | readBit();
-        length--;
-      }
+  function receive(length) {
+    let n = 0;
 
-      return n;
+    while (length > 0) {
+      n = n << 1 | readBit();
+      length--;
     }
 
-    function receiveAndExtend(length) {
-      if (length === 1) {
-        return readBit() === 1 ? 1 : -1;
-      }
+    return n;
+  }
 
-      var n = receive(length);
+  function receiveAndExtend(length) {
+    if (length === 1) {
+      return readBit() === 1 ? 1 : -1;
+    }
 
-      if (n >= 1 << length - 1) {
-        return n;
-      }
+    const n = receive(length);
 
-      return n + (-1 << length) + 1;
+    if (n >= 1 << length - 1) {
+      return n;
     }
 
-    function decodeBaseline(component, blockOffset) {
-      var t = decodeHuffman(component.huffmanTableDC);
-      var diff = t === 0 ? 0 : receiveAndExtend(t);
-      component.blockData[blockOffset] = component.pred += diff;
-      var k = 1;
+    return n + (-1 << length) + 1;
+  }
 
-      while (k < 64) {
-        var rs = decodeHuffman(component.huffmanTableAC);
-        var s = rs & 15,
-            r = rs >> 4;
+  function decodeBaseline(component, blockOffset) {
+    const t = decodeHuffman(component.huffmanTableDC);
+    const diff = t === 0 ? 0 : receiveAndExtend(t);
+    component.blockData[blockOffset] = component.pred += diff;
+    let k = 1;
 
-        if (s === 0) {
-          if (r < 15) {
-            break;
-          }
+    while (k < 64) {
+      const rs = decodeHuffman(component.huffmanTableAC);
+      const s = rs & 15,
+            r = rs >> 4;
 
-          k += 16;
-          continue;
+      if (s === 0) {
+        if (r < 15) {
+          break;
         }
 
-        k += r;
-        var z = dctZigZag[k];
-        component.blockData[blockOffset + z] = receiveAndExtend(s);
-        k++;
+        k += 16;
+        continue;
       }
-    }
 
-    function decodeDCFirst(component, blockOffset) {
-      var t = decodeHuffman(component.huffmanTableDC);
-      var diff = t === 0 ? 0 : receiveAndExtend(t) << successive;
-      component.blockData[blockOffset] = component.pred += diff;
+      k += r;
+      const z = dctZigZag[k];
+      component.blockData[blockOffset + z] = receiveAndExtend(s);
+      k++;
     }
+  }
 
-    function decodeDCSuccessive(component, blockOffset) {
-      component.blockData[blockOffset] |= readBit() << successive;
-    }
+  function decodeDCFirst(component, blockOffset) {
+    const t = decodeHuffman(component.huffmanTableDC);
+    const diff = t === 0 ? 0 : receiveAndExtend(t) << successive;
+    component.blockData[blockOffset] = component.pred += diff;
+  }
+
+  function decodeDCSuccessive(component, blockOffset) {
+    component.blockData[blockOffset] |= readBit() << successive;
+  }
 
-    var eobrun = 0;
+  let eobrun = 0;
 
-    function decodeACFirst(component, blockOffset) {
-      if (eobrun > 0) {
-        eobrun--;
-        return;
-      }
+  function decodeACFirst(component, blockOffset) {
+    if (eobrun > 0) {
+      eobrun--;
+      return;
+    }
 
-      var k = spectralStart,
-          e = spectralEnd;
+    let k = spectralStart;
+    const e = spectralEnd;
 
-      while (k <= e) {
-        var rs = decodeHuffman(component.huffmanTableAC);
-        var s = rs & 15,
+    while (k <= e) {
+      const rs = decodeHuffman(component.huffmanTableAC);
+      const s = rs & 15,
             r = rs >> 4;
 
-        if (s === 0) {
-          if (r < 15) {
-            eobrun = receive(r) + (1 << r) - 1;
-            break;
-          }
-
-          k += 16;
-          continue;
+      if (s === 0) {
+        if (r < 15) {
+          eobrun = receive(r) + (1 << r) - 1;
+          break;
         }
 
-        k += r;
-        var z = dctZigZag[k];
-        component.blockData[blockOffset + z] = receiveAndExtend(s) * (1 << successive);
-        k++;
+        k += 16;
+        continue;
       }
-    }
-
-    var successiveACState = 0,
-        successiveACNextValue;
 
-    function decodeACSuccessive(component, blockOffset) {
-      var k = spectralStart;
-      var e = spectralEnd;
-      var r = 0;
-      var s;
-      var rs;
-
-      while (k <= e) {
-        const offsetZ = blockOffset + dctZigZag[k];
-        const sign = component.blockData[offsetZ] < 0 ? -1 : 1;
-
-        switch (successiveACState) {
-          case 0:
-            rs = decodeHuffman(component.huffmanTableAC);
-            s = rs & 15;
-            r = rs >> 4;
+      k += r;
+      const z = dctZigZag[k];
+      component.blockData[blockOffset + z] = receiveAndExtend(s) * (1 << successive);
+      k++;
+    }
+  }
 
-            if (s === 0) {
-              if (r < 15) {
-                eobrun = receive(r) + (1 << r);
-                successiveACState = 4;
-              } else {
-                r = 16;
-                successiveACState = 1;
-              }
+  let successiveACState = 0,
+      successiveACNextValue;
+
+  function decodeACSuccessive(component, blockOffset) {
+    let k = spectralStart;
+    const e = spectralEnd;
+    let r = 0;
+    let s;
+    let rs;
+
+    while (k <= e) {
+      const offsetZ = blockOffset + dctZigZag[k];
+      const sign = component.blockData[offsetZ] < 0 ? -1 : 1;
+
+      switch (successiveACState) {
+        case 0:
+          rs = decodeHuffman(component.huffmanTableAC);
+          s = rs & 15;
+          r = rs >> 4;
+
+          if (s === 0) {
+            if (r < 15) {
+              eobrun = receive(r) + (1 << r);
+              successiveACState = 4;
             } else {
-              if (s !== 1) {
-                throw new JpegError("invalid ACn encoding");
-              }
-
-              successiveACNextValue = receiveAndExtend(s);
-              successiveACState = r ? 2 : 3;
+              r = 16;
+              successiveACState = 1;
+            }
+          } else {
+            if (s !== 1) {
+              throw new JpegError("invalid ACn encoding");
             }
 
-            continue;
-
-          case 1:
-          case 2:
-            if (component.blockData[offsetZ]) {
-              component.blockData[offsetZ] += sign * (readBit() << successive);
-            } else {
-              r--;
+            successiveACNextValue = receiveAndExtend(s);
+            successiveACState = r ? 2 : 3;
+          }
 
-              if (r === 0) {
-                successiveACState = successiveACState === 2 ? 3 : 0;
-              }
-            }
+          continue;
 
-            break;
+        case 1:
+        case 2:
+          if (component.blockData[offsetZ]) {
+            component.blockData[offsetZ] += sign * (readBit() << successive);
+          } else {
+            r--;
 
-          case 3:
-            if (component.blockData[offsetZ]) {
-              component.blockData[offsetZ] += sign * (readBit() << successive);
-            } else {
-              component.blockData[offsetZ] = successiveACNextValue << successive;
-              successiveACState = 0;
+            if (r === 0) {
+              successiveACState = successiveACState === 2 ? 3 : 0;
             }
+          }
 
-            break;
+          break;
 
-          case 4:
-            if (component.blockData[offsetZ]) {
-              component.blockData[offsetZ] += sign * (readBit() << successive);
-            }
+        case 3:
+          if (component.blockData[offsetZ]) {
+            component.blockData[offsetZ] += sign * (readBit() << successive);
+          } else {
+            component.blockData[offsetZ] = successiveACNextValue << successive;
+            successiveACState = 0;
+          }
 
-            break;
-        }
+          break;
 
-        k++;
+        case 4:
+          if (component.blockData[offsetZ]) {
+            component.blockData[offsetZ] += sign * (readBit() << successive);
+          }
+
+          break;
       }
 
-      if (successiveACState === 4) {
-        eobrun--;
+      k++;
+    }
+
+    if (successiveACState === 4) {
+      eobrun--;
 
-        if (eobrun === 0) {
-          successiveACState = 0;
-        }
+      if (eobrun === 0) {
+        successiveACState = 0;
       }
     }
+  }
 
-    let blockRow = 0;
+  let blockRow = 0;
 
-    function decodeMcu(component, decode, mcu, row, col) {
-      var mcuRow = mcu / mcusPerLine | 0;
-      var mcuCol = mcu % mcusPerLine;
-      blockRow = mcuRow * component.v + row;
-      var blockCol = mcuCol * component.h + col;
-      const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);
-      decode(component, blockOffset);
-    }
+  function decodeMcu(component, decode, mcu, row, col) {
+    const mcuRow = mcu / mcusPerLine | 0;
+    const mcuCol = mcu % mcusPerLine;
+    blockRow = mcuRow * component.v + row;
+    const blockCol = mcuCol * component.h + col;
+    const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);
+    decode(component, blockOffset);
+  }
 
-    function decodeBlock(component, decode, mcu) {
-      blockRow = mcu / component.blocksPerLine | 0;
-      var blockCol = mcu % component.blocksPerLine;
-      const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);
-      decode(component, blockOffset);
-    }
+  function decodeBlock(component, decode, mcu) {
+    blockRow = mcu / component.blocksPerLine | 0;
+    const blockCol = mcu % component.blocksPerLine;
+    const blockOffset = getBlockBufferOffset(component, blockRow, blockCol);
+    decode(component, blockOffset);
+  }
 
-    var componentsLength = components.length;
-    var component, i, j, k, n;
-    var decodeFn;
+  const componentsLength = components.length;
+  let component, i, j, k, n;
+  let decodeFn;
 
-    if (progressive) {
-      if (spectralStart === 0) {
-        decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
-      } else {
-        decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
-      }
+  if (progressive) {
+    if (spectralStart === 0) {
+      decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive;
     } else {
-      decodeFn = decodeBaseline;
+      decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive;
     }
+  } else {
+    decodeFn = decodeBaseline;
+  }
 
-    var mcu = 0,
-        fileMarker;
-    var mcuExpected;
+  let mcu = 0,
+      fileMarker;
+  let mcuExpected;
 
-    if (componentsLength === 1) {
-      mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
-    } else {
-      mcuExpected = mcusPerLine * frame.mcusPerColumn;
-    }
+  if (componentsLength === 1) {
+    mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn;
+  } else {
+    mcuExpected = mcusPerLine * frame.mcusPerColumn;
+  }
 
-    var h, v;
+  let h, v;
 
-    while (mcu <= mcuExpected) {
-      var mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
+  while (mcu <= mcuExpected) {
+    const mcuToRead = resetInterval ? Math.min(mcuExpected - mcu, resetInterval) : mcuExpected;
 
-      if (mcuToRead > 0) {
-        for (i = 0; i < componentsLength; i++) {
-          components[i].pred = 0;
-        }
+    if (mcuToRead > 0) {
+      for (i = 0; i < componentsLength; i++) {
+        components[i].pred = 0;
+      }
 
-        eobrun = 0;
+      eobrun = 0;
 
-        if (componentsLength === 1) {
-          component = components[0];
+      if (componentsLength === 1) {
+        component = components[0];
 
-          for (n = 0; n < mcuToRead; n++) {
-            decodeBlock(component, decodeFn, mcu);
-            mcu++;
-          }
-        } else {
-          for (n = 0; n < mcuToRead; n++) {
-            for (i = 0; i < componentsLength; i++) {
-              component = components[i];
-              h = component.h;
-              v = component.v;
-
-              for (j = 0; j < v; j++) {
-                for (k = 0; k < h; k++) {
-                  decodeMcu(component, decodeFn, mcu, j, k);
-                }
+        for (n = 0; n < mcuToRead; n++) {
+          decodeBlock(component, decodeFn, mcu);
+          mcu++;
+        }
+      } else {
+        for (n = 0; n < mcuToRead; n++) {
+          for (i = 0; i < componentsLength; i++) {
+            component = components[i];
+            h = component.h;
+            v = component.v;
+
+            for (j = 0; j < v; j++) {
+              for (k = 0; k < h; k++) {
+                decodeMcu(component, decodeFn, mcu, j, k);
               }
             }
-
-            mcu++;
           }
+
+          mcu++;
         }
       }
+    }
 
-      bitsCount = 0;
-      fileMarker = findNextFileMarker(data, offset);
-
-      if (!fileMarker) {
-        break;
-      }
+    bitsCount = 0;
+    fileMarker = findNextFileMarker(data, offset);
 
-      if (fileMarker.invalid) {
-        const partialMsg = mcuToRead > 0 ? "unexpected" : "excessive";
-        (0, _util.warn)(`decodeScan - ${partialMsg} MCU data, current marker is: ${fileMarker.invalid}`);
-        offset = fileMarker.offset;
-      }
+    if (!fileMarker) {
+      break;
+    }
 
-      if (fileMarker.marker >= 0xffd0 && fileMarker.marker <= 0xffd7) {
-        offset += 2;
-      } else {
-        break;
-      }
+    if (fileMarker.invalid) {
+      const partialMsg = mcuToRead > 0 ? "unexpected" : "excessive";
+      (0, _util.warn)(`decodeScan - ${partialMsg} MCU data, current marker is: ${fileMarker.invalid}`);
+      offset = fileMarker.offset;
     }
 
-    return offset - startOffset;
+    if (fileMarker.marker >= 0xffd0 && fileMarker.marker <= 0xffd7) {
+      offset += 2;
+    } else {
+      break;
+    }
   }
 
-  function quantizeAndInverse(component, blockBufferOffset, p) {
-    var qt = component.quantizationTable,
+  return offset - startOffset;
+}
+
+function quantizeAndInverse(component, blockBufferOffset, p) {
+  const qt = component.quantizationTable,
         blockData = component.blockData;
-    var v0, v1, v2, v3, v4, v5, v6, v7;
-    var p0, p1, p2, p3, p4, p5, p6, p7;
-    var t;
+  let v0, v1, v2, v3, v4, v5, v6, v7;
+  let p0, p1, p2, p3, p4, p5, p6, p7;
+  let t;
 
-    if (!qt) {
-      throw new JpegError("missing required Quantization Table.");
+  if (!qt) {
+    throw new JpegError("missing required Quantization Table.");
+  }
+
+  for (let row = 0; row < 64; row += 8) {
+    p0 = blockData[blockBufferOffset + row];
+    p1 = blockData[blockBufferOffset + row + 1];
+    p2 = blockData[blockBufferOffset + row + 2];
+    p3 = blockData[blockBufferOffset + row + 3];
+    p4 = blockData[blockBufferOffset + row + 4];
+    p5 = blockData[blockBufferOffset + row + 5];
+    p6 = blockData[blockBufferOffset + row + 6];
+    p7 = blockData[blockBufferOffset + row + 7];
+    p0 *= qt[row];
+
+    if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
+      t = dctSqrt2 * p0 + 512 >> 10;
+      p[row] = t;
+      p[row + 1] = t;
+      p[row + 2] = t;
+      p[row + 3] = t;
+      p[row + 4] = t;
+      p[row + 5] = t;
+      p[row + 6] = t;
+      p[row + 7] = t;
+      continue;
     }
 
-    for (var row = 0; row < 64; row += 8) {
-      p0 = blockData[blockBufferOffset + row];
-      p1 = blockData[blockBufferOffset + row + 1];
-      p2 = blockData[blockBufferOffset + row + 2];
-      p3 = blockData[blockBufferOffset + row + 3];
-      p4 = blockData[blockBufferOffset + row + 4];
-      p5 = blockData[blockBufferOffset + row + 5];
-      p6 = blockData[blockBufferOffset + row + 6];
-      p7 = blockData[blockBufferOffset + row + 7];
-      p0 *= qt[row];
-
-      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
-        t = dctSqrt2 * p0 + 512 >> 10;
-        p[row] = t;
-        p[row + 1] = t;
-        p[row + 2] = t;
-        p[row + 3] = t;
-        p[row + 4] = t;
-        p[row + 5] = t;
-        p[row + 6] = t;
-        p[row + 7] = t;
-        continue;
+    p1 *= qt[row + 1];
+    p2 *= qt[row + 2];
+    p3 *= qt[row + 3];
+    p4 *= qt[row + 4];
+    p5 *= qt[row + 5];
+    p6 *= qt[row + 6];
+    p7 *= qt[row + 7];
+    v0 = dctSqrt2 * p0 + 128 >> 8;
+    v1 = dctSqrt2 * p4 + 128 >> 8;
+    v2 = p2;
+    v3 = p6;
+    v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;
+    v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;
+    v5 = p3 << 4;
+    v6 = p5 << 4;
+    v0 = v0 + v1 + 1 >> 1;
+    v1 = v0 - v1;
+    t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;
+    v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;
+    v3 = t;
+    v4 = v4 + v6 + 1 >> 1;
+    v6 = v4 - v6;
+    v7 = v7 + v5 + 1 >> 1;
+    v5 = v7 - v5;
+    v0 = v0 + v3 + 1 >> 1;
+    v3 = v0 - v3;
+    v1 = v1 + v2 + 1 >> 1;
+    v2 = v1 - v2;
+    t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
+    v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
+    v7 = t;
+    t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
+    v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
+    v6 = t;
+    p[row] = v0 + v7;
+    p[row + 7] = v0 - v7;
+    p[row + 1] = v1 + v6;
+    p[row + 6] = v1 - v6;
+    p[row + 2] = v2 + v5;
+    p[row + 5] = v2 - v5;
+    p[row + 3] = v3 + v4;
+    p[row + 4] = v3 - v4;
+  }
+
+  for (let col = 0; col < 8; ++col) {
+    p0 = p[col];
+    p1 = p[col + 8];
+    p2 = p[col + 16];
+    p3 = p[col + 24];
+    p4 = p[col + 32];
+    p5 = p[col + 40];
+    p6 = p[col + 48];
+    p7 = p[col + 56];
+
+    if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
+      t = dctSqrt2 * p0 + 8192 >> 14;
+
+      if (t < -2040) {
+        t = 0;
+      } else if (t >= 2024) {
+        t = 255;
+      } else {
+        t = t + 2056 >> 4;
       }
 
-      p1 *= qt[row + 1];
-      p2 *= qt[row + 2];
-      p3 *= qt[row + 3];
-      p4 *= qt[row + 4];
-      p5 *= qt[row + 5];
-      p6 *= qt[row + 6];
-      p7 *= qt[row + 7];
-      v0 = dctSqrt2 * p0 + 128 >> 8;
-      v1 = dctSqrt2 * p4 + 128 >> 8;
-      v2 = p2;
-      v3 = p6;
-      v4 = dctSqrt1d2 * (p1 - p7) + 128 >> 8;
-      v7 = dctSqrt1d2 * (p1 + p7) + 128 >> 8;
-      v5 = p3 << 4;
-      v6 = p5 << 4;
-      v0 = v0 + v1 + 1 >> 1;
-      v1 = v0 - v1;
-      t = v2 * dctSin6 + v3 * dctCos6 + 128 >> 8;
-      v2 = v2 * dctCos6 - v3 * dctSin6 + 128 >> 8;
-      v3 = t;
-      v4 = v4 + v6 + 1 >> 1;
-      v6 = v4 - v6;
-      v7 = v7 + v5 + 1 >> 1;
-      v5 = v7 - v5;
-      v0 = v0 + v3 + 1 >> 1;
-      v3 = v0 - v3;
-      v1 = v1 + v2 + 1 >> 1;
-      v2 = v1 - v2;
-      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
-      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
-      v7 = t;
-      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
-      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
-      v6 = t;
-      p[row] = v0 + v7;
-      p[row + 7] = v0 - v7;
-      p[row + 1] = v1 + v6;
-      p[row + 6] = v1 - v6;
-      p[row + 2] = v2 + v5;
-      p[row + 5] = v2 - v5;
-      p[row + 3] = v3 + v4;
-      p[row + 4] = v3 - v4;
+      blockData[blockBufferOffset + col] = t;
+      blockData[blockBufferOffset + col + 8] = t;
+      blockData[blockBufferOffset + col + 16] = t;
+      blockData[blockBufferOffset + col + 24] = t;
+      blockData[blockBufferOffset + col + 32] = t;
+      blockData[blockBufferOffset + col + 40] = t;
+      blockData[blockBufferOffset + col + 48] = t;
+      blockData[blockBufferOffset + col + 56] = t;
+      continue;
     }
 
-    for (var col = 0; col < 8; ++col) {
-      p0 = p[col];
-      p1 = p[col + 8];
-      p2 = p[col + 16];
-      p3 = p[col + 24];
-      p4 = p[col + 32];
-      p5 = p[col + 40];
-      p6 = p[col + 48];
-      p7 = p[col + 56];
-
-      if ((p1 | p2 | p3 | p4 | p5 | p6 | p7) === 0) {
-        t = dctSqrt2 * p0 + 8192 >> 14;
-
-        if (t < -2040) {
-          t = 0;
-        } else if (t >= 2024) {
-          t = 255;
-        } else {
-          t = t + 2056 >> 4;
-        }
+    v0 = dctSqrt2 * p0 + 2048 >> 12;
+    v1 = dctSqrt2 * p4 + 2048 >> 12;
+    v2 = p2;
+    v3 = p6;
+    v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
+    v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
+    v5 = p3;
+    v6 = p5;
+    v0 = (v0 + v1 + 1 >> 1) + 4112;
+    v1 = v0 - v1;
+    t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
+    v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
+    v3 = t;
+    v4 = v4 + v6 + 1 >> 1;
+    v6 = v4 - v6;
+    v7 = v7 + v5 + 1 >> 1;
+    v5 = v7 - v5;
+    v0 = v0 + v3 + 1 >> 1;
+    v3 = v0 - v3;
+    v1 = v1 + v2 + 1 >> 1;
+    v2 = v1 - v2;
+    t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
+    v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
+    v7 = t;
+    t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
+    v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
+    v6 = t;
+    p0 = v0 + v7;
+    p7 = v0 - v7;
+    p1 = v1 + v6;
+    p6 = v1 - v6;
+    p2 = v2 + v5;
+    p5 = v2 - v5;
+    p3 = v3 + v4;
+    p4 = v3 - v4;
+
+    if (p0 < 16) {
+      p0 = 0;
+    } else if (p0 >= 4080) {
+      p0 = 255;
+    } else {
+      p0 >>= 4;
+    }
 
-        blockData[blockBufferOffset + col] = t;
-        blockData[blockBufferOffset + col + 8] = t;
-        blockData[blockBufferOffset + col + 16] = t;
-        blockData[blockBufferOffset + col + 24] = t;
-        blockData[blockBufferOffset + col + 32] = t;
-        blockData[blockBufferOffset + col + 40] = t;
-        blockData[blockBufferOffset + col + 48] = t;
-        blockData[blockBufferOffset + col + 56] = t;
-        continue;
-      }
+    if (p1 < 16) {
+      p1 = 0;
+    } else if (p1 >= 4080) {
+      p1 = 255;
+    } else {
+      p1 >>= 4;
+    }
 
-      v0 = dctSqrt2 * p0 + 2048 >> 12;
-      v1 = dctSqrt2 * p4 + 2048 >> 12;
-      v2 = p2;
-      v3 = p6;
-      v4 = dctSqrt1d2 * (p1 - p7) + 2048 >> 12;
-      v7 = dctSqrt1d2 * (p1 + p7) + 2048 >> 12;
-      v5 = p3;
-      v6 = p5;
-      v0 = (v0 + v1 + 1 >> 1) + 4112;
-      v1 = v0 - v1;
-      t = v2 * dctSin6 + v3 * dctCos6 + 2048 >> 12;
-      v2 = v2 * dctCos6 - v3 * dctSin6 + 2048 >> 12;
-      v3 = t;
-      v4 = v4 + v6 + 1 >> 1;
-      v6 = v4 - v6;
-      v7 = v7 + v5 + 1 >> 1;
-      v5 = v7 - v5;
-      v0 = v0 + v3 + 1 >> 1;
-      v3 = v0 - v3;
-      v1 = v1 + v2 + 1 >> 1;
-      v2 = v1 - v2;
-      t = v4 * dctSin3 + v7 * dctCos3 + 2048 >> 12;
-      v4 = v4 * dctCos3 - v7 * dctSin3 + 2048 >> 12;
-      v7 = t;
-      t = v5 * dctSin1 + v6 * dctCos1 + 2048 >> 12;
-      v5 = v5 * dctCos1 - v6 * dctSin1 + 2048 >> 12;
-      v6 = t;
-      p0 = v0 + v7;
-      p7 = v0 - v7;
-      p1 = v1 + v6;
-      p6 = v1 - v6;
-      p2 = v2 + v5;
-      p5 = v2 - v5;
-      p3 = v3 + v4;
-      p4 = v3 - v4;
-
-      if (p0 < 16) {
-        p0 = 0;
-      } else if (p0 >= 4080) {
-        p0 = 255;
-      } else {
-        p0 >>= 4;
-      }
+    if (p2 < 16) {
+      p2 = 0;
+    } else if (p2 >= 4080) {
+      p2 = 255;
+    } else {
+      p2 >>= 4;
+    }
 
-      if (p1 < 16) {
-        p1 = 0;
-      } else if (p1 >= 4080) {
-        p1 = 255;
-      } else {
-        p1 >>= 4;
-      }
+    if (p3 < 16) {
+      p3 = 0;
+    } else if (p3 >= 4080) {
+      p3 = 255;
+    } else {
+      p3 >>= 4;
+    }
 
-      if (p2 < 16) {
-        p2 = 0;
-      } else if (p2 >= 4080) {
-        p2 = 255;
-      } else {
-        p2 >>= 4;
-      }
+    if (p4 < 16) {
+      p4 = 0;
+    } else if (p4 >= 4080) {
+      p4 = 255;
+    } else {
+      p4 >>= 4;
+    }
 
-      if (p3 < 16) {
-        p3 = 0;
-      } else if (p3 >= 4080) {
-        p3 = 255;
-      } else {
-        p3 >>= 4;
-      }
+    if (p5 < 16) {
+      p5 = 0;
+    } else if (p5 >= 4080) {
+      p5 = 255;
+    } else {
+      p5 >>= 4;
+    }
 
-      if (p4 < 16) {
-        p4 = 0;
-      } else if (p4 >= 4080) {
-        p4 = 255;
-      } else {
-        p4 >>= 4;
-      }
+    if (p6 < 16) {
+      p6 = 0;
+    } else if (p6 >= 4080) {
+      p6 = 255;
+    } else {
+      p6 >>= 4;
+    }
 
-      if (p5 < 16) {
-        p5 = 0;
-      } else if (p5 >= 4080) {
-        p5 = 255;
-      } else {
-        p5 >>= 4;
-      }
+    if (p7 < 16) {
+      p7 = 0;
+    } else if (p7 >= 4080) {
+      p7 = 255;
+    } else {
+      p7 >>= 4;
+    }
 
-      if (p6 < 16) {
-        p6 = 0;
-      } else if (p6 >= 4080) {
-        p6 = 255;
-      } else {
-        p6 >>= 4;
-      }
+    blockData[blockBufferOffset + col] = p0;
+    blockData[blockBufferOffset + col + 8] = p1;
+    blockData[blockBufferOffset + col + 16] = p2;
+    blockData[blockBufferOffset + col + 24] = p3;
+    blockData[blockBufferOffset + col + 32] = p4;
+    blockData[blockBufferOffset + col + 40] = p5;
+    blockData[blockBufferOffset + col + 48] = p6;
+    blockData[blockBufferOffset + col + 56] = p7;
+  }
+}
 
-      if (p7 < 16) {
-        p7 = 0;
-      } else if (p7 >= 4080) {
-        p7 = 255;
-      } else {
-        p7 >>= 4;
-      }
+function buildComponentData(frame, component) {
+  const blocksPerLine = component.blocksPerLine;
+  const blocksPerColumn = component.blocksPerColumn;
+  const computationBuffer = new Int16Array(64);
 
-      blockData[blockBufferOffset + col] = p0;
-      blockData[blockBufferOffset + col + 8] = p1;
-      blockData[blockBufferOffset + col + 16] = p2;
-      blockData[blockBufferOffset + col + 24] = p3;
-      blockData[blockBufferOffset + col + 32] = p4;
-      blockData[blockBufferOffset + col + 40] = p5;
-      blockData[blockBufferOffset + col + 48] = p6;
-      blockData[blockBufferOffset + col + 56] = p7;
+  for (let blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
+    for (let blockCol = 0; blockCol < blocksPerLine; blockCol++) {
+      const offset = getBlockBufferOffset(component, blockRow, blockCol);
+      quantizeAndInverse(component, offset, computationBuffer);
     }
   }
 
-  function buildComponentData(frame, component) {
-    var blocksPerLine = component.blocksPerLine;
-    var blocksPerColumn = component.blocksPerColumn;
-    var computationBuffer = new Int16Array(64);
+  return component.blockData;
+}
 
-    for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) {
-      for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) {
-        var offset = getBlockBufferOffset(component, blockRow, blockCol);
-        quantizeAndInverse(component, offset, computationBuffer);
-      }
-    }
+function findNextFileMarker(data, currentPos, startPos = currentPos) {
+  const maxPos = data.length - 1;
+  let newPos = startPos < currentPos ? startPos : currentPos;
 
-    return component.blockData;
+  if (currentPos >= maxPos) {
+    return null;
   }
 
-  function findNextFileMarker(data, currentPos, startPos = currentPos) {
-    const maxPos = data.length - 1;
-    var newPos = startPos < currentPos ? startPos : currentPos;
+  const currentMarker = (0, _core_utils.readUint16)(data, currentPos);
 
-    if (currentPos >= maxPos) {
-      return null;
-    }
+  if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) {
+    return {
+      invalid: null,
+      marker: currentMarker,
+      offset: currentPos
+    };
+  }
 
-    var currentMarker = (0, _core_utils.readUint16)(data, currentPos);
+  let newMarker = (0, _core_utils.readUint16)(data, newPos);
 
-    if (currentMarker >= 0xffc0 && currentMarker <= 0xfffe) {
-      return {
-        invalid: null,
-        marker: currentMarker,
-        offset: currentPos
-      };
+  while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) {
+    if (++newPos >= maxPos) {
+      return null;
     }
 
-    var newMarker = (0, _core_utils.readUint16)(data, newPos);
-
-    while (!(newMarker >= 0xffc0 && newMarker <= 0xfffe)) {
-      if (++newPos >= maxPos) {
-        return null;
-      }
+    newMarker = (0, _core_utils.readUint16)(data, newPos);
+  }
 
-      newMarker = (0, _core_utils.readUint16)(data, newPos);
-    }
+  return {
+    invalid: currentMarker.toString(16),
+    marker: newMarker,
+    offset: newPos
+  };
+}
 
-    return {
-      invalid: currentMarker.toString(16),
-      marker: newMarker,
-      offset: newPos
-    };
+class JpegImage {
+  constructor({
+    decodeTransform = null,
+    colorTransform = -1
+  } = {}) {
+    this._decodeTransform = decodeTransform;
+    this._colorTransform = colorTransform;
   }
 
-  JpegImage.prototype = {
-    parse(data, {
-      dnlScanLines = null
-    } = {}) {
-      function readDataBlock() {
-        const length = (0, _core_utils.readUint16)(data, offset);
-        offset += 2;
-        let endOffset = offset + length - 2;
-        var fileMarker = findNextFileMarker(data, endOffset, offset);
-
-        if (fileMarker && fileMarker.invalid) {
-          (0, _util.warn)("readDataBlock - incorrect length, current marker is: " + fileMarker.invalid);
-          endOffset = fileMarker.offset;
-        }
+  parse(data, {
+    dnlScanLines = null
+  } = {}) {
+    function readDataBlock() {
+      const length = (0, _core_utils.readUint16)(data, offset);
+      offset += 2;
+      let endOffset = offset + length - 2;
+      const fileMarker = findNextFileMarker(data, endOffset, offset);
 
-        var array = data.subarray(offset, endOffset);
-        offset += array.length;
-        return array;
+      if (fileMarker && fileMarker.invalid) {
+        (0, _util.warn)("readDataBlock - incorrect length, current marker is: " + fileMarker.invalid);
+        endOffset = fileMarker.offset;
       }
 
-      function prepareComponents(frame) {
-        var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
-        var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
-
-        for (var i = 0; i < frame.components.length; i++) {
-          component = frame.components[i];
-          var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
-          var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
-          var blocksPerLineForMcu = mcusPerLine * component.h;
-          var blocksPerColumnForMcu = mcusPerColumn * component.v;
-          var blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
-          component.blockData = new Int16Array(blocksBufferSize);
-          component.blocksPerLine = blocksPerLine;
-          component.blocksPerColumn = blocksPerColumn;
-        }
+      const array = data.subarray(offset, endOffset);
+      offset += array.length;
+      return array;
+    }
 
-        frame.mcusPerLine = mcusPerLine;
-        frame.mcusPerColumn = mcusPerColumn;
+    function prepareComponents(frame) {
+      const mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH);
+      const mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV);
+
+      for (let i = 0, ii = frame.components.length; i < ii; i++) {
+        const component = frame.components[i];
+        const blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH);
+        const blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV);
+        const blocksPerLineForMcu = mcusPerLine * component.h;
+        const blocksPerColumnForMcu = mcusPerColumn * component.v;
+        const blocksBufferSize = 64 * blocksPerColumnForMcu * (blocksPerLineForMcu + 1);
+        component.blockData = new Int16Array(blocksBufferSize);
+        component.blocksPerLine = blocksPerLine;
+        component.blocksPerColumn = blocksPerColumn;
       }
 
-      var offset = 0;
-      var jfif = null;
-      var adobe = null;
-      var frame, resetInterval;
-      let numSOSMarkers = 0;
-      var quantizationTables = [];
-      var huffmanTablesAC = [],
-          huffmanTablesDC = [];
-      let fileMarker = (0, _core_utils.readUint16)(data, offset);
-      offset += 2;
+      frame.mcusPerLine = mcusPerLine;
+      frame.mcusPerColumn = mcusPerColumn;
+    }
 
-      if (fileMarker !== 0xffd8) {
-        throw new JpegError("SOI not found");
-      }
+    let offset = 0;
+    let jfif = null;
+    let adobe = null;
+    let frame, resetInterval;
+    let numSOSMarkers = 0;
+    const quantizationTables = [];
+    const huffmanTablesAC = [],
+          huffmanTablesDC = [];
+    let fileMarker = (0, _core_utils.readUint16)(data, offset);
+    offset += 2;
 
-      fileMarker = (0, _core_utils.readUint16)(data, offset);
-      offset += 2;
+    if (fileMarker !== 0xffd8) {
+      throw new JpegError("SOI not found");
+    }
 
-      markerLoop: while (fileMarker !== 0xffd9) {
-        var i, j, l;
-
-        switch (fileMarker) {
-          case 0xffe0:
-          case 0xffe1:
-          case 0xffe2:
-          case 0xffe3:
-          case 0xffe4:
-          case 0xffe5:
-          case 0xffe6:
-          case 0xffe7:
-          case 0xffe8:
-          case 0xffe9:
-          case 0xffea:
-          case 0xffeb:
-          case 0xffec:
-          case 0xffed:
-          case 0xffee:
-          case 0xffef:
-          case 0xfffe:
-            var appData = readDataBlock();
-
-            if (fileMarker === 0xffe0) {
-              if (appData[0] === 0x4a && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
-                jfif = {
-                  version: {
-                    major: appData[5],
-                    minor: appData[6]
-                  },
-                  densityUnits: appData[7],
-                  xDensity: appData[8] << 8 | appData[9],
-                  yDensity: appData[10] << 8 | appData[11],
-                  thumbWidth: appData[12],
-                  thumbHeight: appData[13],
-                  thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
-                };
-              }
+    fileMarker = (0, _core_utils.readUint16)(data, offset);
+    offset += 2;
+
+    markerLoop: while (fileMarker !== 0xffd9) {
+      let i, j, l;
+
+      switch (fileMarker) {
+        case 0xffe0:
+        case 0xffe1:
+        case 0xffe2:
+        case 0xffe3:
+        case 0xffe4:
+        case 0xffe5:
+        case 0xffe6:
+        case 0xffe7:
+        case 0xffe8:
+        case 0xffe9:
+        case 0xffea:
+        case 0xffeb:
+        case 0xffec:
+        case 0xffed:
+        case 0xffee:
+        case 0xffef:
+        case 0xfffe:
+          const appData = readDataBlock();
+
+          if (fileMarker === 0xffe0) {
+            if (appData[0] === 0x4a && appData[1] === 0x46 && appData[2] === 0x49 && appData[3] === 0x46 && appData[4] === 0) {
+              jfif = {
+                version: {
+                  major: appData[5],
+                  minor: appData[6]
+                },
+                densityUnits: appData[7],
+                xDensity: appData[8] << 8 | appData[9],
+                yDensity: appData[10] << 8 | appData[11],
+                thumbWidth: appData[12],
+                thumbHeight: appData[13],
+                thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13])
+              };
             }
+          }
 
-            if (fileMarker === 0xffee) {
-              if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6f && appData[3] === 0x62 && appData[4] === 0x65) {
-                adobe = {
-                  version: appData[5] << 8 | appData[6],
-                  flags0: appData[7] << 8 | appData[8],
-                  flags1: appData[9] << 8 | appData[10],
-                  transformCode: appData[11]
-                };
-              }
+          if (fileMarker === 0xffee) {
+            if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6f && appData[3] === 0x62 && appData[4] === 0x65) {
+              adobe = {
+                version: appData[5] << 8 | appData[6],
+                flags0: appData[7] << 8 | appData[8],
+                flags1: appData[9] << 8 | appData[10],
+                transformCode: appData[11]
+              };
             }
+          }
 
-            break;
+          break;
 
-          case 0xffdb:
-            const quantizationTablesLength = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
-            var quantizationTablesEnd = quantizationTablesLength + offset - 2;
-            var z;
-
-            while (offset < quantizationTablesEnd) {
-              var quantizationTableSpec = data[offset++];
-              var tableData = new Uint16Array(64);
-
-              if (quantizationTableSpec >> 4 === 0) {
-                for (j = 0; j < 64; j++) {
-                  z = dctZigZag[j];
-                  tableData[z] = data[offset++];
-                }
-              } else if (quantizationTableSpec >> 4 === 1) {
-                for (j = 0; j < 64; j++) {
-                  z = dctZigZag[j];
-                  tableData[z] = (0, _core_utils.readUint16)(data, offset);
-                  offset += 2;
-                }
-              } else {
-                throw new JpegError("DQT - invalid table spec");
-              }
+        case 0xffdb:
+          const quantizationTablesLength = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
+          const quantizationTablesEnd = quantizationTablesLength + offset - 2;
+          let z;
+
+          while (offset < quantizationTablesEnd) {
+            const quantizationTableSpec = data[offset++];
+            const tableData = new Uint16Array(64);
 
-              quantizationTables[quantizationTableSpec & 15] = tableData;
+            if (quantizationTableSpec >> 4 === 0) {
+              for (j = 0; j < 64; j++) {
+                z = dctZigZag[j];
+                tableData[z] = data[offset++];
+              }
+            } else if (quantizationTableSpec >> 4 === 1) {
+              for (j = 0; j < 64; j++) {
+                z = dctZigZag[j];
+                tableData[z] = (0, _core_utils.readUint16)(data, offset);
+                offset += 2;
+              }
+            } else {
+              throw new JpegError("DQT - invalid table spec");
             }
 
-            break;
+            quantizationTables[quantizationTableSpec & 15] = tableData;
+          }
 
-          case 0xffc0:
-          case 0xffc1:
-          case 0xffc2:
-            if (frame) {
-              throw new JpegError("Only single frame JPEGs supported");
-            }
+          break;
 
-            offset += 2;
-            frame = {};
-            frame.extended = fileMarker === 0xffc1;
-            frame.progressive = fileMarker === 0xffc2;
-            frame.precision = data[offset++];
-            const sofScanLines = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
-            frame.scanLines = dnlScanLines || sofScanLines;
-            frame.samplesPerLine = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
-            frame.components = [];
-            frame.componentIds = {};
-            var componentsCount = data[offset++],
-                componentId;
-            var maxH = 0,
-                maxV = 0;
-
-            for (i = 0; i < componentsCount; i++) {
-              componentId = data[offset];
-              var h = data[offset + 1] >> 4;
-              var v = data[offset + 1] & 15;
-
-              if (maxH < h) {
-                maxH = h;
-              }
+        case 0xffc0:
+        case 0xffc1:
+        case 0xffc2:
+          if (frame) {
+            throw new JpegError("Only single frame JPEGs supported");
+          }
 
-              if (maxV < v) {
-                maxV = v;
-              }
+          offset += 2;
+          frame = {};
+          frame.extended = fileMarker === 0xffc1;
+          frame.progressive = fileMarker === 0xffc2;
+          frame.precision = data[offset++];
+          const sofScanLines = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
+          frame.scanLines = dnlScanLines || sofScanLines;
+          frame.samplesPerLine = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
+          frame.components = [];
+          frame.componentIds = {};
+          const componentsCount = data[offset++];
+          let maxH = 0,
+              maxV = 0;
+
+          for (i = 0; i < componentsCount; i++) {
+            const componentId = data[offset];
+            const h = data[offset + 1] >> 4;
+            const v = data[offset + 1] & 15;
+
+            if (maxH < h) {
+              maxH = h;
+            }
 
-              var qId = data[offset + 2];
-              l = frame.components.push({
-                h,
-                v,
-                quantizationId: qId,
-                quantizationTable: null
-              });
-              frame.componentIds[componentId] = l - 1;
-              offset += 3;
+            if (maxV < v) {
+              maxV = v;
             }
 
-            frame.maxH = maxH;
-            frame.maxV = maxV;
-            prepareComponents(frame);
-            break;
+            const qId = data[offset + 2];
+            l = frame.components.push({
+              h,
+              v,
+              quantizationId: qId,
+              quantizationTable: null
+            });
+            frame.componentIds[componentId] = l - 1;
+            offset += 3;
+          }
 
-          case 0xffc4:
-            const huffmanLength = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
+          frame.maxH = maxH;
+          frame.maxV = maxV;
+          prepareComponents(frame);
+          break;
 
-            for (i = 2; i < huffmanLength;) {
-              var huffmanTableSpec = data[offset++];
-              var codeLengths = new Uint8Array(16);
-              var codeLengthSum = 0;
+        case 0xffc4:
+          const huffmanLength = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
 
-              for (j = 0; j < 16; j++, offset++) {
-                codeLengthSum += codeLengths[j] = data[offset];
-              }
+          for (i = 2; i < huffmanLength;) {
+            const huffmanTableSpec = data[offset++];
+            const codeLengths = new Uint8Array(16);
+            let codeLengthSum = 0;
 
-              var huffmanValues = new Uint8Array(codeLengthSum);
+            for (j = 0; j < 16; j++, offset++) {
+              codeLengthSum += codeLengths[j] = data[offset];
+            }
 
-              for (j = 0; j < codeLengthSum; j++, offset++) {
-                huffmanValues[j] = data[offset];
-              }
+            const huffmanValues = new Uint8Array(codeLengthSum);
 
-              i += 17 + codeLengthSum;
-              (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
+            for (j = 0; j < codeLengthSum; j++, offset++) {
+              huffmanValues[j] = data[offset];
             }
 
-            break;
+            i += 17 + codeLengthSum;
+            (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
+          }
 
-          case 0xffdd:
-            offset += 2;
-            resetInterval = (0, _core_utils.readUint16)(data, offset);
-            offset += 2;
-            break;
+          break;
+
+        case 0xffdd:
+          offset += 2;
+          resetInterval = (0, _core_utils.readUint16)(data, offset);
+          offset += 2;
+          break;
+
+        case 0xffda:
+          const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;
+          offset += 2;
+          const selectorsCount = data[offset++],
+                components = [];
+
+          for (i = 0; i < selectorsCount; i++) {
+            const index = data[offset++];
+            const componentIndex = frame.componentIds[index];
+            const component = frame.components[componentIndex];
+            component.index = index;
+            const tableSpec = data[offset++];
+            component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
+            component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
+            components.push(component);
+          }
 
-          case 0xffda:
-            const parseDNLMarker = ++numSOSMarkers === 1 && !dnlScanLines;
-            offset += 2;
-            var selectorsCount = data[offset++];
-            var components = [],
-                component;
-
-            for (i = 0; i < selectorsCount; i++) {
-              const index = data[offset++];
-              var componentIndex = frame.componentIds[index];
-              component = frame.components[componentIndex];
-              component.index = index;
-              var tableSpec = data[offset++];
-              component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
-              component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
-              components.push(component);
+          const spectralStart = data[offset++],
+                spectralEnd = data[offset++],
+                successiveApproximation = data[offset++];
+
+          try {
+            const processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);
+            offset += processed;
+          } catch (ex) {
+            if (ex instanceof DNLMarkerError) {
+              (0, _util.warn)(`${ex.message} -- attempting to re-parse the JPEG image.`);
+              return this.parse(data, {
+                dnlScanLines: ex.scanLines
+              });
+            } else if (ex instanceof EOIMarkerError) {
+              (0, _util.warn)(`${ex.message} -- ignoring the rest of the image data.`);
+              break markerLoop;
             }
 
-            var spectralStart = data[offset++];
-            var spectralEnd = data[offset++];
-            var successiveApproximation = data[offset++];
-
-            try {
-              var processed = decodeScan(data, offset, frame, components, resetInterval, spectralStart, spectralEnd, successiveApproximation >> 4, successiveApproximation & 15, parseDNLMarker);
-              offset += processed;
-            } catch (ex) {
-              if (ex instanceof DNLMarkerError) {
-                (0, _util.warn)(`${ex.message} -- attempting to re-parse the JPEG image.`);
-                return this.parse(data, {
-                  dnlScanLines: ex.scanLines
-                });
-              } else if (ex instanceof EOIMarkerError) {
-                (0, _util.warn)(`${ex.message} -- ignoring the rest of the image data.`);
-                break markerLoop;
-              }
+            throw ex;
+          }
 
-              throw ex;
-            }
+          break;
 
-            break;
+        case 0xffdc:
+          offset += 4;
+          break;
 
-          case 0xffdc:
-            offset += 4;
-            break;
+        case 0xffff:
+          if (data[offset] !== 0xff) {
+            offset--;
+          }
 
-          case 0xffff:
-            if (data[offset] !== 0xff) {
-              offset--;
-            }
+          break;
+
+        default:
+          const nextFileMarker = findNextFileMarker(data, offset - 2, offset - 3);
 
+          if (nextFileMarker && nextFileMarker.invalid) {
+            (0, _util.warn)("JpegImage.parse - unexpected data, current marker is: " + nextFileMarker.invalid);
+            offset = nextFileMarker.offset;
             break;
+          }
 
-          default:
-            const nextFileMarker = findNextFileMarker(data, offset - 2, offset - 3);
+          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;
+          }
 
-            if (nextFileMarker && nextFileMarker.invalid) {
-              (0, _util.warn)("JpegImage.parse - unexpected data, current marker is: " + nextFileMarker.invalid);
-              offset = nextFileMarker.offset;
-              break;
-            }
+          throw new JpegError("JpegImage.parse - unknown marker: " + fileMarker.toString(16));
+      }
 
-            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;
-            }
+      fileMarker = (0, _core_utils.readUint16)(data, offset);
+      offset += 2;
+    }
 
-            throw new JpegError("JpegImage.parse - unknown marker: " + fileMarker.toString(16));
-        }
+    this.width = frame.samplesPerLine;
+    this.height = frame.scanLines;
+    this.jfif = jfif;
+    this.adobe = adobe;
+    this.components = [];
+
+    for (let i = 0, ii = frame.components.length; i < ii; i++) {
+      const component = frame.components[i];
+      const quantizationTable = quantizationTables[component.quantizationId];
 
-        fileMarker = (0, _core_utils.readUint16)(data, offset);
-        offset += 2;
+      if (quantizationTable) {
+        component.quantizationTable = quantizationTable;
       }
 
-      this.width = frame.samplesPerLine;
-      this.height = frame.scanLines;
-      this.jfif = jfif;
-      this.adobe = adobe;
-      this.components = [];
+      this.components.push({
+        index: component.index,
+        output: buildComponentData(frame, component),
+        scaleX: component.h / frame.maxH,
+        scaleY: component.v / frame.maxV,
+        blocksPerLine: component.blocksPerLine,
+        blocksPerColumn: component.blocksPerColumn
+      });
+    }
 
-      for (i = 0; i < frame.components.length; i++) {
-        component = frame.components[i];
-        var quantizationTable = quantizationTables[component.quantizationId];
+    this.numComponents = this.components.length;
+    return undefined;
+  }
 
-        if (quantizationTable) {
-          component.quantizationTable = quantizationTable;
+  _getLinearizedBlockData(width, height, isSourcePDF = false) {
+    const scaleX = this.width / width,
+          scaleY = this.height / height;
+    let component, componentScaleX, componentScaleY, blocksPerScanline;
+    let x, y, i, j, k;
+    let index;
+    let offset = 0;
+    let output;
+    const numComponents = this.components.length;
+    const dataLength = width * height * numComponents;
+    const data = new Uint8ClampedArray(dataLength);
+    const xScaleBlockOffset = new Uint32Array(width);
+    const mask3LSB = 0xfffffff8;
+    let lastComponentScaleX;
+
+    for (i = 0; i < numComponents; i++) {
+      component = this.components[i];
+      componentScaleX = component.scaleX * scaleX;
+      componentScaleY = component.scaleY * scaleY;
+      offset = i;
+      output = component.output;
+      blocksPerScanline = component.blocksPerLine + 1 << 3;
+
+      if (componentScaleX !== lastComponentScaleX) {
+        for (x = 0; x < width; x++) {
+          j = 0 | x * componentScaleX;
+          xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
         }
 
-        this.components.push({
-          index: component.index,
-          output: buildComponentData(frame, component),
-          scaleX: component.h / frame.maxH,
-          scaleY: component.v / frame.maxV,
-          blocksPerLine: component.blocksPerLine,
-          blocksPerColumn: component.blocksPerColumn
-        });
+        lastComponentScaleX = componentScaleX;
       }
 
-      this.numComponents = this.components.length;
-      return undefined;
-    },
+      for (y = 0; y < height; y++) {
+        j = 0 | y * componentScaleY;
+        index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
 
-    _getLinearizedBlockData(width, height, isSourcePDF = false) {
-      var scaleX = this.width / width,
-          scaleY = this.height / height;
-      var component, componentScaleX, componentScaleY, blocksPerScanline;
-      var x, y, i, j, k;
-      var index;
-      var offset = 0;
-      var output;
-      var numComponents = this.components.length;
-      var dataLength = width * height * numComponents;
-      var data = new Uint8ClampedArray(dataLength);
-      var xScaleBlockOffset = new Uint32Array(width);
-      var mask3LSB = 0xfffffff8;
-      let lastComponentScaleX;
-
-      for (i = 0; i < numComponents; i++) {
-        component = this.components[i];
-        componentScaleX = component.scaleX * scaleX;
-        componentScaleY = component.scaleY * scaleY;
-        offset = i;
-        output = component.output;
-        blocksPerScanline = component.blocksPerLine + 1 << 3;
-
-        if (componentScaleX !== lastComponentScaleX) {
-          for (x = 0; x < width; x++) {
-            j = 0 | x * componentScaleX;
-            xScaleBlockOffset[x] = (j & mask3LSB) << 3 | j & 7;
-          }
-
-          lastComponentScaleX = componentScaleX;
+        for (x = 0; x < width; x++) {
+          data[offset] = output[index + xScaleBlockOffset[x]];
+          offset += numComponents;
         }
+      }
+    }
 
-        for (y = 0; y < height; y++) {
-          j = 0 | y * componentScaleY;
-          index = blocksPerScanline * (j & mask3LSB) | (j & 7) << 3;
+    let transform = this._decodeTransform;
 
-          for (x = 0; x < width; x++) {
-            data[offset] = output[index + xScaleBlockOffset[x]];
-            offset += numComponents;
-          }
+    if (!isSourcePDF && numComponents === 4 && !transform) {
+      transform = new Int32Array([-256, 255, -256, 255, -256, 255, -256, 255]);
+    }
+
+    if (transform) {
+      for (i = 0; i < dataLength;) {
+        for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
+          data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
         }
       }
+    }
 
-      let transform = this._decodeTransform;
+    return data;
+  }
 
-      if (!isSourcePDF && numComponents === 4 && !transform) {
-        transform = new Int32Array([-256, 255, -256, 255, -256, 255, -256, 255]);
-      }
+  get _isColorConversionNeeded() {
+    if (this.adobe) {
+      return !!this.adobe.transformCode;
+    }
 
-      if (transform) {
-        for (i = 0; i < dataLength;) {
-          for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
-            data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
-          }
-        }
+    if (this.numComponents === 3) {
+      if (this._colorTransform === 0) {
+        return false;
+      } else if (this.components[0].index === 0x52 && this.components[1].index === 0x47 && this.components[2].index === 0x42) {
+        return false;
       }
 
-      return data;
-    },
+      return true;
+    }
 
-    get _isColorConversionNeeded() {
-      if (this.adobe) {
-        return !!this.adobe.transformCode;
-      }
+    if (this._colorTransform === 1) {
+      return true;
+    }
 
-      if (this.numComponents === 3) {
-        if (this._colorTransform === 0) {
-          return false;
-        } else if (this.components[0].index === 0x52 && this.components[1].index === 0x47 && this.components[2].index === 0x42) {
-          return false;
-        }
+    return false;
+  }
 
-        return true;
-      }
+  _convertYccToRgb(data) {
+    let Y, Cb, Cr;
 
-      if (this._colorTransform === 1) {
-        return true;
-      }
+    for (let i = 0, length = data.length; i < length; i += 3) {
+      Y = data[i];
+      Cb = data[i + 1];
+      Cr = data[i + 2];
+      data[i] = Y - 179.456 + 1.402 * Cr;
+      data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
+      data[i + 2] = Y - 226.816 + 1.772 * Cb;
+    }
 
-      return false;
-    },
+    return data;
+  }
 
-    _convertYccToRgb: function convertYccToRgb(data) {
-      var Y, Cb, Cr;
+  _convertYcckToRgb(data) {
+    let Y, Cb, Cr, k;
+    let offset = 0;
+
+    for (let i = 0, length = data.length; i < length; i += 4) {
+      Y = data[i];
+      Cb = data[i + 1];
+      Cr = data[i + 2];
+      k = data[i + 3];
+      data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);
+      data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);
+      data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);
+    }
 
-      for (var i = 0, length = data.length; i < length; i += 3) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        data[i] = Y - 179.456 + 1.402 * Cr;
-        data[i + 1] = Y + 135.459 - 0.344 * Cb - 0.714 * Cr;
-        data[i + 2] = Y - 226.816 + 1.772 * Cb;
-      }
+    return data.subarray(0, offset);
+  }
 
-      return data;
-    },
-    _convertYcckToRgb: function convertYcckToRgb(data) {
-      var Y, Cb, Cr, k;
-      var offset = 0;
-
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        k = data[i + 3];
-        data[offset++] = -122.67195406894 + Cb * (-6.60635669420364e-5 * Cb + 0.000437130475926232 * Cr - 5.4080610064599e-5 * Y + 0.00048449797120281 * k - 0.154362151871126) + Cr * (-0.000957964378445773 * Cr + 0.000817076911346625 * Y - 0.00477271405408747 * k + 1.53380253221734) + Y * (0.000961250184130688 * Y - 0.00266257332283933 * k + 0.48357088451265) + k * (-0.000336197177618394 * k + 0.484791561490776);
-        data[offset++] = 107.268039397724 + Cb * (2.19927104525741e-5 * Cb - 0.000640992018297945 * Cr + 0.000659397001245577 * Y + 0.000426105652938837 * k - 0.176491792462875) + Cr * (-0.000778269941513683 * Cr + 0.00130872261408275 * Y + 0.000770482631801132 * k - 0.151051492775562) + Y * (0.00126935368114843 * Y - 0.00265090189010898 * k + 0.25802910206845) + k * (-0.000318913117588328 * k - 0.213742400323665);
-        data[offset++] = -20.810012546947 + Cb * (-0.000570115196973677 * Cb - 2.63409051004589e-5 * Cr + 0.0020741088115012 * Y - 0.00288260236853442 * k + 0.814272968359295) + Cr * (-1.53496057440975e-5 * Cr - 0.000132689043961446 * Y + 0.000560833691242812 * k - 0.195152027534049) + Y * (0.00174418132927582 * Y - 0.00255243321439347 * k + 0.116935020465145) + k * (-0.000343531996510555 * k + 0.24165260232407);
-      }
+  _convertYcckToCmyk(data) {
+    let Y, Cb, Cr;
 
-      return data.subarray(0, offset);
-    },
-    _convertYcckToCmyk: function convertYcckToCmyk(data) {
-      var Y, Cb, Cr;
-
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        Y = data[i];
-        Cb = data[i + 1];
-        Cr = data[i + 2];
-        data[i] = 434.456 - Y - 1.402 * Cr;
-        data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;
-        data[i + 2] = 481.816 - Y - 1.772 * Cb;
-      }
+    for (let i = 0, length = data.length; i < length; i += 4) {
+      Y = data[i];
+      Cb = data[i + 1];
+      Cr = data[i + 2];
+      data[i] = 434.456 - Y - 1.402 * Cr;
+      data[i + 1] = 119.541 - Y + 0.344 * Cb + 0.714 * Cr;
+      data[i + 2] = 481.816 - Y - 1.772 * Cb;
+    }
 
-      return data;
-    },
-    _convertCmykToRgb: function convertCmykToRgb(data) {
-      var c, m, y, k;
-      var offset = 0;
-
-      for (var i = 0, length = data.length; i < length; i += 4) {
-        c = data[i];
-        m = data[i + 1];
-        y = data[i + 2];
-        k = data[i + 3];
-        data[offset++] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254);
-        data[offset++] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.00031891311758832814 * k + 0.7364883807733168);
-        data[offset++] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144);
-      }
+    return data;
+  }
 
-      return data.subarray(0, offset);
-    },
-
-    getData({
-      width,
-      height,
-      forceRGB = false,
-      isSourcePDF = false
-    }) {
-      if (this.numComponents > 4) {
-        throw new JpegError("Unsupported color mode");
-      }
+  _convertCmykToRgb(data) {
+    let c, m, y, k;
+    let offset = 0;
+
+    for (let i = 0, length = data.length; i < length; i += 4) {
+      c = data[i];
+      m = data[i + 1];
+      y = data[i + 2];
+      k = data[i + 3];
+      data[offset++] = 255 + c * (-0.00006747147073602441 * c + 0.0008379262121013727 * m + 0.0002894718188643294 * y + 0.003264231057537806 * k - 1.1185611867203937) + m * (0.000026374107616089405 * m - 0.00008626949158638572 * y - 0.0002748769067499491 * k - 0.02155688794978967) + y * (-0.00003878099212869363 * y - 0.0003267808279485286 * k + 0.0686742238595345) - k * (0.0003361971776183937 * k + 0.7430659151342254);
+      data[offset++] = 255 + c * (0.00013596372813588848 * c + 0.000924537132573585 * m + 0.00010567359618683593 * y + 0.0004791864687436512 * k - 0.3109689587515875) + m * (-0.00023545346108370344 * m + 0.0002702845253534714 * y + 0.0020200308977307156 * k - 0.7488052167015494) + y * (0.00006834815998235662 * y + 0.00015168452363460973 * k - 0.09751927774728933) - k * (0.00031891311758832814 * k + 0.7364883807733168);
+      data[offset++] = 255 + c * (0.000013598650411385307 * c + 0.00012423956175490851 * m + 0.0004751985097583589 * y - 0.0000036729317476630422 * k - 0.05562186980264034) + m * (0.00016141380598724676 * m + 0.0009692239130725186 * y + 0.0007782692450036253 * k - 0.44015232367526463) + y * (5.068882914068769e-7 * y + 0.0017778369011375071 * k - 0.7591454649749609) - k * (0.0003435319965105553 * k + 0.7063770186160144);
+    }
 
-      var data = this._getLinearizedBlockData(width, height, isSourcePDF);
+    return data.subarray(0, offset);
+  }
 
-      if (this.numComponents === 1 && forceRGB) {
-        var dataLength = data.length;
-        var rgbData = new Uint8ClampedArray(dataLength * 3);
-        var offset = 0;
+  getData({
+    width,
+    height,
+    forceRGB = false,
+    isSourcePDF = false
+  }) {
+    if (this.numComponents > 4) {
+      throw new JpegError("Unsupported color mode");
+    }
 
-        for (var i = 0; i < dataLength; i++) {
-          var grayColor = data[i];
-          rgbData[offset++] = grayColor;
-          rgbData[offset++] = grayColor;
-          rgbData[offset++] = grayColor;
-        }
+    const data = this._getLinearizedBlockData(width, height, isSourcePDF);
 
-        return rgbData;
-      } else if (this.numComponents === 3 && this._isColorConversionNeeded) {
-        return this._convertYccToRgb(data);
-      } else if (this.numComponents === 4) {
-        if (this._isColorConversionNeeded) {
-          if (forceRGB) {
-            return this._convertYcckToRgb(data);
-          }
+    if (this.numComponents === 1 && forceRGB) {
+      const dataLength = data.length;
+      const rgbData = new Uint8ClampedArray(dataLength * 3);
+      let offset = 0;
 
-          return this._convertYcckToCmyk(data);
-        } else if (forceRGB) {
-          return this._convertCmykToRgb(data);
-        }
+      for (let i = 0; i < dataLength; i++) {
+        const grayColor = data[i];
+        rgbData[offset++] = grayColor;
+        rgbData[offset++] = grayColor;
+        rgbData[offset++] = grayColor;
       }
 
-      return data;
+      return rgbData;
+    } else if (this.numComponents === 3 && this._isColorConversionNeeded) {
+      return this._convertYccToRgb(data);
+    } else if (this.numComponents === 4) {
+      if (this._isColorConversionNeeded) {
+        if (forceRGB) {
+          return this._convertYcckToRgb(data);
+        }
+
+        return this._convertYcckToCmyk(data);
+      } else if (forceRGB) {
+        return this._convertCmykToRgb(data);
+      }
     }
 
-  };
-  return JpegImage;
-}();
+    return data;
+  }
+
+}
 
 exports.JpegImage = JpegImage;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 692 - 639
lib/core/jpx.js


+ 12 - 18
lib/core/jpx_stream.js

@@ -26,33 +26,28 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.JpxStream = void 0;
 
-var _stream = require("./stream.js");
+var _decode_stream = require("./decode_stream.js");
 
 var _jpx = require("./jpx.js");
 
 var _util = require("../shared/util.js");
 
-const JpxStream = function JpxStreamClosure() {
-  function JpxStream(stream, maybeLength, dict, params) {
+class JpxStream extends _decode_stream.DecodeStream {
+  constructor(stream, maybeLength, params) {
+    super(maybeLength);
     this.stream = stream;
+    this.dict = stream.dict;
     this.maybeLength = maybeLength;
-    this.dict = dict;
     this.params = params;
-
-    _stream.DecodeStream.call(this, maybeLength);
   }
 
-  JpxStream.prototype = Object.create(_stream.DecodeStream.prototype);
-  Object.defineProperty(JpxStream.prototype, "bytes", {
-    get: function JpxStream_bytes() {
-      return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
-    },
-    configurable: true
-  });
+  get bytes() {
+    return (0, _util.shadow)(this, "bytes", this.stream.getBytes(this.maybeLength));
+  }
 
-  JpxStream.prototype.ensureBuffer = function (requested) {};
+  ensureBuffer(requested) {}
 
-  JpxStream.prototype.readBlock = function () {
+  readBlock() {
     if (this.eof) {
       return;
     }
@@ -94,9 +89,8 @@ const JpxStream = function JpxStreamClosure() {
 
     this.bufferLength = this.buffer.length;
     this.eof = true;
-  };
+  }
 
-  return JpxStream;
-}();
+}
 
 exports.JpxStream = JpxStream;

+ 166 - 0
lib/core/lzw_stream.js

@@ -0,0 +1,166 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.LZWStream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+class LZWStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength, earlyChange) {
+    super(maybeLength);
+    this.str = str;
+    this.dict = str.dict;
+    this.cachedData = 0;
+    this.bitsCached = 0;
+    const maxLzwDictionarySize = 4096;
+    const lzwState = {
+      earlyChange,
+      codeLength: 9,
+      nextCode: 258,
+      dictionaryValues: new Uint8Array(maxLzwDictionarySize),
+      dictionaryLengths: new Uint16Array(maxLzwDictionarySize),
+      dictionaryPrevCodes: new Uint16Array(maxLzwDictionarySize),
+      currentSequence: new Uint8Array(maxLzwDictionarySize),
+      currentSequenceLength: 0
+    };
+
+    for (let i = 0; i < 256; ++i) {
+      lzwState.dictionaryValues[i] = i;
+      lzwState.dictionaryLengths[i] = 1;
+    }
+
+    this.lzwState = lzwState;
+  }
+
+  readBits(n) {
+    let bitsCached = this.bitsCached;
+    let cachedData = this.cachedData;
+
+    while (bitsCached < n) {
+      const c = this.str.getByte();
+
+      if (c === -1) {
+        this.eof = true;
+        return null;
+      }
+
+      cachedData = cachedData << 8 | c;
+      bitsCached += 8;
+    }
+
+    this.bitsCached = bitsCached -= n;
+    this.cachedData = cachedData;
+    this.lastCode = null;
+    return cachedData >>> bitsCached & (1 << n) - 1;
+  }
+
+  readBlock() {
+    const blockSize = 512,
+          decodedSizeDelta = blockSize;
+    let estimatedDecodedSize = blockSize * 2;
+    let i, j, q;
+    const lzwState = this.lzwState;
+
+    if (!lzwState) {
+      return;
+    }
+
+    const earlyChange = lzwState.earlyChange;
+    let nextCode = lzwState.nextCode;
+    const dictionaryValues = lzwState.dictionaryValues;
+    const dictionaryLengths = lzwState.dictionaryLengths;
+    const dictionaryPrevCodes = lzwState.dictionaryPrevCodes;
+    let codeLength = lzwState.codeLength;
+    let prevCode = lzwState.prevCode;
+    const currentSequence = lzwState.currentSequence;
+    let currentSequenceLength = lzwState.currentSequenceLength;
+    let decodedLength = 0;
+    let currentBufferLength = this.bufferLength;
+    let buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
+
+    for (i = 0; i < blockSize; i++) {
+      const code = this.readBits(codeLength);
+      const hasPrev = currentSequenceLength > 0;
+
+      if (code < 256) {
+        currentSequence[0] = code;
+        currentSequenceLength = 1;
+      } else if (code >= 258) {
+        if (code < nextCode) {
+          currentSequenceLength = dictionaryLengths[code];
+
+          for (j = currentSequenceLength - 1, q = code; j >= 0; j--) {
+            currentSequence[j] = dictionaryValues[q];
+            q = dictionaryPrevCodes[q];
+          }
+        } else {
+          currentSequence[currentSequenceLength++] = currentSequence[0];
+        }
+      } else if (code === 256) {
+        codeLength = 9;
+        nextCode = 258;
+        currentSequenceLength = 0;
+        continue;
+      } else {
+        this.eof = true;
+        delete this.lzwState;
+        break;
+      }
+
+      if (hasPrev) {
+        dictionaryPrevCodes[nextCode] = prevCode;
+        dictionaryLengths[nextCode] = dictionaryLengths[prevCode] + 1;
+        dictionaryValues[nextCode] = currentSequence[0];
+        nextCode++;
+        codeLength = nextCode + earlyChange & nextCode + earlyChange - 1 ? codeLength : Math.min(Math.log(nextCode + earlyChange) / 0.6931471805599453 + 1, 12) | 0;
+      }
+
+      prevCode = code;
+      decodedLength += currentSequenceLength;
+
+      if (estimatedDecodedSize < decodedLength) {
+        do {
+          estimatedDecodedSize += decodedSizeDelta;
+        } while (estimatedDecodedSize < decodedLength);
+
+        buffer = this.ensureBuffer(this.bufferLength + estimatedDecodedSize);
+      }
+
+      for (j = 0; j < currentSequenceLength; j++) {
+        buffer[currentBufferLength++] = currentSequence[j];
+      }
+    }
+
+    lzwState.nextCode = nextCode;
+    lzwState.codeLength = codeLength;
+    lzwState.prevCode = prevCode;
+    lzwState.currentSequenceLength = currentSequenceLength;
+    this.bufferLength = currentBufferLength;
+  }
+
+}
+
+exports.LZWStream = LZWStream;

+ 181 - 0
lib/core/name_number_tree.js

@@ -0,0 +1,181 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.NumberTree = exports.NameTree = void 0;
+
+var _util = require("../shared/util.js");
+
+var _primitives = require("./primitives.js");
+
+class NameOrNumberTree {
+  constructor(root, xref, type) {
+    if (this.constructor === NameOrNumberTree) {
+      (0, _util.unreachable)("Cannot initialize NameOrNumberTree.");
+    }
+
+    this.root = root;
+    this.xref = xref;
+    this._type = type;
+  }
+
+  getAll() {
+    const map = new Map();
+
+    if (!this.root) {
+      return map;
+    }
+
+    const xref = this.xref;
+    const processed = new _primitives.RefSet();
+    processed.put(this.root);
+    const queue = [this.root];
+
+    while (queue.length > 0) {
+      const obj = xref.fetchIfRef(queue.shift());
+
+      if (!(0, _primitives.isDict)(obj)) {
+        continue;
+      }
+
+      if (obj.has("Kids")) {
+        const kids = obj.get("Kids");
+
+        for (let i = 0, ii = kids.length; i < ii; i++) {
+          const kid = kids[i];
+
+          if (processed.has(kid)) {
+            throw new _util.FormatError(`Duplicate entry in "${this._type}" tree.`);
+          }
+
+          queue.push(kid);
+          processed.put(kid);
+        }
+
+        continue;
+      }
+
+      const entries = obj.get(this._type);
+
+      if (!Array.isArray(entries)) {
+        continue;
+      }
+
+      for (let i = 0, ii = entries.length; i < ii; i += 2) {
+        map.set(xref.fetchIfRef(entries[i]), xref.fetchIfRef(entries[i + 1]));
+      }
+    }
+
+    return map;
+  }
+
+  get(key) {
+    if (!this.root) {
+      return null;
+    }
+
+    const xref = this.xref;
+    let kidsOrEntries = xref.fetchIfRef(this.root);
+    let loopCount = 0;
+    const MAX_LEVELS = 10;
+
+    while (kidsOrEntries.has("Kids")) {
+      if (++loopCount > MAX_LEVELS) {
+        (0, _util.warn)(`Search depth limit reached for "${this._type}" tree.`);
+        return null;
+      }
+
+      const kids = kidsOrEntries.get("Kids");
+
+      if (!Array.isArray(kids)) {
+        return null;
+      }
+
+      let l = 0,
+          r = kids.length - 1;
+
+      while (l <= r) {
+        const m = l + r >> 1;
+        const kid = xref.fetchIfRef(kids[m]);
+        const limits = kid.get("Limits");
+
+        if (key < xref.fetchIfRef(limits[0])) {
+          r = m - 1;
+        } else if (key > xref.fetchIfRef(limits[1])) {
+          l = m + 1;
+        } else {
+          kidsOrEntries = xref.fetchIfRef(kids[m]);
+          break;
+        }
+      }
+
+      if (l > r) {
+        return null;
+      }
+    }
+
+    const entries = kidsOrEntries.get(this._type);
+
+    if (Array.isArray(entries)) {
+      let l = 0,
+          r = entries.length - 2;
+
+      while (l <= r) {
+        const tmp = l + r >> 1,
+              m = tmp + (tmp & 1);
+        const currentKey = xref.fetchIfRef(entries[m]);
+
+        if (key < currentKey) {
+          r = m - 2;
+        } else if (key > currentKey) {
+          l = m + 2;
+        } else {
+          return xref.fetchIfRef(entries[m + 1]);
+        }
+      }
+    }
+
+    return null;
+  }
+
+}
+
+class NameTree extends NameOrNumberTree {
+  constructor(root, xref) {
+    super(root, xref, "Names");
+  }
+
+}
+
+exports.NameTree = NameTree;
+
+class NumberTree extends NameOrNumberTree {
+  constructor(root, xref) {
+    super(root, xref, "Nums");
+  }
+
+}
+
+exports.NumberTree = NumberTree;

+ 164 - 0
lib/core/object_loader.js

@@ -0,0 +1,164 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.ObjectLoader = void 0;
+
+var _primitives = require("./primitives.js");
+
+var _core_utils = require("./core_utils.js");
+
+var _util = require("../shared/util.js");
+
+function mayHaveChildren(value) {
+  return value instanceof _primitives.Ref || value instanceof _primitives.Dict || Array.isArray(value) || (0, _primitives.isStream)(value);
+}
+
+function addChildren(node, nodesToVisit) {
+  if (node instanceof _primitives.Dict) {
+    node = node.getRawValues();
+  } else if ((0, _primitives.isStream)(node)) {
+    node = node.dict.getRawValues();
+  } else if (!Array.isArray(node)) {
+    return;
+  }
+
+  for (const rawValue of node) {
+    if (mayHaveChildren(rawValue)) {
+      nodesToVisit.push(rawValue);
+    }
+  }
+}
+
+class ObjectLoader {
+  constructor(dict, keys, xref) {
+    this.dict = dict;
+    this.keys = keys;
+    this.xref = xref;
+    this.refSet = null;
+  }
+
+  async load() {
+    if (this.xref.stream.isDataLoaded) {
+      return undefined;
+    }
+
+    const {
+      keys,
+      dict
+    } = this;
+    this.refSet = new _primitives.RefSet();
+    const nodesToVisit = [];
+
+    for (let i = 0, ii = keys.length; i < ii; i++) {
+      const rawValue = dict.getRaw(keys[i]);
+
+      if (rawValue !== undefined) {
+        nodesToVisit.push(rawValue);
+      }
+    }
+
+    return this._walk(nodesToVisit);
+  }
+
+  async _walk(nodesToVisit) {
+    const nodesToRevisit = [];
+    const pendingRequests = [];
+
+    while (nodesToVisit.length) {
+      let currentNode = nodesToVisit.pop();
+
+      if (currentNode instanceof _primitives.Ref) {
+        if (this.refSet.has(currentNode)) {
+          continue;
+        }
+
+        try {
+          this.refSet.put(currentNode);
+          currentNode = this.xref.fetch(currentNode);
+        } catch (ex) {
+          if (!(ex instanceof _core_utils.MissingDataException)) {
+            (0, _util.warn)(`ObjectLoader._walk - requesting all data: "${ex}".`);
+            this.refSet = null;
+            const {
+              manager
+            } = this.xref.stream;
+            return manager.requestAllChunks();
+          }
+
+          nodesToRevisit.push(currentNode);
+          pendingRequests.push({
+            begin: ex.begin,
+            end: ex.end
+          });
+        }
+      }
+
+      if ((0, _primitives.isStream)(currentNode)) {
+        const baseStreams = currentNode.getBaseStreams();
+
+        if (baseStreams) {
+          let foundMissingData = false;
+
+          for (const stream of baseStreams) {
+            if (stream.isDataLoaded) {
+              continue;
+            }
+
+            foundMissingData = true;
+            pendingRequests.push({
+              begin: stream.start,
+              end: stream.end
+            });
+          }
+
+          if (foundMissingData) {
+            nodesToRevisit.push(currentNode);
+          }
+        }
+      }
+
+      addChildren(currentNode, nodesToVisit);
+    }
+
+    if (pendingRequests.length) {
+      await this.xref.stream.manager.requestRanges(pendingRequests);
+
+      for (const node of nodesToRevisit) {
+        if (node instanceof _primitives.Ref) {
+          this.refSet.remove(node);
+        }
+      }
+
+      return this._walk(nodesToRevisit);
+    }
+
+    this.refSet = null;
+    return undefined;
+  }
+
+}
+
+exports.ObjectLoader = ObjectLoader;

+ 156 - 0
lib/core/opentype_file_builder.js

@@ -0,0 +1,156 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.OpenTypeFileBuilder = void 0;
+
+var _core_utils = require("./core_utils.js");
+
+var _util = require("../shared/util.js");
+
+function writeInt16(dest, offset, num) {
+  dest[offset] = num >> 8 & 0xff;
+  dest[offset + 1] = num & 0xff;
+}
+
+function writeInt32(dest, offset, num) {
+  dest[offset] = num >> 24 & 0xff;
+  dest[offset + 1] = num >> 16 & 0xff;
+  dest[offset + 2] = num >> 8 & 0xff;
+  dest[offset + 3] = num & 0xff;
+}
+
+function writeData(dest, offset, data) {
+  if (data instanceof Uint8Array) {
+    dest.set(data, offset);
+  } else if (typeof data === "string") {
+    for (let i = 0, ii = data.length; i < ii; i++) {
+      dest[offset++] = data.charCodeAt(i) & 0xff;
+    }
+  } else {
+    for (let i = 0, ii = data.length; i < ii; i++) {
+      dest[offset++] = data[i] & 0xff;
+    }
+  }
+}
+
+const OTF_HEADER_SIZE = 12;
+const OTF_TABLE_ENTRY_SIZE = 16;
+
+class OpenTypeFileBuilder {
+  constructor(sfnt) {
+    this.sfnt = sfnt;
+    this.tables = Object.create(null);
+  }
+
+  static getSearchParams(entriesCount, entrySize) {
+    let maxPower2 = 1,
+        log2 = 0;
+
+    while ((maxPower2 ^ entriesCount) > maxPower2) {
+      maxPower2 <<= 1;
+      log2++;
+    }
+
+    const searchRange = maxPower2 * entrySize;
+    return {
+      range: searchRange,
+      entry: log2,
+      rangeShift: entrySize * entriesCount - searchRange
+    };
+  }
+
+  toArray() {
+    let sfnt = this.sfnt;
+    const tables = this.tables;
+    const tablesNames = Object.keys(tables);
+    tablesNames.sort();
+    const numTables = tablesNames.length;
+    let i, j, jj, table, tableName;
+    let offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE;
+    const tableOffsets = [offset];
+
+    for (i = 0; i < numTables; i++) {
+      table = tables[tablesNames[i]];
+      const paddedLength = (table.length + 3 & ~3) >>> 0;
+      offset += paddedLength;
+      tableOffsets.push(offset);
+    }
+
+    const file = new Uint8Array(offset);
+
+    for (i = 0; i < numTables; i++) {
+      table = tables[tablesNames[i]];
+      writeData(file, tableOffsets[i], table);
+    }
+
+    if (sfnt === "true") {
+      sfnt = (0, _util.string32)(0x00010000);
+    }
+
+    file[0] = sfnt.charCodeAt(0) & 0xff;
+    file[1] = sfnt.charCodeAt(1) & 0xff;
+    file[2] = sfnt.charCodeAt(2) & 0xff;
+    file[3] = sfnt.charCodeAt(3) & 0xff;
+    writeInt16(file, 4, numTables);
+    const searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16);
+    writeInt16(file, 6, searchParams.range);
+    writeInt16(file, 8, searchParams.entry);
+    writeInt16(file, 10, searchParams.rangeShift);
+    offset = OTF_HEADER_SIZE;
+
+    for (i = 0; i < numTables; i++) {
+      tableName = tablesNames[i];
+      file[offset] = tableName.charCodeAt(0) & 0xff;
+      file[offset + 1] = tableName.charCodeAt(1) & 0xff;
+      file[offset + 2] = tableName.charCodeAt(2) & 0xff;
+      file[offset + 3] = tableName.charCodeAt(3) & 0xff;
+      let checksum = 0;
+
+      for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) {
+        const quad = (0, _core_utils.readUint32)(file, j);
+        checksum = checksum + quad >>> 0;
+      }
+
+      writeInt32(file, offset + 4, checksum);
+      writeInt32(file, offset + 8, tableOffsets[i]);
+      writeInt32(file, offset + 12, tables[tableName].length);
+      offset += OTF_TABLE_ENTRY_SIZE;
+    }
+
+    return file;
+  }
+
+  addTable(tag, data) {
+    if (tag in this.tables) {
+      throw new Error("Table " + tag + " already exists");
+    }
+
+    this.tables[tag] = data;
+  }
+
+}
+
+exports.OpenTypeFileBuilder = OpenTypeFileBuilder;

+ 510 - 517
lib/core/operator_list.js

@@ -28,403 +28,423 @@ exports.OperatorList = void 0;
 
 var _util = require("../shared/util.js");
 
-const QueueOptimizer = function QueueOptimizerClosure() {
-  function addState(parentState, pattern, checkFn, iterateFn, processFn) {
-    let state = parentState;
+function addState(parentState, pattern, checkFn, iterateFn, processFn) {
+  let state = parentState;
 
-    for (let i = 0, ii = pattern.length - 1; i < ii; i++) {
-      const item = pattern[i];
-      state = state[item] || (state[item] = []);
-    }
-
-    state[pattern[pattern.length - 1]] = {
-      checkFn,
-      iterateFn,
-      processFn
-    };
+  for (let i = 0, ii = pattern.length - 1; i < ii; i++) {
+    const item = pattern[i];
+    state = state[item] || (state[item] = []);
   }
 
-  function handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray) {
-    const iFirstPIMXO = iFirstSave + 2;
-    let i;
+  state[pattern[pattern.length - 1]] = {
+    checkFn,
+    iterateFn,
+    processFn
+  };
+}
 
-    for (i = 0; i < count; i++) {
-      const arg = argsArray[iFirstPIMXO + 4 * i];
-      const imageMask = arg.length === 1 && arg[0];
+function handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray) {
+  const iFirstPIMXO = iFirstSave + 2;
+  let i;
 
-      if (imageMask && imageMask.width === 1 && imageMask.height === 1 && (!imageMask.data.length || imageMask.data.length === 1 && imageMask.data[0] === 0)) {
-        fnArray[iFirstPIMXO + 4 * i] = _util.OPS.paintSolidColorImageMask;
-        continue;
-      }
+  for (i = 0; i < count; i++) {
+    const arg = argsArray[iFirstPIMXO + 4 * i];
+    const imageMask = arg.length === 1 && arg[0];
 
-      break;
+    if (imageMask && imageMask.width === 1 && imageMask.height === 1 && (!imageMask.data.length || imageMask.data.length === 1 && imageMask.data[0] === 0)) {
+      fnArray[iFirstPIMXO + 4 * i] = _util.OPS.paintSolidColorImageMask;
+      continue;
     }
 
-    return count - i;
+    break;
   }
 
-  const InitialState = [];
-  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintInlineImageXObject, _util.OPS.restore], null, function iterateInlineImageGroup(context, i) {
-    const fnArray = context.fnArray;
-    const iFirstSave = context.iCurr - 3;
-    const pos = (i - iFirstSave) % 4;
+  return count - i;
+}
 
-    switch (pos) {
-      case 0:
-        return fnArray[i] === _util.OPS.save;
+const InitialState = [];
+addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintInlineImageXObject, _util.OPS.restore], null, function iterateInlineImageGroup(context, i) {
+  const fnArray = context.fnArray;
+  const iFirstSave = context.iCurr - 3;
+  const pos = (i - iFirstSave) % 4;
 
-      case 1:
-        return fnArray[i] === _util.OPS.transform;
+  switch (pos) {
+    case 0:
+      return fnArray[i] === _util.OPS.save;
 
-      case 2:
-        return fnArray[i] === _util.OPS.paintInlineImageXObject;
+    case 1:
+      return fnArray[i] === _util.OPS.transform;
 
-      case 3:
-        return fnArray[i] === _util.OPS.restore;
-    }
+    case 2:
+      return fnArray[i] === _util.OPS.paintInlineImageXObject;
+
+    case 3:
+      return fnArray[i] === _util.OPS.restore;
+  }
+
+  throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`);
+}, function foundInlineImageGroup(context, i) {
+  const MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
+  const MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
+  const MAX_WIDTH = 1000;
+  const IMAGE_PADDING = 1;
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const curr = context.iCurr;
+  const iFirstSave = curr - 3;
+  const iFirstTransform = curr - 2;
+  const iFirstPIIXO = curr - 1;
+  const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
+
+  if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
+    return i - (i - iFirstSave) % 4;
+  }
 
-    throw new Error(`iterateInlineImageGroup - invalid pos: ${pos}`);
-  }, function foundInlineImageGroup(context, i) {
-    const MIN_IMAGES_IN_INLINE_IMAGES_BLOCK = 10;
-    const MAX_IMAGES_IN_INLINE_IMAGES_BLOCK = 200;
-    const MAX_WIDTH = 1000;
-    const IMAGE_PADDING = 1;
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const curr = context.iCurr;
-    const iFirstSave = curr - 3;
-    const iFirstTransform = curr - 2;
-    const iFirstPIIXO = curr - 1;
-    const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_INLINE_IMAGES_BLOCK);
-
-    if (count < MIN_IMAGES_IN_INLINE_IMAGES_BLOCK) {
-      return i - (i - iFirstSave) % 4;
+  let maxX = 0;
+  const map = [];
+  let maxLineHeight = 0;
+  let currentX = IMAGE_PADDING,
+      currentY = IMAGE_PADDING;
+
+  for (let q = 0; q < count; q++) {
+    const transform = argsArray[iFirstTransform + (q << 2)];
+    const img = argsArray[iFirstPIIXO + (q << 2)][0];
+
+    if (currentX + img.width > MAX_WIDTH) {
+      maxX = Math.max(maxX, currentX);
+      currentY += maxLineHeight + 2 * IMAGE_PADDING;
+      currentX = 0;
+      maxLineHeight = 0;
     }
 
-    let maxX = 0;
-    const map = [];
-    let maxLineHeight = 0;
-    let currentX = IMAGE_PADDING,
-        currentY = IMAGE_PADDING;
+    map.push({
+      transform,
+      x: currentX,
+      y: currentY,
+      w: img.width,
+      h: img.height
+    });
+    currentX += img.width + 2 * IMAGE_PADDING;
+    maxLineHeight = Math.max(maxLineHeight, img.height);
+  }
 
-    for (let q = 0; q < count; q++) {
-      const transform = argsArray[iFirstTransform + (q << 2)];
-      const img = argsArray[iFirstPIIXO + (q << 2)][0];
-
-      if (currentX + img.width > MAX_WIDTH) {
-        maxX = Math.max(maxX, currentX);
-        currentY += maxLineHeight + 2 * IMAGE_PADDING;
-        currentX = 0;
-        maxLineHeight = 0;
-      }
+  const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
+  const imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
+  const imgData = new Uint8ClampedArray(imgWidth * imgHeight * 4);
+  const imgRowSize = imgWidth << 2;
+
+  for (let q = 0; q < count; q++) {
+    const data = argsArray[iFirstPIIXO + (q << 2)][0].data;
+    const rowSize = map[q].w << 2;
+    let dataOffset = 0;
+    let offset = map[q].x + map[q].y * imgWidth << 2;
+    imgData.set(data.subarray(0, rowSize), offset - imgRowSize);
+
+    for (let k = 0, kk = map[q].h; k < kk; k++) {
+      imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);
+      dataOffset += rowSize;
+      offset += imgRowSize;
+    }
 
-      map.push({
-        transform,
-        x: currentX,
-        y: currentY,
-        w: img.width,
-        h: img.height
-      });
-      currentX += img.width + 2 * IMAGE_PADDING;
-      maxLineHeight = Math.max(maxLineHeight, img.height);
+    imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);
+
+    while (offset >= 0) {
+      data[offset - 4] = data[offset];
+      data[offset - 3] = data[offset + 1];
+      data[offset - 2] = data[offset + 2];
+      data[offset - 1] = data[offset + 3];
+      data[offset + rowSize] = data[offset + rowSize - 4];
+      data[offset + rowSize + 1] = data[offset + rowSize - 3];
+      data[offset + rowSize + 2] = data[offset + rowSize - 2];
+      data[offset + rowSize + 3] = data[offset + rowSize - 1];
+      offset -= imgRowSize;
     }
+  }
 
-    const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING;
-    const imgHeight = currentY + maxLineHeight + IMAGE_PADDING;
-    const imgData = new Uint8ClampedArray(imgWidth * imgHeight * 4);
-    const imgRowSize = imgWidth << 2;
+  fnArray.splice(iFirstSave, count * 4, _util.OPS.paintInlineImageXObjectGroup);
+  argsArray.splice(iFirstSave, count * 4, [{
+    width: imgWidth,
+    height: imgHeight,
+    kind: _util.ImageKind.RGBA_32BPP,
+    data: imgData
+  }, map]);
+  return iFirstSave + 1;
+});
+addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageMaskXObject, _util.OPS.restore], null, function iterateImageMaskGroup(context, i) {
+  const fnArray = context.fnArray;
+  const iFirstSave = context.iCurr - 3;
+  const pos = (i - iFirstSave) % 4;
 
-    for (let q = 0; q < count; q++) {
-      const data = argsArray[iFirstPIIXO + (q << 2)][0].data;
-      const rowSize = map[q].w << 2;
-      let dataOffset = 0;
-      let offset = map[q].x + map[q].y * imgWidth << 2;
-      imgData.set(data.subarray(0, rowSize), offset - imgRowSize);
-
-      for (let k = 0, kk = map[q].h; k < kk; k++) {
-        imgData.set(data.subarray(dataOffset, dataOffset + rowSize), offset);
-        dataOffset += rowSize;
-        offset += imgRowSize;
-      }
+  switch (pos) {
+    case 0:
+      return fnArray[i] === _util.OPS.save;
 
-      imgData.set(data.subarray(dataOffset - rowSize, dataOffset), offset);
-
-      while (offset >= 0) {
-        data[offset - 4] = data[offset];
-        data[offset - 3] = data[offset + 1];
-        data[offset - 2] = data[offset + 2];
-        data[offset - 1] = data[offset + 3];
-        data[offset + rowSize] = data[offset + rowSize - 4];
-        data[offset + rowSize + 1] = data[offset + rowSize - 3];
-        data[offset + rowSize + 2] = data[offset + rowSize - 2];
-        data[offset + rowSize + 3] = data[offset + rowSize - 1];
-        offset -= imgRowSize;
+    case 1:
+      return fnArray[i] === _util.OPS.transform;
+
+    case 2:
+      return fnArray[i] === _util.OPS.paintImageMaskXObject;
+
+    case 3:
+      return fnArray[i] === _util.OPS.restore;
+  }
+
+  throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`);
+}, function foundImageMaskGroup(context, i) {
+  const MIN_IMAGES_IN_MASKS_BLOCK = 10;
+  const MAX_IMAGES_IN_MASKS_BLOCK = 100;
+  const MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const curr = context.iCurr;
+  const iFirstSave = curr - 3;
+  const iFirstTransform = curr - 2;
+  const iFirstPIMXO = curr - 1;
+  let count = Math.floor((i - iFirstSave) / 4);
+  count = handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray);
+
+  if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
+    return i - (i - iFirstSave) % 4;
+  }
+
+  let isSameImage = false;
+  let iTransform, transformArgs;
+  const firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
+  const firstTransformArg0 = argsArray[iFirstTransform][0],
+        firstTransformArg1 = argsArray[iFirstTransform][1],
+        firstTransformArg2 = argsArray[iFirstTransform][2],
+        firstTransformArg3 = argsArray[iFirstTransform][3];
+
+  if (firstTransformArg1 === firstTransformArg2) {
+    isSameImage = true;
+    iTransform = iFirstTransform + 4;
+    let iPIMXO = iFirstPIMXO + 4;
+
+    for (let q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
+      transformArgs = argsArray[iTransform];
+
+      if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== firstTransformArg1 || transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3) {
+        if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
+          isSameImage = false;
+        } else {
+          count = q;
+        }
+
+        break;
       }
     }
+  }
 
-    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintInlineImageXObjectGroup);
-    argsArray.splice(iFirstSave, count * 4, [{
-      width: imgWidth,
-      height: imgHeight,
-      kind: _util.ImageKind.RGBA_32BPP,
-      data: imgData
-    }, map]);
-    return iFirstSave + 1;
-  });
-  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageMaskXObject, _util.OPS.restore], null, function iterateImageMaskGroup(context, i) {
-    const fnArray = context.fnArray;
-    const iFirstSave = context.iCurr - 3;
-    const pos = (i - iFirstSave) % 4;
-
-    switch (pos) {
-      case 0:
-        return fnArray[i] === _util.OPS.save;
-
-      case 1:
-        return fnArray[i] === _util.OPS.transform;
-
-      case 2:
-        return fnArray[i] === _util.OPS.paintImageMaskXObject;
-
-      case 3:
-        return fnArray[i] === _util.OPS.restore;
-    }
+  if (isSameImage) {
+    count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
+    const positions = new Float32Array(count * 2);
+    iTransform = iFirstTransform;
 
-    throw new Error(`iterateImageMaskGroup - invalid pos: ${pos}`);
-  }, function foundImageMaskGroup(context, i) {
-    const MIN_IMAGES_IN_MASKS_BLOCK = 10;
-    const MAX_IMAGES_IN_MASKS_BLOCK = 100;
-    const MAX_SAME_IMAGES_IN_MASKS_BLOCK = 1000;
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const curr = context.iCurr;
-    const iFirstSave = curr - 3;
-    const iFirstTransform = curr - 2;
-    const iFirstPIMXO = curr - 1;
-    let count = Math.floor((i - iFirstSave) / 4);
-    count = handlePaintSolidColorImageMask(iFirstSave, count, fnArray, argsArray);
-
-    if (count < MIN_IMAGES_IN_MASKS_BLOCK) {
-      return i - (i - iFirstSave) % 4;
+    for (let q = 0; q < count; q++, iTransform += 4) {
+      transformArgs = argsArray[iTransform];
+      positions[q << 1] = transformArgs[4];
+      positions[(q << 1) + 1] = transformArgs[5];
     }
 
-    let isSameImage = false;
-    let iTransform, transformArgs;
-    const firstPIMXOArg0 = argsArray[iFirstPIMXO][0];
-    const firstTransformArg0 = argsArray[iFirstTransform][0],
-          firstTransformArg1 = argsArray[iFirstTransform][1],
-          firstTransformArg2 = argsArray[iFirstTransform][2],
-          firstTransformArg3 = argsArray[iFirstTransform][3];
-
-    if (firstTransformArg1 === firstTransformArg2) {
-      isSameImage = true;
-      iTransform = iFirstTransform + 4;
-      let iPIMXO = iFirstPIMXO + 4;
-
-      for (let q = 1; q < count; q++, iTransform += 4, iPIMXO += 4) {
-        transformArgs = argsArray[iTransform];
-
-        if (argsArray[iPIMXO][0] !== firstPIMXOArg0 || transformArgs[0] !== firstTransformArg0 || transformArgs[1] !== firstTransformArg1 || transformArgs[2] !== firstTransformArg2 || transformArgs[3] !== firstTransformArg3) {
-          if (q < MIN_IMAGES_IN_MASKS_BLOCK) {
-            isSameImage = false;
-          } else {
-            count = q;
-          }
+    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectRepeat);
+    argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg1, firstTransformArg2, firstTransformArg3, positions]);
+  } else {
+    count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
+    const images = [];
 
-          break;
-        }
-      }
+    for (let q = 0; q < count; q++) {
+      transformArgs = argsArray[iFirstTransform + (q << 2)];
+      const maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
+      images.push({
+        data: maskParams.data,
+        width: maskParams.width,
+        height: maskParams.height,
+        transform: transformArgs
+      });
     }
 
-    if (isSameImage) {
-      count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK);
-      const positions = new Float32Array(count * 2);
-      iTransform = iFirstTransform;
+    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectGroup);
+    argsArray.splice(iFirstSave, count * 4, [images]);
+  }
 
-      for (let q = 0; q < count; q++, iTransform += 4) {
-        transformArgs = argsArray[iTransform];
-        positions[q << 1] = transformArgs[4];
-        positions[(q << 1) + 1] = transformArgs[5];
+  return iFirstSave + 1;
+});
+addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageXObject, _util.OPS.restore], function (context) {
+  const argsArray = context.argsArray;
+  const iFirstTransform = context.iCurr - 2;
+  return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;
+}, function iterateImageGroup(context, i) {
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const iFirstSave = context.iCurr - 3;
+  const pos = (i - iFirstSave) % 4;
+
+  switch (pos) {
+    case 0:
+      return fnArray[i] === _util.OPS.save;
+
+    case 1:
+      if (fnArray[i] !== _util.OPS.transform) {
+        return false;
       }
 
-      fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectRepeat);
-      argsArray.splice(iFirstSave, count * 4, [firstPIMXOArg0, firstTransformArg0, firstTransformArg1, firstTransformArg2, firstTransformArg3, positions]);
-    } else {
-      count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK);
-      const images = [];
-
-      for (let q = 0; q < count; q++) {
-        transformArgs = argsArray[iFirstTransform + (q << 2)];
-        const maskParams = argsArray[iFirstPIMXO + (q << 2)][0];
-        images.push({
-          data: maskParams.data,
-          width: maskParams.width,
-          height: maskParams.height,
-          transform: transformArgs
-        });
+      const iFirstTransform = context.iCurr - 2;
+      const firstTransformArg0 = argsArray[iFirstTransform][0];
+      const firstTransformArg3 = argsArray[iFirstTransform][3];
+
+      if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) {
+        return false;
       }
 
-      fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageMaskXObjectGroup);
-      argsArray.splice(iFirstSave, count * 4, [images]);
-    }
+      return true;
 
-    return iFirstSave + 1;
-  });
-  addState(InitialState, [_util.OPS.save, _util.OPS.transform, _util.OPS.paintImageXObject, _util.OPS.restore], function (context) {
-    const argsArray = context.argsArray;
-    const iFirstTransform = context.iCurr - 2;
-    return argsArray[iFirstTransform][1] === 0 && argsArray[iFirstTransform][2] === 0;
-  }, function iterateImageGroup(context, i) {
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const iFirstSave = context.iCurr - 3;
-    const pos = (i - iFirstSave) % 4;
-
-    switch (pos) {
-      case 0:
-        return fnArray[i] === _util.OPS.save;
-
-      case 1:
-        if (fnArray[i] !== _util.OPS.transform) {
-          return false;
-        }
+    case 2:
+      if (fnArray[i] !== _util.OPS.paintImageXObject) {
+        return false;
+      }
 
-        const iFirstTransform = context.iCurr - 2;
-        const firstTransformArg0 = argsArray[iFirstTransform][0];
-        const firstTransformArg3 = argsArray[iFirstTransform][3];
+      const iFirstPIXO = context.iCurr - 1;
+      const firstPIXOArg0 = argsArray[iFirstPIXO][0];
 
-        if (argsArray[i][0] !== firstTransformArg0 || argsArray[i][1] !== 0 || argsArray[i][2] !== 0 || argsArray[i][3] !== firstTransformArg3) {
-          return false;
-        }
+      if (argsArray[i][0] !== firstPIXOArg0) {
+        return false;
+      }
 
-        return true;
+      return true;
 
-      case 2:
-        if (fnArray[i] !== _util.OPS.paintImageXObject) {
-          return false;
-        }
+    case 3:
+      return fnArray[i] === _util.OPS.restore;
+  }
 
-        const iFirstPIXO = context.iCurr - 1;
-        const firstPIXOArg0 = argsArray[iFirstPIXO][0];
+  throw new Error(`iterateImageGroup - invalid pos: ${pos}`);
+}, function (context, i) {
+  const MIN_IMAGES_IN_BLOCK = 3;
+  const MAX_IMAGES_IN_BLOCK = 1000;
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const curr = context.iCurr;
+  const iFirstSave = curr - 3;
+  const iFirstTransform = curr - 2;
+  const iFirstPIXO = curr - 1;
+  const firstPIXOArg0 = argsArray[iFirstPIXO][0];
+  const firstTransformArg0 = argsArray[iFirstTransform][0];
+  const firstTransformArg3 = argsArray[iFirstTransform][3];
+  const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK);
+
+  if (count < MIN_IMAGES_IN_BLOCK) {
+    return i - (i - iFirstSave) % 4;
+  }
 
-        if (argsArray[i][0] !== firstPIXOArg0) {
-          return false;
-        }
+  const positions = new Float32Array(count * 2);
+  let iTransform = iFirstTransform;
 
-        return true;
+  for (let q = 0; q < count; q++, iTransform += 4) {
+    const transformArgs = argsArray[iTransform];
+    positions[q << 1] = transformArgs[4];
+    positions[(q << 1) + 1] = transformArgs[5];
+  }
 
-      case 3:
-        return fnArray[i] === _util.OPS.restore;
-    }
+  const args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];
+  fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageXObjectRepeat);
+  argsArray.splice(iFirstSave, count * 4, args);
+  return iFirstSave + 1;
+});
+addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function iterateShowTextGroup(context, i) {
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const iFirstSave = context.iCurr - 4;
+  const pos = (i - iFirstSave) % 5;
 
-    throw new Error(`iterateImageGroup - invalid pos: ${pos}`);
-  }, function (context, i) {
-    const MIN_IMAGES_IN_BLOCK = 3;
-    const MAX_IMAGES_IN_BLOCK = 1000;
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const curr = context.iCurr;
-    const iFirstSave = curr - 3;
-    const iFirstTransform = curr - 2;
-    const iFirstPIXO = curr - 1;
-    const firstPIXOArg0 = argsArray[iFirstPIXO][0];
-    const firstTransformArg0 = argsArray[iFirstTransform][0];
-    const firstTransformArg3 = argsArray[iFirstTransform][3];
-    const count = Math.min(Math.floor((i - iFirstSave) / 4), MAX_IMAGES_IN_BLOCK);
-
-    if (count < MIN_IMAGES_IN_BLOCK) {
-      return i - (i - iFirstSave) % 4;
-    }
+  switch (pos) {
+    case 0:
+      return fnArray[i] === _util.OPS.beginText;
 
-    const positions = new Float32Array(count * 2);
-    let iTransform = iFirstTransform;
+    case 1:
+      return fnArray[i] === _util.OPS.setFont;
 
-    for (let q = 0; q < count; q++, iTransform += 4) {
-      const transformArgs = argsArray[iTransform];
-      positions[q << 1] = transformArgs[4];
-      positions[(q << 1) + 1] = transformArgs[5];
-    }
+    case 2:
+      return fnArray[i] === _util.OPS.setTextMatrix;
 
-    const args = [firstPIXOArg0, firstTransformArg0, firstTransformArg3, positions];
-    fnArray.splice(iFirstSave, count * 4, _util.OPS.paintImageXObjectRepeat);
-    argsArray.splice(iFirstSave, count * 4, args);
-    return iFirstSave + 1;
-  });
-  addState(InitialState, [_util.OPS.beginText, _util.OPS.setFont, _util.OPS.setTextMatrix, _util.OPS.showText, _util.OPS.endText], null, function iterateShowTextGroup(context, i) {
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const iFirstSave = context.iCurr - 4;
-    const pos = (i - iFirstSave) % 5;
-
-    switch (pos) {
-      case 0:
-        return fnArray[i] === _util.OPS.beginText;
-
-      case 1:
-        return fnArray[i] === _util.OPS.setFont;
-
-      case 2:
-        return fnArray[i] === _util.OPS.setTextMatrix;
-
-      case 3:
-        if (fnArray[i] !== _util.OPS.showText) {
-          return false;
-        }
+    case 3:
+      if (fnArray[i] !== _util.OPS.showText) {
+        return false;
+      }
 
-        const iFirstSetFont = context.iCurr - 3;
-        const firstSetFontArg0 = argsArray[iFirstSetFont][0];
-        const firstSetFontArg1 = argsArray[iFirstSetFont][1];
+      const iFirstSetFont = context.iCurr - 3;
+      const firstSetFontArg0 = argsArray[iFirstSetFont][0];
+      const firstSetFontArg1 = argsArray[iFirstSetFont][1];
 
-        if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) {
-          return false;
-        }
+      if (argsArray[i][0] !== firstSetFontArg0 || argsArray[i][1] !== firstSetFontArg1) {
+        return false;
+      }
 
-        return true;
+      return true;
 
-      case 4:
-        return fnArray[i] === _util.OPS.endText;
-    }
+    case 4:
+      return fnArray[i] === _util.OPS.endText;
+  }
 
-    throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`);
-  }, function (context, i) {
-    const MIN_CHARS_IN_BLOCK = 3;
-    const MAX_CHARS_IN_BLOCK = 1000;
-    const fnArray = context.fnArray,
-          argsArray = context.argsArray;
-    const curr = context.iCurr;
-    const iFirstBeginText = curr - 4;
-    const iFirstSetFont = curr - 3;
-    const iFirstSetTextMatrix = curr - 2;
-    const iFirstShowText = curr - 1;
-    const iFirstEndText = curr;
-    const firstSetFontArg0 = argsArray[iFirstSetFont][0];
-    const firstSetFontArg1 = argsArray[iFirstSetFont][1];
-    let count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
-
-    if (count < MIN_CHARS_IN_BLOCK) {
-      return i - (i - iFirstBeginText) % 5;
-    }
+  throw new Error(`iterateShowTextGroup - invalid pos: ${pos}`);
+}, function (context, i) {
+  const MIN_CHARS_IN_BLOCK = 3;
+  const MAX_CHARS_IN_BLOCK = 1000;
+  const fnArray = context.fnArray,
+        argsArray = context.argsArray;
+  const curr = context.iCurr;
+  const iFirstBeginText = curr - 4;
+  const iFirstSetFont = curr - 3;
+  const iFirstSetTextMatrix = curr - 2;
+  const iFirstShowText = curr - 1;
+  const iFirstEndText = curr;
+  const firstSetFontArg0 = argsArray[iFirstSetFont][0];
+  const firstSetFontArg1 = argsArray[iFirstSetFont][1];
+  let count = Math.min(Math.floor((i - iFirstBeginText) / 5), MAX_CHARS_IN_BLOCK);
+
+  if (count < MIN_CHARS_IN_BLOCK) {
+    return i - (i - iFirstBeginText) % 5;
+  }
 
-    let iFirst = iFirstBeginText;
+  let iFirst = iFirstBeginText;
 
-    if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
-      count++;
-      iFirst -= 5;
-    }
+  if (iFirstBeginText >= 4 && fnArray[iFirstBeginText - 4] === fnArray[iFirstSetFont] && fnArray[iFirstBeginText - 3] === fnArray[iFirstSetTextMatrix] && fnArray[iFirstBeginText - 2] === fnArray[iFirstShowText] && fnArray[iFirstBeginText - 1] === fnArray[iFirstEndText] && argsArray[iFirstBeginText - 4][0] === firstSetFontArg0 && argsArray[iFirstBeginText - 4][1] === firstSetFontArg1) {
+    count++;
+    iFirst -= 5;
+  }
 
-    let iEndText = iFirst + 4;
+  let iEndText = iFirst + 4;
 
-    for (let q = 1; q < count; q++) {
-      fnArray.splice(iEndText, 3);
-      argsArray.splice(iEndText, 3);
-      iEndText += 2;
-    }
+  for (let q = 1; q < count; q++) {
+    fnArray.splice(iEndText, 3);
+    argsArray.splice(iEndText, 3);
+    iEndText += 2;
+  }
 
-    return iEndText + 1;
-  });
+  return iEndText + 1;
+});
 
-  function QueueOptimizer(queue) {
+class NullOptimizer {
+  constructor(queue) {
     this.queue = queue;
+  }
+
+  _optimize() {}
+
+  push(fn, args) {
+    this.queue.fnArray.push(fn);
+    this.queue.argsArray.push(args);
+
+    this._optimize();
+  }
+
+  flush() {}
+
+  reset() {}
+
+}
+
+class QueueOptimizer extends NullOptimizer {
+  constructor(queue) {
+    super(queue);
     this.state = null;
     this.context = {
       iCurr: 0,
@@ -435,116 +455,92 @@ const QueueOptimizer = function QueueOptimizerClosure() {
     this.lastProcessed = 0;
   }
 
-  QueueOptimizer.prototype = {
-    _optimize() {
-      const fnArray = this.queue.fnArray;
-      let i = this.lastProcessed,
-          ii = fnArray.length;
-      let state = this.state;
-      let match = this.match;
-
-      if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) {
-        this.lastProcessed = ii;
-        return;
-      }
-
-      const context = this.context;
-
-      while (i < ii) {
-        if (match) {
-          const iterate = (0, match.iterateFn)(context, i);
-
-          if (iterate) {
-            i++;
-            continue;
-          }
+  _optimize() {
+    const fnArray = this.queue.fnArray;
+    let i = this.lastProcessed,
+        ii = fnArray.length;
+    let state = this.state;
+    let match = this.match;
 
-          i = (0, match.processFn)(context, i + 1);
-          ii = fnArray.length;
-          match = null;
-          state = null;
+    if (!state && !match && i + 1 === ii && !InitialState[fnArray[i]]) {
+      this.lastProcessed = ii;
+      return;
+    }
 
-          if (i >= ii) {
-            break;
-          }
-        }
+    const context = this.context;
 
-        state = (state || InitialState)[fnArray[i]];
+    while (i < ii) {
+      if (match) {
+        const iterate = (0, match.iterateFn)(context, i);
 
-        if (!state || Array.isArray(state)) {
+        if (iterate) {
           i++;
           continue;
         }
 
-        context.iCurr = i;
-        i++;
+        i = (0, match.processFn)(context, i + 1);
+        ii = fnArray.length;
+        match = null;
+        state = null;
 
-        if (state.checkFn && !(0, state.checkFn)(context)) {
-          state = null;
-          continue;
+        if (i >= ii) {
+          break;
         }
-
-        match = state;
-        state = null;
       }
 
-      this.state = state;
-      this.match = match;
-      this.lastProcessed = i;
-    },
+      state = (state || InitialState)[fnArray[i]];
 
-    push(fn, args) {
-      this.queue.fnArray.push(fn);
-      this.queue.argsArray.push(args);
-
-      this._optimize();
-    },
+      if (!state || Array.isArray(state)) {
+        i++;
+        continue;
+      }
 
-    flush() {
-      while (this.match) {
-        const length = this.queue.fnArray.length;
-        this.lastProcessed = (0, this.match.processFn)(this.context, length);
-        this.match = null;
-        this.state = null;
+      context.iCurr = i;
+      i++;
 
-        this._optimize();
+      if (state.checkFn && !(0, state.checkFn)(context)) {
+        state = null;
+        continue;
       }
-    },
 
-    reset() {
-      this.state = null;
-      this.match = null;
-      this.lastProcessed = 0;
+      match = state;
+      state = null;
     }
 
-  };
-  return QueueOptimizer;
-}();
-
-const NullOptimizer = function NullOptimizerClosure() {
-  function NullOptimizer(queue) {
-    this.queue = queue;
+    this.state = state;
+    this.match = match;
+    this.lastProcessed = i;
   }
 
-  NullOptimizer.prototype = {
-    push(fn, args) {
-      this.queue.fnArray.push(fn);
-      this.queue.argsArray.push(args);
-    },
+  flush() {
+    while (this.match) {
+      const length = this.queue.fnArray.length;
+      this.lastProcessed = (0, this.match.processFn)(this.context, length);
+      this.match = null;
+      this.state = null;
 
-    flush() {},
+      this._optimize();
+    }
+  }
 
-    reset() {}
+  reset() {
+    this.state = null;
+    this.match = null;
+    this.lastProcessed = 0;
+  }
 
-  };
-  return NullOptimizer;
-}();
+}
 
-const OperatorList = function OperatorListClosure() {
-  const CHUNK_SIZE = 1000;
-  const CHUNK_SIZE_ABOUT = CHUNK_SIZE - 5;
+class OperatorList {
+  static get CHUNK_SIZE() {
+    return (0, _util.shadow)(this, "CHUNK_SIZE", 1000);
+  }
+
+  static get CHUNK_SIZE_ABOUT() {
+    return (0, _util.shadow)(this, "CHUNK_SIZE_ABOUT", OperatorList.CHUNK_SIZE - 5);
+  }
 
-  function OperatorList(intent, streamSink) {
+  constructor(intent, streamSink) {
     this._streamSink = streamSink;
     this.fnArray = [];
     this.argsArray = [];
@@ -561,118 +557,115 @@ const OperatorList = function OperatorListClosure() {
     this._resolved = streamSink ? null : Promise.resolve();
   }
 
-  OperatorList.prototype = {
-    get length() {
-      return this.argsArray.length;
-    },
+  get length() {
+    return this.argsArray.length;
+  }
 
-    get ready() {
-      return this._resolved || this._streamSink.ready;
-    },
+  get ready() {
+    return this._resolved || this._streamSink.ready;
+  }
 
-    get totalLength() {
-      return this._totalLength + this.length;
-    },
+  get totalLength() {
+    return this._totalLength + this.length;
+  }
 
-    addOp(fn, args) {
-      this.optimizer.push(fn, args);
-      this.weight++;
+  addOp(fn, args) {
+    this.optimizer.push(fn, args);
+    this.weight++;
 
-      if (this._streamSink) {
-        if (this.weight >= CHUNK_SIZE) {
-          this.flush();
-        } else if (this.weight >= CHUNK_SIZE_ABOUT && (fn === _util.OPS.restore || fn === _util.OPS.endText)) {
-          this.flush();
-        }
+    if (this._streamSink) {
+      if (this.weight >= OperatorList.CHUNK_SIZE) {
+        this.flush();
+      } else if (this.weight >= OperatorList.CHUNK_SIZE_ABOUT && (fn === _util.OPS.restore || fn === _util.OPS.endText)) {
+        this.flush();
       }
-    },
+    }
+  }
 
-    addDependency(dependency) {
-      if (this.dependencies.has(dependency)) {
-        return;
-      }
+  addDependency(dependency) {
+    if (this.dependencies.has(dependency)) {
+      return;
+    }
+
+    this.dependencies.add(dependency);
+    this.addOp(_util.OPS.dependency, [dependency]);
+  }
+
+  addDependencies(dependencies) {
+    for (const dependency of dependencies) {
+      this.addDependency(dependency);
+    }
+  }
+
+  addOpList(opList) {
+    if (!(opList instanceof OperatorList)) {
+      (0, _util.warn)('addOpList - ignoring invalid "opList" parameter.');
+      return;
+    }
 
+    for (const dependency of opList.dependencies) {
       this.dependencies.add(dependency);
-      this.addOp(_util.OPS.dependency, [dependency]);
-    },
+    }
 
-    addDependencies(dependencies) {
-      for (const dependency of dependencies) {
-        this.addDependency(dependency);
-      }
-    },
+    for (let i = 0, ii = opList.length; i < ii; i++) {
+      this.addOp(opList.fnArray[i], opList.argsArray[i]);
+    }
+  }
 
-    addOpList(opList) {
-      if (!(opList instanceof OperatorList)) {
-        (0, _util.warn)('addOpList - ignoring invalid "opList" parameter.');
-        return;
-      }
+  getIR() {
+    return {
+      fnArray: this.fnArray,
+      argsArray: this.argsArray,
+      length: this.length
+    };
+  }
 
-      for (const dependency of opList.dependencies) {
-        this.dependencies.add(dependency);
-      }
+  get _transfers() {
+    const transfers = [];
+    const {
+      fnArray,
+      argsArray,
+      length
+    } = this;
+
+    for (let i = 0; i < length; i++) {
+      switch (fnArray[i]) {
+        case _util.OPS.paintInlineImageXObject:
+        case _util.OPS.paintInlineImageXObjectGroup:
+        case _util.OPS.paintImageMaskXObject:
+          const arg = argsArray[i][0];
+          ;
+
+          if (!arg.cached) {
+            transfers.push(arg.data.buffer);
+          }
 
-      for (let i = 0, ii = opList.length; i < ii; i++) {
-        this.addOp(opList.fnArray[i], opList.argsArray[i]);
-      }
-    },
-
-    getIR() {
-      return {
-        fnArray: this.fnArray,
-        argsArray: this.argsArray,
-        length: this.length
-      };
-    },
-
-    get _transfers() {
-      const transfers = [];
-      const {
-        fnArray,
-        argsArray,
-        length
-      } = this;
-
-      for (let i = 0; i < length; i++) {
-        switch (fnArray[i]) {
-          case _util.OPS.paintInlineImageXObject:
-          case _util.OPS.paintInlineImageXObjectGroup:
-          case _util.OPS.paintImageMaskXObject:
-            const arg = argsArray[i][0];
-            ;
-
-            if (!arg.cached) {
-              transfers.push(arg.data.buffer);
-            }
-
-            break;
-        }
+          break;
       }
-
-      return transfers;
-    },
-
-    flush(lastChunk = false) {
-      this.optimizer.flush();
-      const length = this.length;
-      this._totalLength += length;
-
-      this._streamSink.enqueue({
-        fnArray: this.fnArray,
-        argsArray: this.argsArray,
-        lastChunk,
-        length
-      }, 1, this._transfers);
-
-      this.dependencies.clear();
-      this.fnArray.length = 0;
-      this.argsArray.length = 0;
-      this.weight = 0;
-      this.optimizer.reset();
     }
 
-  };
-  return OperatorList;
-}();
+    return transfers;
+  }
+
+  flush(lastChunk = false) {
+    this.optimizer.flush();
+    const length = this.length;
+    this._totalLength += length;
+
+    this._streamSink.enqueue({
+      fnArray: this.fnArray,
+      argsArray: this.argsArray,
+      lastChunk,
+      length
+    }, 1, this._transfers);
+
+    this.dependencies.clear();
+    this.fnArray.length = 0;
+    this.argsArray.length = 0;
+    this.weight = 0;
+    this.optimizer.reset();
+  }
+
+}
 
 exports.OperatorList = OperatorList;

+ 25 - 13
lib/core/parser.js

@@ -26,22 +26,34 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.Parser = exports.Linearization = exports.Lexer = void 0;
 
-var _stream = require("./stream.js");
-
 var _util = require("../shared/util.js");
 
 var _primitives = require("./primitives.js");
 
 var _core_utils = require("./core_utils.js");
 
+var _ascii_85_stream = require("./ascii_85_stream.js");
+
+var _ascii_hex_stream = require("./ascii_hex_stream.js");
+
 var _ccitt_stream = require("./ccitt_stream.js");
 
+var _flate_stream = require("./flate_stream.js");
+
 var _jbig2_stream = require("./jbig2_stream.js");
 
 var _jpeg_stream = require("./jpeg_stream.js");
 
 var _jpx_stream = require("./jpx_stream.js");
 
+var _lzw_stream = require("./lzw_stream.js");
+
+var _stream = require("./stream.js");
+
+var _predictor_stream = require("./predictor_stream.js");
+
+var _run_length_stream = require("./run_length_stream.js");
+
 const MAX_LENGTH_TO_CACHE = 1000;
 const MAX_ADLER32_LENGTH = 5552;
 
@@ -592,7 +604,7 @@ class Parser {
     if (this.tryShift() && (0, _primitives.isCmd)(this.buf2, "endstream")) {
       this.shift();
     } else {
-      const ENDSTREAM_SIGNATURE = new Uint8Array([0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D]);
+      const ENDSTREAM_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d]);
 
       let actualLength = this._findStreamLength(startPos, ENDSTREAM_SIGNATURE);
 
@@ -693,10 +705,10 @@ class Parser {
         xrefStreamStats[_util.StreamType.FLATE] = true;
 
         if (params) {
-          return new _stream.PredictorStream(new _stream.FlateStream(stream, maybeLength), maybeLength, params);
+          return new _predictor_stream.PredictorStream(new _flate_stream.FlateStream(stream, maybeLength), maybeLength, params);
         }
 
-        return new _stream.FlateStream(stream, maybeLength);
+        return new _flate_stream.FlateStream(stream, maybeLength);
       }
 
       if (name === "LZWDecode" || name === "LZW") {
@@ -708,30 +720,30 @@ class Parser {
             earlyChange = params.get("EarlyChange");
           }
 
-          return new _stream.PredictorStream(new _stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
+          return new _predictor_stream.PredictorStream(new _lzw_stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
         }
 
-        return new _stream.LZWStream(stream, maybeLength, earlyChange);
+        return new _lzw_stream.LZWStream(stream, maybeLength, earlyChange);
       }
 
       if (name === "DCTDecode" || name === "DCT") {
         xrefStreamStats[_util.StreamType.DCT] = true;
-        return new _jpeg_stream.JpegStream(stream, maybeLength, stream.dict, params);
+        return new _jpeg_stream.JpegStream(stream, maybeLength, params);
       }
 
       if (name === "JPXDecode" || name === "JPX") {
         xrefStreamStats[_util.StreamType.JPX] = true;
-        return new _jpx_stream.JpxStream(stream, maybeLength, stream.dict, params);
+        return new _jpx_stream.JpxStream(stream, maybeLength, params);
       }
 
       if (name === "ASCII85Decode" || name === "A85") {
         xrefStreamStats[_util.StreamType.A85] = true;
-        return new _stream.Ascii85Stream(stream, maybeLength);
+        return new _ascii_85_stream.Ascii85Stream(stream, maybeLength);
       }
 
       if (name === "ASCIIHexDecode" || name === "AHx") {
         xrefStreamStats[_util.StreamType.AHX] = true;
-        return new _stream.AsciiHexStream(stream, maybeLength);
+        return new _ascii_hex_stream.AsciiHexStream(stream, maybeLength);
       }
 
       if (name === "CCITTFaxDecode" || name === "CCF") {
@@ -741,12 +753,12 @@ class Parser {
 
       if (name === "RunLengthDecode" || name === "RL") {
         xrefStreamStats[_util.StreamType.RLX] = true;
-        return new _stream.RunLengthStream(stream, maybeLength);
+        return new _run_length_stream.RunLengthStream(stream, maybeLength);
       }
 
       if (name === "JBIG2Decode") {
         xrefStreamStats[_util.StreamType.JBIG] = true;
-        return new _jbig2_stream.Jbig2Stream(stream, maybeLength, stream.dict, params);
+        return new _jbig2_stream.Jbig2Stream(stream, maybeLength, params);
       }
 
       (0, _util.warn)(`Filter "${name}" is not supported.`);

+ 372 - 371
lib/core/pattern.js

@@ -45,18 +45,12 @@ const ShadingType = {
   TENSOR_PATCH_MESH: 7
 };
 
-const Pattern = function PatternClosure() {
-  function Pattern() {
-    (0, _util.unreachable)("should not call Pattern constructor");
+class Pattern {
+  constructor() {
+    (0, _util.unreachable)("Cannot initialize Pattern.");
   }
 
-  Pattern.prototype = {
-    getPattern: function Pattern_getPattern(ctx) {
-      (0, _util.unreachable)(`Should not call Pattern.getStyle: ${ctx}`);
-    }
-  };
-
-  Pattern.parseShading = function (shading, matrix, xref, res, handler, pdfFunctionFactory, localColorSpaceCache) {
+  static parseShading(shading, matrix, xref, res, handler, pdfFunctionFactory, localColorSpaceCache) {
     const dict = (0, _primitives.isStream)(shading) ? shading.dict : shading;
     const type = dict.get("ShadingType");
 
@@ -64,13 +58,13 @@ const Pattern = function PatternClosure() {
       switch (type) {
         case ShadingType.AXIAL:
         case ShadingType.RADIAL:
-          return new Shadings.RadialAxial(dict, matrix, xref, res, pdfFunctionFactory, localColorSpaceCache);
+          return new RadialAxialShading(dict, matrix, xref, res, pdfFunctionFactory, localColorSpaceCache);
 
         case ShadingType.FREE_FORM_MESH:
         case ShadingType.LATTICE_FORM_MESH:
         case ShadingType.COONS_PATCH_MESH:
         case ShadingType.TENSOR_PATCH_MESH:
-          return new Shadings.Mesh(shading, matrix, xref, res, pdfFunctionFactory, localColorSpaceCache);
+          return new MeshShading(shading, matrix, xref, res, pdfFunctionFactory, localColorSpaceCache);
 
         default:
           throw new _util.FormatError("Unsupported ShadingType: " + type);
@@ -84,23 +78,37 @@ const Pattern = function PatternClosure() {
         featureId: _util.UNSUPPORTED_FEATURES.shadingPattern
       });
       (0, _util.warn)(ex);
-      return new Shadings.Dummy();
+      return new DummyShading();
     }
-  };
+  }
 
-  return Pattern;
-}();
+}
 
 exports.Pattern = Pattern;
-const Shadings = {};
-Shadings.SMALL_NUMBER = 1e-6;
 
-Shadings.RadialAxial = function RadialAxialClosure() {
-  function RadialAxial(dict, matrix, xref, resources, pdfFunctionFactory, localColorSpaceCache) {
+class BaseShading {
+  static get SMALL_NUMBER() {
+    return (0, _util.shadow)(this, "SMALL_NUMBER", 1e-6);
+  }
+
+  constructor() {
+    if (this.constructor === BaseShading) {
+      (0, _util.unreachable)("Cannot initialize BaseShading.");
+    }
+  }
+
+  getIR() {
+    (0, _util.unreachable)("Abstract method `getIR` called.");
+  }
+
+}
+
+class RadialAxialShading extends BaseShading {
+  constructor(dict, matrix, xref, resources, pdfFunctionFactory, localColorSpaceCache) {
+    super();
     this.matrix = matrix;
     this.coordsArr = dict.getArray("Coords");
     this.shadingType = dict.get("ShadingType");
-    this.type = "Pattern";
 
     const cs = _colorspace.ColorSpace.parse({
       cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
@@ -110,7 +118,6 @@ Shadings.RadialAxial = function RadialAxialClosure() {
       localColorSpaceCache
     });
 
-    this.cs = cs;
     const bbox = dict.getArray("BBox");
 
     if (Array.isArray(bbox) && bbox.length === 4) {
@@ -182,61 +189,45 @@ Shadings.RadialAxial = function RadialAxialClosure() {
 
     if (!extendStart) {
       colorStops.unshift([0, background]);
-      colorStops[1][0] += Shadings.SMALL_NUMBER;
+      colorStops[1][0] += BaseShading.SMALL_NUMBER;
     }
 
     if (!extendEnd) {
-      colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
+      colorStops[colorStops.length - 1][0] -= BaseShading.SMALL_NUMBER;
       colorStops.push([1, background]);
     }
 
     this.colorStops = colorStops;
   }
 
-  RadialAxial.prototype = {
-    getIR: function RadialAxial_getIR() {
-      const coordsArr = this.coordsArr;
-      const shadingType = this.shadingType;
-      let type, p0, p1, r0, r1;
-
-      if (shadingType === ShadingType.AXIAL) {
-        p0 = [coordsArr[0], coordsArr[1]];
-        p1 = [coordsArr[2], coordsArr[3]];
-        r0 = null;
-        r1 = null;
-        type = "axial";
-      } else if (shadingType === ShadingType.RADIAL) {
-        p0 = [coordsArr[0], coordsArr[1]];
-        p1 = [coordsArr[3], coordsArr[4]];
-        r0 = coordsArr[2];
-        r1 = coordsArr[5];
-        type = "radial";
-      } else {
-        (0, _util.unreachable)(`getPattern type unknown: ${shadingType}`);
-      }
-
-      const matrix = this.matrix;
-
-      if (matrix) {
-        p0 = _util.Util.applyTransform(p0, matrix);
-        p1 = _util.Util.applyTransform(p1, matrix);
-
-        if (shadingType === ShadingType.RADIAL) {
-          const scale = _util.Util.singularValueDecompose2dScale(matrix);
+  getIR() {
+    const coordsArr = this.coordsArr;
+    const shadingType = this.shadingType;
+    let type, p0, p1, r0, r1;
+
+    if (shadingType === ShadingType.AXIAL) {
+      p0 = [coordsArr[0], coordsArr[1]];
+      p1 = [coordsArr[2], coordsArr[3]];
+      r0 = null;
+      r1 = null;
+      type = "axial";
+    } else if (shadingType === ShadingType.RADIAL) {
+      p0 = [coordsArr[0], coordsArr[1]];
+      p1 = [coordsArr[3], coordsArr[4]];
+      r0 = coordsArr[2];
+      r1 = coordsArr[5];
+      type = "radial";
+    } else {
+      (0, _util.unreachable)(`getPattern type unknown: ${shadingType}`);
+    }
 
-          r0 *= scale[0];
-          r1 *= scale[1];
-        }
-      }
+    return ["RadialAxial", type, this.bbox, this.colorStops, p0, p1, r0, r1, this.matrix];
+  }
 
-      return ["RadialAxial", type, this.bbox, this.colorStops, p0, p1, r0, r1];
-    }
-  };
-  return RadialAxial;
-}();
+}
 
-Shadings.Mesh = function MeshClosure() {
-  function MeshStreamReader(stream, context) {
+class MeshStreamReader {
+  constructor(stream, context) {
     this.stream = stream;
     this.context = context;
     this.buffer = 0;
@@ -247,96 +238,227 @@ Shadings.Mesh = function MeshClosure() {
     this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) : this.tmpCompsBuf;
   }
 
-  MeshStreamReader.prototype = {
-    get hasData() {
-      if (this.stream.end) {
-        return this.stream.pos < this.stream.end;
-      }
+  get hasData() {
+    if (this.stream.end) {
+      return this.stream.pos < this.stream.end;
+    }
+
+    if (this.bufferLength > 0) {
+      return true;
+    }
+
+    const nextByte = this.stream.getByte();
+
+    if (nextByte < 0) {
+      return false;
+    }
+
+    this.buffer = nextByte;
+    this.bufferLength = 8;
+    return true;
+  }
 
-      if (this.bufferLength > 0) {
-        return true;
+  readBits(n) {
+    let buffer = this.buffer;
+    let bufferLength = this.bufferLength;
+
+    if (n === 32) {
+      if (bufferLength === 0) {
+        return (this.stream.getByte() << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte()) >>> 0;
       }
 
+      buffer = buffer << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte();
       const nextByte = this.stream.getByte();
+      this.buffer = nextByte & (1 << bufferLength) - 1;
+      return (buffer << 8 - bufferLength | (nextByte & 0xff) >> bufferLength) >>> 0;
+    }
 
-      if (nextByte < 0) {
-        return false;
-      }
+    if (n === 8 && bufferLength === 0) {
+      return this.stream.getByte();
+    }
 
-      this.buffer = nextByte;
-      this.bufferLength = 8;
-      return true;
-    },
+    while (bufferLength < n) {
+      buffer = buffer << 8 | this.stream.getByte();
+      bufferLength += 8;
+    }
 
-    readBits: function MeshStreamReader_readBits(n) {
-      let buffer = this.buffer;
-      let bufferLength = this.bufferLength;
+    bufferLength -= n;
+    this.bufferLength = bufferLength;
+    this.buffer = buffer & (1 << bufferLength) - 1;
+    return buffer >> bufferLength;
+  }
 
-      if (n === 32) {
-        if (bufferLength === 0) {
-          return (this.stream.getByte() << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte()) >>> 0;
-        }
+  align() {
+    this.buffer = 0;
+    this.bufferLength = 0;
+  }
 
-        buffer = buffer << 24 | this.stream.getByte() << 16 | this.stream.getByte() << 8 | this.stream.getByte();
-        const nextByte = this.stream.getByte();
-        this.buffer = nextByte & (1 << bufferLength) - 1;
-        return (buffer << 8 - bufferLength | (nextByte & 0xff) >> bufferLength) >>> 0;
-      }
+  readFlag() {
+    return this.readBits(this.context.bitsPerFlag);
+  }
 
-      if (n === 8 && bufferLength === 0) {
-        return this.stream.getByte();
-      }
+  readCoordinate() {
+    const bitsPerCoordinate = this.context.bitsPerCoordinate;
+    const xi = this.readBits(bitsPerCoordinate);
+    const yi = this.readBits(bitsPerCoordinate);
+    const decode = this.context.decode;
+    const scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) : 2.3283064365386963e-10;
+    return [xi * scale * (decode[1] - decode[0]) + decode[0], yi * scale * (decode[3] - decode[2]) + decode[2]];
+  }
 
-      while (bufferLength < n) {
-        buffer = buffer << 8 | this.stream.getByte();
-        bufferLength += 8;
-      }
+  readComponents() {
+    const numComps = this.context.numComps;
+    const bitsPerComponent = this.context.bitsPerComponent;
+    const scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) : 2.3283064365386963e-10;
+    const decode = this.context.decode;
+    const components = this.tmpCompsBuf;
 
-      bufferLength -= n;
-      this.bufferLength = bufferLength;
-      this.buffer = buffer & (1 << bufferLength) - 1;
-      return buffer >> bufferLength;
-    },
-    align: function MeshStreamReader_align() {
-      this.buffer = 0;
-      this.bufferLength = 0;
-    },
-    readFlag: function MeshStreamReader_readFlag() {
-      return this.readBits(this.context.bitsPerFlag);
-    },
-    readCoordinate: function MeshStreamReader_readCoordinate() {
-      const bitsPerCoordinate = this.context.bitsPerCoordinate;
-      const xi = this.readBits(bitsPerCoordinate);
-      const yi = this.readBits(bitsPerCoordinate);
-      const decode = this.context.decode;
-      const scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) : 2.3283064365386963e-10;
-      return [xi * scale * (decode[1] - decode[0]) + decode[0], yi * scale * (decode[3] - decode[2]) + decode[2]];
-    },
-    readComponents: function MeshStreamReader_readComponents() {
-      const numComps = this.context.numComps;
-      const bitsPerComponent = this.context.bitsPerComponent;
-      const scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) : 2.3283064365386963e-10;
-      const decode = this.context.decode;
-      const components = this.tmpCompsBuf;
-
-      for (let i = 0, j = 4; i < numComps; i++, j += 2) {
-        const ci = this.readBits(bitsPerComponent);
-        components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
-      }
+    for (let i = 0, j = 4; i < numComps; i++, j += 2) {
+      const ci = this.readBits(bitsPerComponent);
+      components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
+    }
 
-      const color = this.tmpCsCompsBuf;
+    const color = this.tmpCsCompsBuf;
 
-      if (this.context.colorFn) {
-        this.context.colorFn(components, 0, color, 0);
-      }
+    if (this.context.colorFn) {
+      this.context.colorFn(components, 0, color, 0);
+    }
+
+    return this.context.colorSpace.getRgb(color, 0);
+  }
+
+}
+
+const getB = function getBClosure() {
+  function buildB(count) {
+    const lut = [];
 
-      return this.context.colorSpace.getRgb(color, 0);
+    for (let i = 0; i <= count; i++) {
+      const t = i / count,
+            t_ = 1 - t;
+      lut.push(new Float32Array([t_ * t_ * t_, 3 * t * t_ * t_, 3 * t * t * t_, t * t * t]));
     }
+
+    return lut;
+  }
+
+  const cache = [];
+  return function (count) {
+    if (!cache[count]) {
+      cache[count] = buildB(count);
+    }
+
+    return cache[count];
   };
+}();
+
+class MeshShading extends BaseShading {
+  static get MIN_SPLIT_PATCH_CHUNKS_AMOUNT() {
+    return (0, _util.shadow)(this, "MIN_SPLIT_PATCH_CHUNKS_AMOUNT", 3);
+  }
+
+  static get MAX_SPLIT_PATCH_CHUNKS_AMOUNT() {
+    return (0, _util.shadow)(this, "MAX_SPLIT_PATCH_CHUNKS_AMOUNT", 20);
+  }
+
+  static get TRIANGLE_DENSITY() {
+    return (0, _util.shadow)(this, "TRIANGLE_DENSITY", 20);
+  }
+
+  constructor(stream, matrix, xref, resources, pdfFunctionFactory, localColorSpaceCache) {
+    super();
+
+    if (!(0, _primitives.isStream)(stream)) {
+      throw new _util.FormatError("Mesh data is not a stream");
+    }
+
+    const dict = stream.dict;
+    this.matrix = matrix;
+    this.shadingType = dict.get("ShadingType");
+    const bbox = dict.getArray("BBox");
+
+    if (Array.isArray(bbox) && bbox.length === 4) {
+      this.bbox = _util.Util.normalizeRect(bbox);
+    } else {
+      this.bbox = null;
+    }
+
+    const cs = _colorspace.ColorSpace.parse({
+      cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
+      xref,
+      resources,
+      pdfFunctionFactory,
+      localColorSpaceCache
+    });
 
-  function decodeType4Shading(mesh, reader) {
-    const coords = mesh.coords;
-    const colors = mesh.colors;
+    this.background = dict.has("Background") ? cs.getRgb(dict.get("Background"), 0) : null;
+    const fnObj = dict.getRaw("Function");
+    const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
+    this.coords = [];
+    this.colors = [];
+    this.figures = [];
+    const decodeContext = {
+      bitsPerCoordinate: dict.get("BitsPerCoordinate"),
+      bitsPerComponent: dict.get("BitsPerComponent"),
+      bitsPerFlag: dict.get("BitsPerFlag"),
+      decode: dict.getArray("Decode"),
+      colorFn: fn,
+      colorSpace: cs,
+      numComps: fn ? 1 : cs.numComps
+    };
+    const reader = new MeshStreamReader(stream, decodeContext);
+    let patchMesh = false;
+
+    switch (this.shadingType) {
+      case ShadingType.FREE_FORM_MESH:
+        this._decodeType4Shading(reader);
+
+        break;
+
+      case ShadingType.LATTICE_FORM_MESH:
+        const verticesPerRow = dict.get("VerticesPerRow") | 0;
+
+        if (verticesPerRow < 2) {
+          throw new _util.FormatError("Invalid VerticesPerRow");
+        }
+
+        this._decodeType5Shading(reader, verticesPerRow);
+
+        break;
+
+      case ShadingType.COONS_PATCH_MESH:
+        this._decodeType6Shading(reader);
+
+        patchMesh = true;
+        break;
+
+      case ShadingType.TENSOR_PATCH_MESH:
+        this._decodeType7Shading(reader);
+
+        patchMesh = true;
+        break;
+
+      default:
+        (0, _util.unreachable)("Unsupported mesh type.");
+        break;
+    }
+
+    if (patchMesh) {
+      this._updateBounds();
+
+      for (let i = 0, ii = this.figures.length; i < ii; i++) {
+        this._buildFigureFromPatch(i);
+      }
+    }
+
+    this._updateBounds();
+
+    this._packData();
+  }
+
+  _decodeType4Shading(reader) {
+    const coords = this.coords;
+    const colors = this.colors;
     const operators = [];
     const ps = [];
     let verticesLeft = 0;
@@ -377,16 +499,16 @@ Shadings.Mesh = function MeshClosure() {
       reader.align();
     }
 
-    mesh.figures.push({
+    this.figures.push({
       type: "triangles",
       coords: new Int32Array(ps),
       colors: new Int32Array(ps)
     });
   }
 
-  function decodeType5Shading(mesh, reader, verticesPerRow) {
-    const coords = mesh.coords;
-    const colors = mesh.colors;
+  _decodeType5Shading(reader, verticesPerRow) {
+    const coords = this.coords;
+    const colors = this.colors;
     const ps = [];
 
     while (reader.hasData) {
@@ -397,7 +519,7 @@ Shadings.Mesh = function MeshClosure() {
       colors.push(color);
     }
 
-    mesh.figures.push({
+    this.figures.push({
       type: "lattice",
       coords: new Int32Array(ps),
       colors: new Int32Array(ps),
@@ -405,116 +527,9 @@ Shadings.Mesh = function MeshClosure() {
     });
   }
 
-  const MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
-  const MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
-  const TRIANGLE_DENSITY = 20;
-
-  const getB = function getBClosure() {
-    function buildB(count) {
-      const lut = [];
-
-      for (let i = 0; i <= count; i++) {
-        const t = i / count,
-              t_ = 1 - t;
-        lut.push(new Float32Array([t_ * t_ * t_, 3 * t * t_ * t_, 3 * t * t * t_, t * t * t]));
-      }
-
-      return lut;
-    }
-
-    const cache = [];
-    return function getB(count) {
-      if (!cache[count]) {
-        cache[count] = buildB(count);
-      }
-
-      return cache[count];
-    };
-  }();
-
-  function buildFigureFromPatch(mesh, index) {
-    const figure = mesh.figures[index];
-    (0, _util.assert)(figure.type === "patch", "Unexpected patch mesh figure");
-    const coords = mesh.coords,
-          colors = mesh.colors;
-    const pi = figure.coords;
-    const ci = figure.colors;
-    const figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
-    const figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
-    const figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
-    const figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
-    let splitXBy = Math.ceil((figureMaxX - figureMinX) * TRIANGLE_DENSITY / (mesh.bounds[2] - mesh.bounds[0]));
-    splitXBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));
-    let splitYBy = Math.ceil((figureMaxY - figureMinY) * TRIANGLE_DENSITY / (mesh.bounds[3] - mesh.bounds[1]));
-    splitYBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));
-    const verticesPerRow = splitXBy + 1;
-    const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
-    const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
-    let k = 0;
-    const cl = new Uint8Array(3),
-          cr = new Uint8Array(3);
-    const c0 = colors[ci[0]],
-          c1 = colors[ci[1]],
-          c2 = colors[ci[2]],
-          c3 = colors[ci[3]];
-    const bRow = getB(splitYBy),
-          bCol = getB(splitXBy);
-
-    for (let row = 0; row <= splitYBy; row++) {
-      cl[0] = (c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy | 0;
-      cl[1] = (c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy | 0;
-      cl[2] = (c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy | 0;
-      cr[0] = (c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy | 0;
-      cr[1] = (c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy | 0;
-      cr[2] = (c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy | 0;
-
-      for (let col = 0; col <= splitXBy; col++, k++) {
-        if ((row === 0 || row === splitYBy) && (col === 0 || col === splitXBy)) {
-          continue;
-        }
-
-        let x = 0,
-            y = 0;
-        let q = 0;
-
-        for (let i = 0; i <= 3; i++) {
-          for (let j = 0; j <= 3; j++, q++) {
-            const m = bRow[row][i] * bCol[col][j];
-            x += coords[pi[q]][0] * m;
-            y += coords[pi[q]][1] * m;
-          }
-        }
-
-        figureCoords[k] = coords.length;
-        coords.push([x, y]);
-        figureColors[k] = colors.length;
-        const newColor = new Uint8Array(3);
-        newColor[0] = (cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy | 0;
-        newColor[1] = (cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy | 0;
-        newColor[2] = (cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy | 0;
-        colors.push(newColor);
-      }
-    }
-
-    figureCoords[0] = pi[0];
-    figureColors[0] = ci[0];
-    figureCoords[splitXBy] = pi[3];
-    figureColors[splitXBy] = ci[1];
-    figureCoords[verticesPerRow * splitYBy] = pi[12];
-    figureColors[verticesPerRow * splitYBy] = ci[2];
-    figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
-    figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
-    mesh.figures[index] = {
-      type: "lattice",
-      coords: figureCoords,
-      colors: figureColors,
-      verticesPerRow
-    };
-  }
-
-  function decodeType6Shading(mesh, reader) {
-    const coords = mesh.coords;
-    const colors = mesh.colors;
+  _decodeType6Shading(reader) {
+    const coords = this.coords;
+    const colors = this.colors;
     const ps = new Int32Array(16);
     const cs = new Int32Array(4);
 
@@ -634,7 +649,7 @@ Shadings.Mesh = function MeshClosure() {
       coords.push([(-4 * coords[ps[12]][0] - coords[ps[3]][0] + 6 * (coords[ps[8]][0] + coords[ps[13]][0]) - 2 * (coords[ps[0]][0] + coords[ps[15]][0]) + 3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9, (-4 * coords[ps[12]][1] - coords[ps[3]][1] + 6 * (coords[ps[8]][1] + coords[ps[13]][1]) - 2 * (coords[ps[0]][1] + coords[ps[15]][1]) + 3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9]);
       ps[10] = coords.length;
       coords.push([(-4 * coords[ps[15]][0] - coords[ps[0]][0] + 6 * (coords[ps[11]][0] + coords[ps[14]][0]) - 2 * (coords[ps[12]][0] + coords[ps[3]][0]) + 3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9, (-4 * coords[ps[15]][1] - coords[ps[0]][1] + 6 * (coords[ps[11]][1] + coords[ps[14]][1]) - 2 * (coords[ps[12]][1] + coords[ps[3]][1]) + 3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9]);
-      mesh.figures.push({
+      this.figures.push({
         type: "patch",
         coords: new Int32Array(ps),
         colors: new Int32Array(cs)
@@ -642,9 +657,9 @@ Shadings.Mesh = function MeshClosure() {
     }
   }
 
-  function decodeType7Shading(mesh, reader) {
-    const coords = mesh.coords;
-    const colors = mesh.colors;
+  _decodeType7Shading(reader) {
+    const coords = this.coords;
+    const colors = this.colors;
     const ps = new Int32Array(16);
     const cs = new Int32Array(4);
 
@@ -772,7 +787,7 @@ Shadings.Mesh = function MeshClosure() {
           break;
       }
 
-      mesh.figures.push({
+      this.figures.push({
         type: "patch",
         coords: new Int32Array(ps),
         colors: new Int32Array(cs)
@@ -780,27 +795,107 @@ Shadings.Mesh = function MeshClosure() {
     }
   }
 
-  function updateBounds(mesh) {
-    let minX = mesh.coords[0][0],
-        minY = mesh.coords[0][1],
+  _buildFigureFromPatch(index) {
+    const figure = this.figures[index];
+    (0, _util.assert)(figure.type === "patch", "Unexpected patch mesh figure");
+    const coords = this.coords,
+          colors = this.colors;
+    const pi = figure.coords;
+    const ci = figure.colors;
+    const figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
+    const figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
+    const figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0], coords[pi[12]][0], coords[pi[15]][0]);
+    const figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1], coords[pi[12]][1], coords[pi[15]][1]);
+    let splitXBy = Math.ceil((figureMaxX - figureMinX) * MeshShading.TRIANGLE_DENSITY / (this.bounds[2] - this.bounds[0]));
+    splitXBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));
+    let splitYBy = Math.ceil((figureMaxY - figureMinY) * MeshShading.TRIANGLE_DENSITY / (this.bounds[3] - this.bounds[1]));
+    splitYBy = Math.max(MeshShading.MIN_SPLIT_PATCH_CHUNKS_AMOUNT, Math.min(MeshShading.MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));
+    const verticesPerRow = splitXBy + 1;
+    const figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
+    const figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
+    let k = 0;
+    const cl = new Uint8Array(3),
+          cr = new Uint8Array(3);
+    const c0 = colors[ci[0]],
+          c1 = colors[ci[1]],
+          c2 = colors[ci[2]],
+          c3 = colors[ci[3]];
+    const bRow = getB(splitYBy),
+          bCol = getB(splitXBy);
+
+    for (let row = 0; row <= splitYBy; row++) {
+      cl[0] = (c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy | 0;
+      cl[1] = (c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy | 0;
+      cl[2] = (c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy | 0;
+      cr[0] = (c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy | 0;
+      cr[1] = (c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy | 0;
+      cr[2] = (c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy | 0;
+
+      for (let col = 0; col <= splitXBy; col++, k++) {
+        if ((row === 0 || row === splitYBy) && (col === 0 || col === splitXBy)) {
+          continue;
+        }
+
+        let x = 0,
+            y = 0;
+        let q = 0;
+
+        for (let i = 0; i <= 3; i++) {
+          for (let j = 0; j <= 3; j++, q++) {
+            const m = bRow[row][i] * bCol[col][j];
+            x += coords[pi[q]][0] * m;
+            y += coords[pi[q]][1] * m;
+          }
+        }
+
+        figureCoords[k] = coords.length;
+        coords.push([x, y]);
+        figureColors[k] = colors.length;
+        const newColor = new Uint8Array(3);
+        newColor[0] = (cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy | 0;
+        newColor[1] = (cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy | 0;
+        newColor[2] = (cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy | 0;
+        colors.push(newColor);
+      }
+    }
+
+    figureCoords[0] = pi[0];
+    figureColors[0] = ci[0];
+    figureCoords[splitXBy] = pi[3];
+    figureColors[splitXBy] = ci[1];
+    figureCoords[verticesPerRow * splitYBy] = pi[12];
+    figureColors[verticesPerRow * splitYBy] = ci[2];
+    figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
+    figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
+    this.figures[index] = {
+      type: "lattice",
+      coords: figureCoords,
+      colors: figureColors,
+      verticesPerRow
+    };
+  }
+
+  _updateBounds() {
+    let minX = this.coords[0][0],
+        minY = this.coords[0][1],
         maxX = minX,
         maxY = minY;
 
-    for (let i = 1, ii = mesh.coords.length; i < ii; i++) {
-      const x = mesh.coords[i][0],
-            y = mesh.coords[i][1];
+    for (let i = 1, ii = this.coords.length; i < ii; i++) {
+      const x = this.coords[i][0],
+            y = this.coords[i][1];
       minX = minX > x ? x : minX;
       minY = minY > y ? y : minY;
       maxX = maxX < x ? x : maxX;
       maxY = maxY < y ? y : maxY;
     }
 
-    mesh.bounds = [minX, minY, maxX, maxY];
+    this.bounds = [minX, minY, maxX, maxY];
   }
 
-  function packData(mesh) {
+  _packData() {
     let i, ii, j, jj;
-    const coords = mesh.coords;
+    const coords = this.coords;
     const coordsPacked = new Float32Array(coords.length * 2);
 
     for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
@@ -809,8 +904,8 @@ Shadings.Mesh = function MeshClosure() {
       coordsPacked[j++] = xy[1];
     }
 
-    mesh.coords = coordsPacked;
-    const colors = mesh.colors;
+    this.coords = coordsPacked;
+    const colors = this.colors;
     const colorsPacked = new Uint8Array(colors.length * 3);
 
     for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
@@ -820,8 +915,8 @@ Shadings.Mesh = function MeshClosure() {
       colorsPacked[j++] = c[2];
     }
 
-    mesh.colors = colorsPacked;
-    const figures = mesh.figures;
+    this.colors = colorsPacked;
+    const figures = this.figures;
 
     for (i = 0, ii = figures.length; i < ii; i++) {
       const figure = figures[i],
@@ -835,112 +930,18 @@ Shadings.Mesh = function MeshClosure() {
     }
   }
 
-  function Mesh(stream, matrix, xref, resources, pdfFunctionFactory, localColorSpaceCache) {
-    if (!(0, _primitives.isStream)(stream)) {
-      throw new _util.FormatError("Mesh data is not a stream");
-    }
-
-    const dict = stream.dict;
-    this.matrix = matrix;
-    this.shadingType = dict.get("ShadingType");
-    this.type = "Pattern";
-    const bbox = dict.getArray("BBox");
-
-    if (Array.isArray(bbox) && bbox.length === 4) {
-      this.bbox = _util.Util.normalizeRect(bbox);
-    } else {
-      this.bbox = null;
-    }
-
-    const cs = _colorspace.ColorSpace.parse({
-      cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
-      xref,
-      resources,
-      pdfFunctionFactory,
-      localColorSpaceCache
-    });
-
-    this.cs = cs;
-    this.background = dict.has("Background") ? cs.getRgb(dict.get("Background"), 0) : null;
-    const fnObj = dict.getRaw("Function");
-    const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
-    this.coords = [];
-    this.colors = [];
-    this.figures = [];
-    const decodeContext = {
-      bitsPerCoordinate: dict.get("BitsPerCoordinate"),
-      bitsPerComponent: dict.get("BitsPerComponent"),
-      bitsPerFlag: dict.get("BitsPerFlag"),
-      decode: dict.getArray("Decode"),
-      colorFn: fn,
-      colorSpace: cs,
-      numComps: fn ? 1 : cs.numComps
-    };
-    const reader = new MeshStreamReader(stream, decodeContext);
-    let patchMesh = false;
-
-    switch (this.shadingType) {
-      case ShadingType.FREE_FORM_MESH:
-        decodeType4Shading(this, reader);
-        break;
-
-      case ShadingType.LATTICE_FORM_MESH:
-        const verticesPerRow = dict.get("VerticesPerRow") | 0;
-
-        if (verticesPerRow < 2) {
-          throw new _util.FormatError("Invalid VerticesPerRow");
-        }
-
-        decodeType5Shading(this, reader, verticesPerRow);
-        break;
-
-      case ShadingType.COONS_PATCH_MESH:
-        decodeType6Shading(this, reader);
-        patchMesh = true;
-        break;
-
-      case ShadingType.TENSOR_PATCH_MESH:
-        decodeType7Shading(this, reader);
-        patchMesh = true;
-        break;
-
-      default:
-        (0, _util.unreachable)("Unsupported mesh type.");
-        break;
-    }
-
-    if (patchMesh) {
-      updateBounds(this);
-
-      for (let i = 0, ii = this.figures.length; i < ii; i++) {
-        buildFigureFromPatch(this, i);
-      }
-    }
-
-    updateBounds(this);
-    packData(this);
+  getIR() {
+    return ["Mesh", this.shadingType, this.coords, this.colors, this.figures, this.bounds, this.matrix, this.bbox, this.background];
   }
 
-  Mesh.prototype = {
-    getIR: function Mesh_getIR() {
-      return ["Mesh", this.shadingType, this.coords, this.colors, this.figures, this.bounds, this.matrix, this.bbox, this.background];
-    }
-  };
-  return Mesh;
-}();
+}
 
-Shadings.Dummy = function DummyClosure() {
-  function Dummy() {
-    this.type = "Pattern";
+class DummyShading extends BaseShading {
+  getIR() {
+    return ["Dummy"];
   }
 
-  Dummy.prototype = {
-    getIR: function Dummy_getIR() {
-      return ["Dummy"];
-    }
-  };
-  return Dummy;
-}();
+}
 
 function getTilingPatternIR(operatorList, dict, color) {
   const matrix = dict.getArray("Matrix");

+ 4 - 0
lib/core/pdf_manager.js

@@ -93,6 +93,10 @@ class BasePdfManager {
     return this.pdfDocument.fontFallback(id, handler);
   }
 
+  loadXfaFonts(handler, task) {
+    return this.pdfDocument.loadXfaFonts(handler, task);
+  }
+
   cleanup(manuallyTriggered = false) {
     return this.pdfDocument.cleanup(manuallyTriggered);
   }

+ 268 - 0
lib/core/predictor_stream.js

@@ -0,0 +1,268 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.PredictorStream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+var _util = require("../shared/util.js");
+
+var _primitives = require("./primitives.js");
+
+class PredictorStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength, params) {
+    super(maybeLength);
+
+    if (!(0, _primitives.isDict)(params)) {
+      return str;
+    }
+
+    const predictor = this.predictor = params.get("Predictor") || 1;
+
+    if (predictor <= 1) {
+      return str;
+    }
+
+    if (predictor !== 2 && (predictor < 10 || predictor > 15)) {
+      throw new _util.FormatError(`Unsupported predictor: ${predictor}`);
+    }
+
+    if (predictor === 2) {
+      this.readBlock = this.readBlockTiff;
+    } else {
+      this.readBlock = this.readBlockPng;
+    }
+
+    this.str = str;
+    this.dict = str.dict;
+    const colors = this.colors = params.get("Colors") || 1;
+    const bits = this.bits = params.get("BitsPerComponent") || 8;
+    const columns = this.columns = params.get("Columns") || 1;
+    this.pixBytes = colors * bits + 7 >> 3;
+    this.rowBytes = columns * colors * bits + 7 >> 3;
+    return this;
+  }
+
+  readBlockTiff() {
+    const rowBytes = this.rowBytes;
+    const bufferLength = this.bufferLength;
+    const buffer = this.ensureBuffer(bufferLength + rowBytes);
+    const bits = this.bits;
+    const colors = this.colors;
+    const rawBytes = this.str.getBytes(rowBytes);
+    this.eof = !rawBytes.length;
+
+    if (this.eof) {
+      return;
+    }
+
+    let inbuf = 0,
+        outbuf = 0;
+    let inbits = 0,
+        outbits = 0;
+    let pos = bufferLength;
+    let i;
+
+    if (bits === 1 && colors === 1) {
+      for (i = 0; i < rowBytes; ++i) {
+        let c = rawBytes[i] ^ inbuf;
+        c ^= c >> 1;
+        c ^= c >> 2;
+        c ^= c >> 4;
+        inbuf = (c & 1) << 7;
+        buffer[pos++] = c;
+      }
+    } else if (bits === 8) {
+      for (i = 0; i < colors; ++i) {
+        buffer[pos++] = rawBytes[i];
+      }
+
+      for (; i < rowBytes; ++i) {
+        buffer[pos] = buffer[pos - colors] + rawBytes[i];
+        pos++;
+      }
+    } else if (bits === 16) {
+      const bytesPerPixel = colors * 2;
+
+      for (i = 0; i < bytesPerPixel; ++i) {
+        buffer[pos++] = rawBytes[i];
+      }
+
+      for (; i < rowBytes; i += 2) {
+        const sum = ((rawBytes[i] & 0xff) << 8) + (rawBytes[i + 1] & 0xff) + ((buffer[pos - bytesPerPixel] & 0xff) << 8) + (buffer[pos - bytesPerPixel + 1] & 0xff);
+        buffer[pos++] = sum >> 8 & 0xff;
+        buffer[pos++] = sum & 0xff;
+      }
+    } else {
+      const compArray = new Uint8Array(colors + 1);
+      const bitMask = (1 << bits) - 1;
+      let j = 0,
+          k = bufferLength;
+      const columns = this.columns;
+
+      for (i = 0; i < columns; ++i) {
+        for (let kk = 0; kk < colors; ++kk) {
+          if (inbits < bits) {
+            inbuf = inbuf << 8 | rawBytes[j++] & 0xff;
+            inbits += 8;
+          }
+
+          compArray[kk] = compArray[kk] + (inbuf >> inbits - bits) & bitMask;
+          inbits -= bits;
+          outbuf = outbuf << bits | compArray[kk];
+          outbits += bits;
+
+          if (outbits >= 8) {
+            buffer[k++] = outbuf >> outbits - 8 & 0xff;
+            outbits -= 8;
+          }
+        }
+      }
+
+      if (outbits > 0) {
+        buffer[k++] = (outbuf << 8 - outbits) + (inbuf & (1 << 8 - outbits) - 1);
+      }
+    }
+
+    this.bufferLength += rowBytes;
+  }
+
+  readBlockPng() {
+    const rowBytes = this.rowBytes;
+    const pixBytes = this.pixBytes;
+    const predictor = this.str.getByte();
+    const rawBytes = this.str.getBytes(rowBytes);
+    this.eof = !rawBytes.length;
+
+    if (this.eof) {
+      return;
+    }
+
+    const bufferLength = this.bufferLength;
+    const buffer = this.ensureBuffer(bufferLength + rowBytes);
+    let prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
+
+    if (prevRow.length === 0) {
+      prevRow = new Uint8Array(rowBytes);
+    }
+
+    let i,
+        j = bufferLength,
+        up,
+        c;
+
+    switch (predictor) {
+      case 0:
+        for (i = 0; i < rowBytes; ++i) {
+          buffer[j++] = rawBytes[i];
+        }
+
+        break;
+
+      case 1:
+        for (i = 0; i < pixBytes; ++i) {
+          buffer[j++] = rawBytes[i];
+        }
+
+        for (; i < rowBytes; ++i) {
+          buffer[j] = buffer[j - pixBytes] + rawBytes[i] & 0xff;
+          j++;
+        }
+
+        break;
+
+      case 2:
+        for (i = 0; i < rowBytes; ++i) {
+          buffer[j++] = prevRow[i] + rawBytes[i] & 0xff;
+        }
+
+        break;
+
+      case 3:
+        for (i = 0; i < pixBytes; ++i) {
+          buffer[j++] = (prevRow[i] >> 1) + rawBytes[i];
+        }
+
+        for (; i < rowBytes; ++i) {
+          buffer[j] = (prevRow[i] + buffer[j - pixBytes] >> 1) + rawBytes[i] & 0xff;
+          j++;
+        }
+
+        break;
+
+      case 4:
+        for (i = 0; i < pixBytes; ++i) {
+          up = prevRow[i];
+          c = rawBytes[i];
+          buffer[j++] = up + c;
+        }
+
+        for (; i < rowBytes; ++i) {
+          up = prevRow[i];
+          const upLeft = prevRow[i - pixBytes];
+          const left = buffer[j - pixBytes];
+          const p = left + up - upLeft;
+          let pa = p - left;
+
+          if (pa < 0) {
+            pa = -pa;
+          }
+
+          let pb = p - up;
+
+          if (pb < 0) {
+            pb = -pb;
+          }
+
+          let pc = p - upLeft;
+
+          if (pc < 0) {
+            pc = -pc;
+          }
+
+          c = rawBytes[i];
+
+          if (pa <= pb && pa <= pc) {
+            buffer[j++] = left + c;
+          } else if (pb <= pc) {
+            buffer[j++] = up + c;
+          } else {
+            buffer[j++] = upLeft + c;
+          }
+        }
+
+        break;
+
+      default:
+        throw new _util.FormatError(`Unsupported predictor: ${predictor}`);
+    }
+
+    this.bufferLength += rowBytes;
+  }
+
+}
+
+exports.PredictorStream = PredictorStream;

+ 127 - 112
lib/core/primitives.js

@@ -36,26 +36,29 @@ exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF
 
 var _util = require("../shared/util.js");
 
+var _base_stream = require("./base_stream.js");
+
 const EOF = {};
 exports.EOF = EOF;
 
 const Name = function NameClosure() {
   let nameCache = Object.create(null);
 
-  function Name(name) {
-    this.name = name;
-  }
+  class Name {
+    constructor(name) {
+      this.name = name;
+    }
 
-  Name.prototype = {};
+    static get(name) {
+      const nameValue = nameCache[name];
+      return nameValue ? nameValue : nameCache[name] = new Name(name);
+    }
 
-  Name.get = function Name_get(name) {
-    const nameValue = nameCache[name];
-    return nameValue ? nameValue : nameCache[name] = new Name(name);
-  };
+    static _clearCache() {
+      nameCache = Object.create(null);
+    }
 
-  Name._clearCache = function () {
-    nameCache = Object.create(null);
-  };
+  }
 
   return Name;
 }();
@@ -65,32 +68,33 @@ exports.Name = Name;
 const Cmd = function CmdClosure() {
   let cmdCache = Object.create(null);
 
-  function Cmd(cmd) {
-    this.cmd = cmd;
-  }
+  class Cmd {
+    constructor(cmd) {
+      this.cmd = cmd;
+    }
 
-  Cmd.prototype = {};
+    static get(cmd) {
+      const cmdValue = cmdCache[cmd];
+      return cmdValue ? cmdValue : cmdCache[cmd] = new Cmd(cmd);
+    }
 
-  Cmd.get = function Cmd_get(cmd) {
-    const cmdValue = cmdCache[cmd];
-    return cmdValue ? cmdValue : cmdCache[cmd] = new Cmd(cmd);
-  };
+    static _clearCache() {
+      cmdCache = Object.create(null);
+    }
 
-  Cmd._clearCache = function () {
-    cmdCache = Object.create(null);
-  };
+  }
 
   return Cmd;
 }();
 
 exports.Cmd = Cmd;
 
-const Dict = function DictClosure() {
-  const nonSerializable = function nonSerializableClosure() {
-    return nonSerializable;
-  };
+const nonSerializable = function nonSerializableClosure() {
+  return nonSerializable;
+};
 
-  function Dict(xref) {
+class Dict {
+  constructor(xref = null) {
     this._map = Object.create(null);
     this.xref = xref;
     this.objId = null;
@@ -98,104 +102,115 @@ const Dict = function DictClosure() {
     this.__nonSerializable__ = nonSerializable;
   }
 
-  Dict.prototype = {
-    assignXref: function Dict_assignXref(newXref) {
-      this.xref = newXref;
-    },
+  assignXref(newXref) {
+    this.xref = newXref;
+  }
 
-    get size() {
-      return Object.keys(this._map).length;
-    },
+  get size() {
+    return Object.keys(this._map).length;
+  }
 
-    get(key1, key2, key3) {
-      let value = this._map[key1];
+  get(key1, key2, key3) {
+    let value = this._map[key1];
 
-      if (value === undefined && key2 !== undefined) {
-        value = this._map[key2];
+    if (value === undefined && key2 !== undefined) {
+      value = this._map[key2];
 
-        if (value === undefined && key3 !== undefined) {
-          value = this._map[key3];
-        }
+      if (value === undefined && key3 !== undefined) {
+        value = this._map[key3];
       }
+    }
 
-      if (value instanceof Ref && this.xref) {
-        return this.xref.fetch(value, this.suppressEncryption);
-      }
+    if (value instanceof Ref && this.xref) {
+      return this.xref.fetch(value, this.suppressEncryption);
+    }
 
-      return value;
-    },
+    return value;
+  }
 
-    async getAsync(key1, key2, key3) {
-      let value = this._map[key1];
+  async getAsync(key1, key2, key3) {
+    let value = this._map[key1];
 
-      if (value === undefined && key2 !== undefined) {
-        value = this._map[key2];
+    if (value === undefined && key2 !== undefined) {
+      value = this._map[key2];
 
-        if (value === undefined && key3 !== undefined) {
-          value = this._map[key3];
-        }
+      if (value === undefined && key3 !== undefined) {
+        value = this._map[key3];
       }
+    }
 
-      if (value instanceof Ref && this.xref) {
-        return this.xref.fetchAsync(value, this.suppressEncryption);
-      }
+    if (value instanceof Ref && this.xref) {
+      return this.xref.fetchAsync(value, this.suppressEncryption);
+    }
+
+    return value;
+  }
 
-      return value;
-    },
+  getArray(key1, key2, key3) {
+    let value = this._map[key1];
 
-    getArray(key1, key2, key3) {
-      let value = this.get(key1, key2, key3);
+    if (value === undefined && key2 !== undefined) {
+      value = this._map[key2];
 
-      if (!Array.isArray(value) || !this.xref) {
-        return value;
+      if (value === undefined && key3 !== undefined) {
+        value = this._map[key3];
       }
+    }
+
+    if (value instanceof Ref && this.xref) {
+      value = this.xref.fetch(value, this.suppressEncryption);
+    }
 
+    if (Array.isArray(value)) {
       value = value.slice();
 
       for (let i = 0, ii = value.length; i < ii; i++) {
-        if (!(value[i] instanceof Ref)) {
-          continue;
+        if (value[i] instanceof Ref && this.xref) {
+          value[i] = this.xref.fetch(value[i], this.suppressEncryption);
         }
-
-        value[i] = this.xref.fetch(value[i], this.suppressEncryption);
       }
+    }
 
-      return value;
-    },
-
-    getRaw: function Dict_getRaw(key) {
-      return this._map[key];
-    },
-    getKeys: function Dict_getKeys() {
-      return Object.keys(this._map);
-    },
-    getRawValues: function Dict_getRawValues() {
-      return Object.values(this._map);
-    },
-    set: function Dict_set(key, value) {
-      this._map[key] = value;
-    },
-    has: function Dict_has(key) {
-      return this._map[key] !== undefined;
-    },
-    forEach: function Dict_forEach(callback) {
-      for (const key in this._map) {
-        callback(key, this.get(key));
-      }
+    return value;
+  }
+
+  getRaw(key) {
+    return this._map[key];
+  }
+
+  getKeys() {
+    return Object.keys(this._map);
+  }
+
+  getRawValues() {
+    return Object.values(this._map);
+  }
+
+  set(key, value) {
+    this._map[key] = value;
+  }
+
+  has(key) {
+    return this._map[key] !== undefined;
+  }
+
+  forEach(callback) {
+    for (const key in this._map) {
+      callback(key, this.get(key));
     }
-  };
+  }
 
-  Dict.empty = function () {
+  static get empty() {
     const emptyDict = new Dict(null);
 
     emptyDict.set = (key, value) => {
       (0, _util.unreachable)("Should not call `set` on the empty dictionary.");
     };
 
-    return emptyDict;
-  }();
+    return (0, _util.shadow)(this, "empty", emptyDict);
+  }
 
-  Dict.merge = function ({
+  static merge({
     xref,
     dictArray,
     mergeSubDicts = false
@@ -264,40 +279,40 @@ const Dict = function DictClosure() {
 
     properties.clear();
     return mergedDict.size > 0 ? mergedDict : Dict.empty;
-  };
+  }
 
-  return Dict;
-}();
+}
 
 exports.Dict = Dict;
 
 const Ref = function RefClosure() {
   let refCache = Object.create(null);
 
-  function Ref(num, gen) {
-    this.num = num;
-    this.gen = gen;
-  }
+  class Ref {
+    constructor(num, gen) {
+      this.num = num;
+      this.gen = gen;
+    }
 
-  Ref.prototype = {
-    toString: function Ref_toString() {
+    toString() {
       if (this.gen === 0) {
         return `${this.num}R`;
       }
 
       return `${this.num}R${this.gen}`;
     }
-  };
 
-  Ref.get = function (num, gen) {
-    const key = gen === 0 ? `${num}R` : `${num}R${gen}`;
-    const refValue = refCache[key];
-    return refValue ? refValue : refCache[key] = new Ref(num, gen);
-  };
+    static get(num, gen) {
+      const key = gen === 0 ? `${num}R` : `${num}R${gen}`;
+      const refValue = refCache[key];
+      return refValue ? refValue : refCache[key] = new Ref(num, gen);
+    }
 
-  Ref._clearCache = function () {
-    refCache = Object.create(null);
-  };
+    static _clearCache() {
+      refCache = Object.create(null);
+    }
+
+  }
 
   return Ref;
 }();
@@ -399,7 +414,7 @@ function isRefsEqual(v1, v2) {
 }
 
 function isStream(v) {
-  return typeof v === "object" && v !== null && v.getBytes !== undefined;
+  return v instanceof _base_stream.BaseStream;
 }
 
 function clearPrimitiveCaches() {

+ 74 - 0
lib/core/run_length_stream.js

@@ -0,0 +1,74 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.RunLengthStream = void 0;
+
+var _decode_stream = require("./decode_stream.js");
+
+class RunLengthStream extends _decode_stream.DecodeStream {
+  constructor(str, maybeLength) {
+    super(maybeLength);
+    this.str = str;
+    this.dict = str.dict;
+  }
+
+  readBlock() {
+    const repeatHeader = this.str.getBytes(2);
+
+    if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {
+      this.eof = true;
+      return;
+    }
+
+    let buffer;
+    let bufferLength = this.bufferLength;
+    let n = repeatHeader[0];
+
+    if (n < 128) {
+      buffer = this.ensureBuffer(bufferLength + n + 1);
+      buffer[bufferLength++] = repeatHeader[1];
+
+      if (n > 0) {
+        const source = this.str.getBytes(n);
+        buffer.set(source, bufferLength);
+        bufferLength += n;
+      }
+    } else {
+      n = 257 - n;
+      const b = repeatHeader[1];
+      buffer = this.ensureBuffer(bufferLength + n + 1);
+
+      for (let i = 0; i < n; i++) {
+        buffer[bufferLength++] = b;
+      }
+    }
+
+    this.bufferLength = bufferLength;
+  }
+
+}
+
+exports.RunLengthStream = RunLengthStream;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 13 - 346
lib/core/stream.js


+ 379 - 0
lib/core/struct_tree.js

@@ -0,0 +1,379 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.StructTreeRoot = exports.StructTreePage = void 0;
+
+var _primitives = require("./primitives.js");
+
+var _util = require("../shared/util.js");
+
+var _name_number_tree = require("./name_number_tree.js");
+
+const MAX_DEPTH = 40;
+const StructElementType = {
+  PAGE_CONTENT: "PAGE_CONTENT",
+  STREAM_CONTENT: "STREAM_CONTENT",
+  OBJECT: "OBJECT",
+  ELEMENT: "ELEMENT"
+};
+
+class StructTreeRoot {
+  constructor(rootDict) {
+    this.dict = rootDict;
+    this.roleMap = new Map();
+  }
+
+  init() {
+    this.readRoleMap();
+  }
+
+  readRoleMap() {
+    const roleMapDict = this.dict.get("RoleMap");
+
+    if (!(0, _primitives.isDict)(roleMapDict)) {
+      return;
+    }
+
+    roleMapDict.forEach((key, value) => {
+      if (!(0, _primitives.isName)(value)) {
+        return;
+      }
+
+      this.roleMap.set(key, value.name);
+    });
+  }
+
+}
+
+exports.StructTreeRoot = StructTreeRoot;
+
+class StructElementNode {
+  constructor(tree, dict) {
+    this.tree = tree;
+    this.dict = dict;
+    this.kids = [];
+    this.parseKids();
+  }
+
+  get role() {
+    const nameObj = this.dict.get("S");
+    const name = (0, _primitives.isName)(nameObj) ? nameObj.name : "";
+    const {
+      root
+    } = this.tree;
+
+    if (root.roleMap.has(name)) {
+      return root.roleMap.get(name);
+    }
+
+    return name;
+  }
+
+  parseKids() {
+    let pageObjId = null;
+    const objRef = this.dict.getRaw("Pg");
+
+    if ((0, _primitives.isRef)(objRef)) {
+      pageObjId = objRef.toString();
+    }
+
+    const kids = this.dict.get("K");
+
+    if (Array.isArray(kids)) {
+      for (const kid of kids) {
+        const element = this.parseKid(pageObjId, kid);
+
+        if (element) {
+          this.kids.push(element);
+        }
+      }
+    } else {
+      const element = this.parseKid(pageObjId, kids);
+
+      if (element) {
+        this.kids.push(element);
+      }
+    }
+  }
+
+  parseKid(pageObjId, kid) {
+    if (Number.isInteger(kid)) {
+      if (this.tree.pageDict.objId !== pageObjId) {
+        return null;
+      }
+
+      return new StructElement({
+        type: StructElementType.PAGE_CONTENT,
+        mcid: kid,
+        pageObjId
+      });
+    }
+
+    let kidDict = null;
+
+    if ((0, _primitives.isRef)(kid)) {
+      kidDict = this.dict.xref.fetch(kid);
+    } else if ((0, _primitives.isDict)(kid)) {
+      kidDict = kid;
+    }
+
+    if (!kidDict) {
+      return null;
+    }
+
+    const pageRef = kidDict.getRaw("Pg");
+
+    if ((0, _primitives.isRef)(pageRef)) {
+      pageObjId = pageRef.toString();
+    }
+
+    const type = (0, _primitives.isName)(kidDict.get("Type")) ? kidDict.get("Type").name : null;
+
+    if (type === "MCR") {
+      if (this.tree.pageDict.objId !== pageObjId) {
+        return null;
+      }
+
+      return new StructElement({
+        type: StructElementType.STREAM_CONTENT,
+        refObjId: (0, _primitives.isRef)(kidDict.getRaw("Stm")) ? kidDict.getRaw("Stm").toString() : null,
+        pageObjId,
+        mcid: kidDict.get("MCID")
+      });
+    }
+
+    if (type === "OBJR") {
+      if (this.tree.pageDict.objId !== pageObjId) {
+        return null;
+      }
+
+      return new StructElement({
+        type: StructElementType.OBJECT,
+        refObjId: (0, _primitives.isRef)(kidDict.getRaw("Obj")) ? kidDict.getRaw("Obj").toString() : null,
+        pageObjId
+      });
+    }
+
+    return new StructElement({
+      type: StructElementType.ELEMENT,
+      dict: kidDict
+    });
+  }
+
+}
+
+class StructElement {
+  constructor({
+    type,
+    dict = null,
+    mcid = null,
+    pageObjId = null,
+    refObjId = null
+  }) {
+    this.type = type;
+    this.dict = dict;
+    this.mcid = mcid;
+    this.pageObjId = pageObjId;
+    this.refObjId = refObjId;
+    this.parentNode = null;
+  }
+
+}
+
+class StructTreePage {
+  constructor(structTreeRoot, pageDict) {
+    this.root = structTreeRoot;
+    this.rootDict = structTreeRoot ? structTreeRoot.dict : null;
+    this.pageDict = pageDict;
+    this.nodes = [];
+  }
+
+  parse() {
+    if (!this.root || !this.rootDict) {
+      return;
+    }
+
+    const parentTree = this.rootDict.get("ParentTree");
+
+    if (!parentTree) {
+      return;
+    }
+
+    const id = this.pageDict.get("StructParents");
+
+    if (!Number.isInteger(id)) {
+      return;
+    }
+
+    const numberTree = new _name_number_tree.NumberTree(parentTree, this.rootDict.xref);
+    const parentArray = numberTree.get(id);
+
+    if (!Array.isArray(parentArray)) {
+      return;
+    }
+
+    const map = new Map();
+
+    for (const ref of parentArray) {
+      if ((0, _primitives.isRef)(ref)) {
+        this.addNode(this.rootDict.xref.fetch(ref), map);
+      }
+    }
+  }
+
+  addNode(dict, map, level = 0) {
+    if (level > MAX_DEPTH) {
+      (0, _util.warn)("StructTree MAX_DEPTH reached.");
+      return null;
+    }
+
+    if (map.has(dict)) {
+      return map.get(dict);
+    }
+
+    const element = new StructElementNode(this, dict);
+    map.set(dict, element);
+    const parent = dict.get("P");
+
+    if (!parent || (0, _primitives.isName)(parent.get("Type"), "StructTreeRoot")) {
+      if (!this.addTopLevelNode(dict, element)) {
+        map.delete(dict);
+      }
+
+      return element;
+    }
+
+    const parentNode = this.addNode(parent, map, level + 1);
+
+    if (!parentNode) {
+      return element;
+    }
+
+    let save = false;
+
+    for (const kid of parentNode.kids) {
+      if (kid.type === StructElementType.ELEMENT && kid.dict === dict) {
+        kid.parentNode = element;
+        save = true;
+      }
+    }
+
+    if (!save) {
+      map.delete(dict);
+    }
+
+    return element;
+  }
+
+  addTopLevelNode(dict, element) {
+    const obj = this.rootDict.get("K");
+
+    if (!obj) {
+      return false;
+    }
+
+    if ((0, _primitives.isDict)(obj)) {
+      if (obj.objId !== dict.objId) {
+        return false;
+      }
+
+      this.nodes[0] = element;
+      return true;
+    }
+
+    if (!Array.isArray(obj)) {
+      return true;
+    }
+
+    let save = false;
+
+    for (let i = 0; i < obj.length; i++) {
+      const kidRef = obj[i];
+
+      if (kidRef && kidRef.toString() === dict.objId) {
+        this.nodes[i] = element;
+        save = true;
+      }
+    }
+
+    return save;
+  }
+
+  get serializable() {
+    function nodeToSerializable(node, parent, level = 0) {
+      if (level > MAX_DEPTH) {
+        (0, _util.warn)("StructTree too deep to be fully serialized.");
+        return;
+      }
+
+      const obj = Object.create(null);
+      obj.role = node.role;
+      obj.children = [];
+      parent.children.push(obj);
+      const alt = node.dict.get("Alt");
+
+      if ((0, _util.isString)(alt)) {
+        obj.alt = (0, _util.stringToPDFString)(alt);
+      }
+
+      for (const kid of node.kids) {
+        const kidElement = kid.type === StructElementType.ELEMENT ? kid.parentNode : null;
+
+        if (kidElement) {
+          nodeToSerializable(kidElement, obj, level + 1);
+          continue;
+        } else if (kid.type === StructElementType.PAGE_CONTENT || kid.type === StructElementType.STREAM_CONTENT) {
+          obj.children.push({
+            type: "content",
+            id: `page${kid.pageObjId}_mcid${kid.mcid}`
+          });
+        } else if (kid.type === StructElementType.OBJECT) {
+          obj.children.push({
+            type: "object",
+            id: kid.refObjId
+          });
+        }
+      }
+    }
+
+    const root = Object.create(null);
+    root.children = [];
+    root.role = "Root";
+
+    for (const child of this.nodes) {
+      if (!child) {
+        continue;
+      }
+
+      nodeToSerializable(child, root);
+    }
+
+    return root;
+  }
+
+}
+
+exports.StructTreePage = StructTreePage;

+ 118 - 0
lib/core/to_unicode_map.js

@@ -0,0 +1,118 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.ToUnicodeMap = exports.IdentityToUnicodeMap = void 0;
+
+var _util = require("../shared/util.js");
+
+class ToUnicodeMap {
+  constructor(cmap = []) {
+    this._map = cmap;
+  }
+
+  get length() {
+    return this._map.length;
+  }
+
+  forEach(callback) {
+    for (const charCode in this._map) {
+      callback(charCode, this._map[charCode].charCodeAt(0));
+    }
+  }
+
+  has(i) {
+    return this._map[i] !== undefined;
+  }
+
+  get(i) {
+    return this._map[i];
+  }
+
+  charCodeOf(value) {
+    const map = this._map;
+
+    if (map.length <= 0x10000) {
+      return map.indexOf(value);
+    }
+
+    for (const charCode in map) {
+      if (map[charCode] === value) {
+        return charCode | 0;
+      }
+    }
+
+    return -1;
+  }
+
+  amend(map) {
+    for (const charCode in map) {
+      this._map[charCode] = map[charCode];
+    }
+  }
+
+}
+
+exports.ToUnicodeMap = ToUnicodeMap;
+
+class IdentityToUnicodeMap {
+  constructor(firstChar, lastChar) {
+    this.firstChar = firstChar;
+    this.lastChar = lastChar;
+  }
+
+  get length() {
+    return this.lastChar + 1 - this.firstChar;
+  }
+
+  forEach(callback) {
+    for (let i = this.firstChar, ii = this.lastChar; i <= ii; i++) {
+      callback(i, i);
+    }
+  }
+
+  has(i) {
+    return this.firstChar <= i && i <= this.lastChar;
+  }
+
+  get(i) {
+    if (this.firstChar <= i && i <= this.lastChar) {
+      return String.fromCharCode(i);
+    }
+
+    return undefined;
+  }
+
+  charCodeOf(v) {
+    return Number.isInteger(v) && v >= this.firstChar && v <= this.lastChar ? v : -1;
+  }
+
+  amend(map) {
+    (0, _util.unreachable)("Should not call amend()");
+  }
+
+}
+
+exports.IdentityToUnicodeMap = IdentityToUnicodeMap;

+ 381 - 0
lib/core/type1_font.js

@@ -0,0 +1,381 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.Type1Font = void 0;
+
+var _cff_parser = require("./cff_parser.js");
+
+var _fonts_utils = require("./fonts_utils.js");
+
+var _core_utils = require("./core_utils.js");
+
+var _stream = require("./stream.js");
+
+var _type1_parser = require("./type1_parser.js");
+
+var _util = require("../shared/util.js");
+
+function findBlock(streamBytes, signature, startIndex) {
+  const streamBytesLength = streamBytes.length;
+  const signatureLength = signature.length;
+  const scanLength = streamBytesLength - signatureLength;
+  let i = startIndex,
+      found = false;
+
+  while (i < scanLength) {
+    let j = 0;
+
+    while (j < signatureLength && streamBytes[i + j] === signature[j]) {
+      j++;
+    }
+
+    if (j >= signatureLength) {
+      i += j;
+
+      while (i < streamBytesLength && (0, _core_utils.isWhiteSpace)(streamBytes[i])) {
+        i++;
+      }
+
+      found = true;
+      break;
+    }
+
+    i++;
+  }
+
+  return {
+    found,
+    length: i
+  };
+}
+
+function getHeaderBlock(stream, suggestedLength) {
+  const EEXEC_SIGNATURE = [0x65, 0x65, 0x78, 0x65, 0x63];
+  const streamStartPos = stream.pos;
+  let headerBytes, headerBytesLength, block;
+
+  try {
+    headerBytes = stream.getBytes(suggestedLength);
+    headerBytesLength = headerBytes.length;
+  } catch (ex) {}
+
+  if (headerBytesLength === suggestedLength) {
+    block = findBlock(headerBytes, EEXEC_SIGNATURE, suggestedLength - 2 * EEXEC_SIGNATURE.length);
+
+    if (block.found && block.length === suggestedLength) {
+      return {
+        stream: new _stream.Stream(headerBytes),
+        length: suggestedLength
+      };
+    }
+  }
+
+  (0, _util.warn)('Invalid "Length1" property in Type1 font -- trying to recover.');
+  stream.pos = streamStartPos;
+  const SCAN_BLOCK_LENGTH = 2048;
+  let actualLength;
+
+  while (true) {
+    const scanBytes = stream.peekBytes(SCAN_BLOCK_LENGTH);
+    block = findBlock(scanBytes, EEXEC_SIGNATURE, 0);
+
+    if (block.length === 0) {
+      break;
+    }
+
+    stream.pos += block.length;
+
+    if (block.found) {
+      actualLength = stream.pos - streamStartPos;
+      break;
+    }
+  }
+
+  stream.pos = streamStartPos;
+
+  if (actualLength) {
+    return {
+      stream: new _stream.Stream(stream.getBytes(actualLength)),
+      length: actualLength
+    };
+  }
+
+  (0, _util.warn)('Unable to recover "Length1" property in Type1 font -- using as is.');
+  return {
+    stream: new _stream.Stream(stream.getBytes(suggestedLength)),
+    length: suggestedLength
+  };
+}
+
+function getEexecBlock(stream, suggestedLength) {
+  const eexecBytes = stream.getBytes();
+  return {
+    stream: new _stream.Stream(eexecBytes),
+    length: eexecBytes.length
+  };
+}
+
+class Type1Font {
+  constructor(name, file, properties) {
+    const PFB_HEADER_SIZE = 6;
+    let headerBlockLength = properties.length1;
+    let eexecBlockLength = properties.length2;
+    let pfbHeader = file.peekBytes(PFB_HEADER_SIZE);
+    const pfbHeaderPresent = pfbHeader[0] === 0x80 && pfbHeader[1] === 0x01;
+
+    if (pfbHeaderPresent) {
+      file.skip(PFB_HEADER_SIZE);
+      headerBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
+    }
+
+    const headerBlock = getHeaderBlock(file, headerBlockLength);
+    const headerBlockParser = new _type1_parser.Type1Parser(headerBlock.stream, false, _fonts_utils.SEAC_ANALYSIS_ENABLED);
+    headerBlockParser.extractFontHeader(properties);
+
+    if (pfbHeaderPresent) {
+      pfbHeader = file.getBytes(PFB_HEADER_SIZE);
+      eexecBlockLength = pfbHeader[5] << 24 | pfbHeader[4] << 16 | pfbHeader[3] << 8 | pfbHeader[2];
+    }
+
+    const eexecBlock = getEexecBlock(file, eexecBlockLength);
+    const eexecBlockParser = new _type1_parser.Type1Parser(eexecBlock.stream, true, _fonts_utils.SEAC_ANALYSIS_ENABLED);
+    const data = eexecBlockParser.extractFontProgram(properties);
+
+    for (const key in data.properties) {
+      properties[key] = data.properties[key];
+    }
+
+    const charstrings = data.charstrings;
+    const type2Charstrings = this.getType2Charstrings(charstrings);
+    const subrs = this.getType2Subrs(data.subrs);
+    this.charstrings = charstrings;
+    this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);
+    this.seacs = this.getSeacs(data.charstrings);
+  }
+
+  get numGlyphs() {
+    return this.charstrings.length + 1;
+  }
+
+  getCharset() {
+    const charset = [".notdef"];
+    const charstrings = this.charstrings;
+
+    for (let glyphId = 0; glyphId < charstrings.length; glyphId++) {
+      charset.push(charstrings[glyphId].glyphName);
+    }
+
+    return charset;
+  }
+
+  getGlyphMapping(properties) {
+    const charstrings = this.charstrings;
+
+    if (properties.composite) {
+      const charCodeToGlyphId = Object.create(null);
+
+      for (let glyphId = 0, charstringsLen = charstrings.length; glyphId < charstringsLen; glyphId++) {
+        const charCode = properties.cMap.charCodeOf(glyphId);
+        charCodeToGlyphId[charCode] = glyphId + 1;
+      }
+
+      return charCodeToGlyphId;
+    }
+
+    const glyphNames = [".notdef"];
+    let builtInEncoding, glyphId;
+
+    for (glyphId = 0; glyphId < charstrings.length; glyphId++) {
+      glyphNames.push(charstrings[glyphId].glyphName);
+    }
+
+    const encoding = properties.builtInEncoding;
+
+    if (encoding) {
+      builtInEncoding = Object.create(null);
+
+      for (const charCode in encoding) {
+        glyphId = glyphNames.indexOf(encoding[charCode]);
+
+        if (glyphId >= 0) {
+          builtInEncoding[charCode] = glyphId;
+        }
+      }
+    }
+
+    return (0, _fonts_utils.type1FontGlyphMapping)(properties, builtInEncoding, glyphNames);
+  }
+
+  hasGlyphId(id) {
+    if (id < 0 || id >= this.numGlyphs) {
+      return false;
+    }
+
+    if (id === 0) {
+      return true;
+    }
+
+    const glyph = this.charstrings[id - 1];
+    return glyph.charstring.length > 0;
+  }
+
+  getSeacs(charstrings) {
+    const seacMap = [];
+
+    for (let i = 0, ii = charstrings.length; i < ii; i++) {
+      const charstring = charstrings[i];
+
+      if (charstring.seac) {
+        seacMap[i + 1] = charstring.seac;
+      }
+    }
+
+    return seacMap;
+  }
+
+  getType2Charstrings(type1Charstrings) {
+    const type2Charstrings = [];
+
+    for (let i = 0, ii = type1Charstrings.length; i < ii; i++) {
+      type2Charstrings.push(type1Charstrings[i].charstring);
+    }
+
+    return type2Charstrings;
+  }
+
+  getType2Subrs(type1Subrs) {
+    let bias = 0;
+    const count = type1Subrs.length;
+
+    if (count < 1133) {
+      bias = 107;
+    } else if (count < 33769) {
+      bias = 1131;
+    } else {
+      bias = 32768;
+    }
+
+    const type2Subrs = [];
+    let i;
+
+    for (i = 0; i < bias; i++) {
+      type2Subrs.push([0x0b]);
+    }
+
+    for (i = 0; i < count; i++) {
+      type2Subrs.push(type1Subrs[i]);
+    }
+
+    return type2Subrs;
+  }
+
+  wrap(name, glyphs, charstrings, subrs, properties) {
+    const cff = new _cff_parser.CFF();
+    cff.header = new _cff_parser.CFFHeader(1, 0, 4, 4);
+    cff.names = [name];
+    const topDict = new _cff_parser.CFFTopDict();
+    topDict.setByName("version", 391);
+    topDict.setByName("Notice", 392);
+    topDict.setByName("FullName", 393);
+    topDict.setByName("FamilyName", 394);
+    topDict.setByName("Weight", 395);
+    topDict.setByName("Encoding", null);
+    topDict.setByName("FontMatrix", properties.fontMatrix);
+    topDict.setByName("FontBBox", properties.bbox);
+    topDict.setByName("charset", null);
+    topDict.setByName("CharStrings", null);
+    topDict.setByName("Private", null);
+    cff.topDict = topDict;
+    const strings = new _cff_parser.CFFStrings();
+    strings.add("Version 0.11");
+    strings.add("See original notice");
+    strings.add(name);
+    strings.add(name);
+    strings.add("Medium");
+    cff.strings = strings;
+    cff.globalSubrIndex = new _cff_parser.CFFIndex();
+    const count = glyphs.length;
+    const charsetArray = [".notdef"];
+    let i, ii;
+
+    for (i = 0; i < count; i++) {
+      const glyphName = charstrings[i].glyphName;
+
+      const index = _cff_parser.CFFStandardStrings.indexOf(glyphName);
+
+      if (index === -1) {
+        strings.add(glyphName);
+      }
+
+      charsetArray.push(glyphName);
+    }
+
+    cff.charset = new _cff_parser.CFFCharset(false, 0, charsetArray);
+    const charStringsIndex = new _cff_parser.CFFIndex();
+    charStringsIndex.add([0x8b, 0x0e]);
+
+    for (i = 0; i < count; i++) {
+      charStringsIndex.add(glyphs[i]);
+    }
+
+    cff.charStrings = charStringsIndex;
+    const privateDict = new _cff_parser.CFFPrivateDict();
+    privateDict.setByName("Subrs", null);
+    const fields = ["BlueValues", "OtherBlues", "FamilyBlues", "FamilyOtherBlues", "StemSnapH", "StemSnapV", "BlueShift", "BlueFuzz", "BlueScale", "LanguageGroup", "ExpansionFactor", "ForceBold", "StdHW", "StdVW"];
+
+    for (i = 0, ii = fields.length; i < ii; i++) {
+      const field = fields[i];
+
+      if (!(field in properties.privateData)) {
+        continue;
+      }
+
+      const value = properties.privateData[field];
+
+      if (Array.isArray(value)) {
+        for (let j = value.length - 1; j > 0; j--) {
+          value[j] -= value[j - 1];
+        }
+      }
+
+      privateDict.setByName(field, value);
+    }
+
+    cff.topDict.privateDict = privateDict;
+    const subrIndex = new _cff_parser.CFFIndex();
+
+    for (i = 0, ii = subrs.length; i < ii; i++) {
+      subrIndex.add(subrs[i]);
+    }
+
+    privateDict.subrsIndex = subrIndex;
+    const compiler = new _cff_parser.CFFCompiler(cff);
+    return compiler.compile();
+  }
+
+}
+
+exports.Type1Font = Type1Font;

+ 47 - 36
lib/core/worker.js

@@ -73,7 +73,7 @@ exports.WorkerTask = WorkerTask;
 
 class WorkerMessageHandler {
   static setup(handler, port) {
-    var testMessageProcessed = false;
+    let testMessageProcessed = false;
     handler.on("test", function wphSetupTest(data) {
       if (testMessageProcessed) {
         return;
@@ -101,13 +101,13 @@ class WorkerMessageHandler {
   }
 
   static createDocumentHandler(docParams, port) {
-    var pdfManager;
-    var terminated = false;
-    var cancelXHRs = null;
-    var WorkerTasks = [];
+    let pdfManager;
+    let terminated = false;
+    let cancelXHRs = null;
+    const WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     const apiVersion = docParams.apiVersion;
-    const workerVersion = '2.8.335';
+    const workerVersion = '2.9.359';
 
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
@@ -127,10 +127,10 @@ class WorkerMessageHandler {
       throw new Error("The browser/environment lacks native support for critical " + "functionality used by the PDF.js library (e.g. `ReadableStream`); " + "please use a `legacy`-build instead.");
     }
 
-    var docId = docParams.docId;
-    var docBaseUrl = docParams.docBaseUrl;
-    var workerHandlerName = docParams.docId + "_worker";
-    var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
+    const docId = docParams.docId;
+    const docBaseUrl = docParams.docBaseUrl;
+    const workerHandlerName = docParams.docId + "_worker";
+    let handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
 
     function ensureNotTerminated() {
@@ -145,7 +145,7 @@ class WorkerMessageHandler {
 
     function finishWorkerTask(task) {
       task.finish();
-      var i = WorkerTasks.indexOf(task);
+      const i = WorkerTasks.indexOf(task);
       WorkerTasks.splice(i, 1);
     }
 
@@ -159,6 +159,13 @@ class WorkerMessageHandler {
       }
 
       const [numPages, fingerprint, isPureXfa] = await Promise.all([pdfManager.ensureDoc("numPages"), pdfManager.ensureDoc("fingerprint"), pdfManager.ensureDoc("isPureXfa")]);
+
+      if (isPureXfa) {
+        const task = new WorkerTask("loadXfaFonts");
+        startWorkerTask(task);
+        await pdfManager.loadXfaFonts(handler, task).catch(reason => {}).then(() => finishWorkerTask(task));
+      }
+
       return {
         numPages,
         fingerprint,
@@ -167,9 +174,9 @@ class WorkerMessageHandler {
     }
 
     function getPdfManager(data, evaluatorOptions, enableXfa) {
-      var pdfManagerCapability = (0, _util.createPromiseCapability)();
+      const pdfManagerCapability = (0, _util.createPromiseCapability)();
       let newPdfManager;
-      var source = data.source;
+      const source = data.source;
 
       if (source.data) {
         try {
@@ -182,7 +189,7 @@ class WorkerMessageHandler {
         return pdfManagerCapability.promise;
       }
 
-      var pdfStream,
+      let pdfStream,
           cachedChunks = [];
 
       try {
@@ -192,13 +199,13 @@ class WorkerMessageHandler {
         return pdfManagerCapability.promise;
       }
 
-      var fullRequest = pdfStream.getFullReader();
+      const fullRequest = pdfStream.getFullReader();
       fullRequest.headersReady.then(function () {
         if (!fullRequest.isRangeSupported) {
           return;
         }
 
-        var disableAutoFetch = source.disableAutoFetch || fullRequest.isStreamingSupported;
+        const disableAutoFetch = source.disableAutoFetch || fullRequest.isStreamingSupported;
         newPdfManager = new _pdf_manager.NetworkPdfManager(docId, pdfStream, {
           msgHandler: handler,
           password: source.password,
@@ -218,10 +225,10 @@ class WorkerMessageHandler {
         pdfManagerCapability.reject(reason);
         cancelXHRs = null;
       });
-      var loaded = 0;
+      let loaded = 0;
 
-      var flushChunks = function () {
-        var pdfFile = (0, _util.arraysToBytes)(cachedChunks);
+      const flushChunks = function () {
+        const pdfFile = (0, _util.arraysToBytes)(cachedChunks);
 
         if (source.length && pdfFile.length !== source.length) {
           (0, _util.warn)("reported HTTP length is different from actual");
@@ -237,8 +244,8 @@ class WorkerMessageHandler {
         cachedChunks = [];
       };
 
-      var readPromise = new Promise(function (resolve, reject) {
-        var readChunk = function ({
+      const readPromise = new Promise(function (resolve, reject) {
+        const readChunk = function ({
           value,
           done
         }) {
@@ -301,7 +308,7 @@ class WorkerMessageHandler {
         ensureNotTerminated();
 
         if (ex instanceof _util.PasswordException) {
-          var task = new WorkerTask(`PasswordException: response ${ex.code}`);
+          const task = new WorkerTask(`PasswordException: response ${ex.code}`);
           startWorkerTask(task);
           handler.sendWithPromise("PasswordRequest", ex).then(function ({
             password
@@ -339,7 +346,7 @@ class WorkerMessageHandler {
       }
 
       ensureNotTerminated();
-      var evaluatorOptions = {
+      const evaluatorOptions = {
         maxImageSize: data.maxImageSize,
         disableFontFace: data.disableFontFace,
         ignoreErrors: data.ignoreErrors,
@@ -414,7 +421,7 @@ class WorkerMessageHandler {
       pageIndex
     }) {
       return pdfManager.getPage(pageIndex).then(function (page) {
-        return page.jsActions;
+        return pdfManager.ensure(page, "jsActions");
       });
     });
     handler.on("GetPageXfa", function wphSetupGetXfa({
@@ -424,9 +431,6 @@ class WorkerMessageHandler {
         return pdfManager.ensure(page, "xfaData");
       });
     });
-    handler.on("GetIsPureXfa", function wphSetupGetIsPureXfa(data) {
-      return pdfManager.ensureDoc("isPureXfa");
-    });
     handler.on("GetOutline", function wphSetupGetOutline(data) {
       return pdfManager.ensureCatalog("documentOutline");
     });
@@ -526,11 +530,11 @@ class WorkerMessageHandler {
 
           newXrefInfo = {
             rootRef: xref.trailer.getRaw("Root") || null,
-            encrypt: xref.trailer.getRaw("Encrypt") || null,
+            encryptRef: xref.trailer.getRaw("Encrypt") || null,
             newRef: xref.getNewRef(),
             infoRef: xref.trailer.getRaw("Info") || null,
             info: infoObj,
-            fileIds: xref.trailer.getRaw("ID") || null,
+            fileIds: xref.trailer.get("ID") || null,
             startXRef,
             filename
           };
@@ -547,9 +551,9 @@ class WorkerMessageHandler {
       });
     });
     handler.on("GetOperatorList", function wphSetupRenderPage(data, sink) {
-      var pageIndex = data.pageIndex;
+      const pageIndex = data.pageIndex;
       pdfManager.getPage(pageIndex).then(function (page) {
-        var task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);
+        const task = new WorkerTask(`GetOperatorList: page ${pageIndex}`);
         startWorkerTask(task);
         const start = verbosity >= _util.VerbosityLevel.INFOS ? Date.now() : 0;
         page.getOperatorList({
@@ -582,14 +586,14 @@ class WorkerMessageHandler {
       });
     });
     handler.on("GetTextContent", function wphExtractText(data, sink) {
-      var pageIndex = data.pageIndex;
+      const pageIndex = data.pageIndex;
 
       sink.onPull = function (desiredSize) {};
 
       sink.onCancel = function (reason) {};
 
       pdfManager.getPage(pageIndex).then(function (page) {
-        var task = new WorkerTask("GetTextContent: page " + pageIndex);
+        const task = new WorkerTask("GetTextContent: page " + pageIndex);
         startWorkerTask(task);
         const start = verbosity >= _util.VerbosityLevel.INFOS ? Date.now() : 0;
         page.extractTextContent({
@@ -597,6 +601,7 @@ class WorkerMessageHandler {
           task,
           sink,
           normalizeWhitespace: data.normalizeWhitespace,
+          includeMarkedContent: data.includeMarkedContent,
           combineTextItems: data.combineTextItems
         }).then(function () {
           finishWorkerTask(task);
@@ -617,6 +622,11 @@ class WorkerMessageHandler {
         });
       });
     });
+    handler.on("GetStructTree", function wphGetStructTree(data) {
+      return pdfManager.getPage(data.pageIndex).then(function (page) {
+        return pdfManager.ensure(page, "getStructTree");
+      });
+    });
     handler.on("FontFallback", function (data) {
       return pdfManager.fontFallback(data.id, handler);
     });
@@ -640,10 +650,11 @@ class WorkerMessageHandler {
         cancelXHRs(new _util.AbortException("Worker was terminated."));
       }
 
-      WorkerTasks.forEach(function (task) {
+      for (const task of WorkerTasks) {
         waitOn.push(task.finished);
         task.terminate();
-      });
+      }
+
       return Promise.all(waitOn).then(function () {
         handler.destroy();
         handler = null;
@@ -657,7 +668,7 @@ class WorkerMessageHandler {
   }
 
   static initializeFromPort(port) {
-    var handler = new _message_handler.MessageHandler("worker", "main", port);
+    const handler = new _message_handler.MessageHandler("worker", "main", port);
     WorkerMessageHandler.setup(handler, port);
     handler.send("ready", null);
   }

+ 2 - 4
lib/core/worker_stream.js

@@ -55,11 +55,9 @@ class PDFWorkerStream {
       this._fullRequestReader.cancel(reason);
     }
 
-    const readers = this._rangeRequestReaders.slice(0);
-
-    readers.forEach(function (reader) {
+    for (const reader of this._rangeRequestReaders.slice(0)) {
       reader.cancel(reason);
-    });
+    }
   }
 
 }

+ 7 - 9
lib/core/writer.js

@@ -51,14 +51,13 @@ function writeDict(dict, buffer, transform) {
 function writeStream(stream, buffer, transform) {
   writeDict(stream.dict, buffer, transform);
   buffer.push(" stream\n");
-  let string = (0, _util.bytesToString)(stream.getBytes());
+  let string = stream.getString();
 
   if (transform !== null) {
     string = transform.encryptString(string);
   }
 
-  buffer.push(string);
-  buffer.push("\nendstream\n");
+  buffer.push(string, "\nendstream\n");
 }
 
 function writeArray(array, buffer, transform) {
@@ -161,7 +160,7 @@ function updateXFA(datasetsRef, newRefs, xref) {
   }
 
   const datasets = xref.fetchIfRef(datasetsRef);
-  const str = (0, _util.bytesToString)(datasets.getBytes());
+  const str = datasets.getString();
   const xml = new _xml_parser.SimpleXMLParser({
     hasAttributes: true
   }).parseFromString(str);
@@ -241,8 +240,8 @@ function incrementalUpdate({
     newXref.set("Info", xrefInfo.infoRef);
   }
 
-  if (xrefInfo.encrypt !== null) {
-    newXref.set("Encrypt", xrefInfo.encrypt);
+  if (xrefInfo.encryptRef !== null) {
+    newXref.set("Encrypt", xrefInfo.encryptRef);
   }
 
   newRefs.push({
@@ -263,14 +262,13 @@ function incrementalUpdate({
     maxOffset = Math.max(maxOffset, baseOffset);
     xrefTableData.push([1, baseOffset, Math.min(ref.gen, 0xffff)]);
     baseOffset += data.length;
-    indexes.push(ref.num);
-    indexes.push(1);
+    indexes.push(ref.num, 1);
     buffer.push(data);
   }
 
   newXref.set("Index", indexes);
 
-  if (xrefInfo.fileIds.length !== 0) {
+  if (Array.isArray(xrefInfo.fileIds) && xrefInfo.fileIds.length > 0) {
     const md5 = computeMD5(baseOffset, xrefInfo);
     newXref.set("ID", [xrefInfo.fileIds[0], md5]);
   }

+ 23 - 7
lib/core/xfa/bind.js

@@ -79,7 +79,7 @@ class Binder {
   _bindValue(formNode, data, picture) {
     if (formNode[_xfa_object.$hasSettableValue]()) {
       if (data[_xfa_object.$isDataValue]()) {
-        const value = data[_xfa_object.$content].trim();
+        const value = data[_xfa_object.$getDataValue]();
 
         formNode[_xfa_object.$setValue](createText(value));
 
@@ -102,7 +102,7 @@ class Binder {
     }
   }
 
-  _findDataByNameToConsume(name, dataNode, global) {
+  _findDataByNameToConsume(name, isValue, dataNode, global) {
     if (!name) {
       return null;
     }
@@ -111,10 +111,17 @@ class Binder {
 
     for (let i = 0; i < 3; i++) {
       generator = dataNode[_xfa_object.$getRealChildrenByNameIt](name, false, true);
-      match = generator.next().value;
 
-      if (match) {
-        return match;
+      while (true) {
+        match = generator.next().value;
+
+        if (!match) {
+          break;
+        }
+
+        if (isValue === match[_xfa_object.$isDataValue]()) {
+          return match;
+        }
       }
 
       if (dataNode[_xfa_object.$namespaceId] === _namespaces.NamespaceIds.datasets.id && dataNode[_xfa_object.$nodeName] === "data") {
@@ -128,7 +135,7 @@ class Binder {
       return null;
     }
 
-    generator = this.datasets[_xfa_object.$getRealChildrenByNameIt](name, false, false);
+    generator = this.data[_xfa_object.$getRealChildrenByNameIt](name, false, false);
 
     while (true) {
       match = generator.next().value;
@@ -424,6 +431,8 @@ class Binder {
       if (child.bind) {
         switch (child.bind.match) {
           case "none":
+            this._bindElement(child, dataNode);
+
             continue;
 
           case "global":
@@ -433,6 +442,9 @@ class Binder {
           case "dataRef":
             if (!child.bind.ref) {
               (0, _util.warn)(`XFA - ref is empty in node ${child[_xfa_object.$nodeName]}.`);
+
+              this._bindElement(child, dataNode);
+
               continue;
             }
 
@@ -489,7 +501,7 @@ class Binder {
           const matches = [];
 
           while (matches.length < max) {
-            const found = this._findDataByNameToConsume(child.name, dataNode, global);
+            const found = this._findDataByNameToConsume(child.name, child[_xfa_object.$hasSettableValue](), dataNode, global);
 
             if (!found) {
               break;
@@ -521,6 +533,10 @@ class Binder {
 
         this._bindOccurrences(child, match, picture);
       } else if (min > 0) {
+        this._setProperties(child, dataNode);
+
+        this._bindItems(child, dataNode);
+
         this._bindElement(child, dataNode);
       } else {
         uselessNodes.push(child);

+ 4 - 4
lib/core/xfa/builder.js

@@ -38,13 +38,11 @@ var _unknown = require("./unknown.js");
 
 var _util = require("../../shared/util.js");
 
-const _ids = Symbol();
-
 class Root extends _xfa_object.XFAObject {
   constructor(ids) {
     super(-1, "root", Object.create(null));
     this.element = null;
-    this[_ids] = ids;
+    this[_xfa_object.$ids] = ids;
   }
 
   [_xfa_object.$onChild](child) {
@@ -56,7 +54,9 @@ class Root extends _xfa_object.XFAObject {
     super[_xfa_object.$finalize]();
 
     if (this.element.template instanceof _template.Template) {
-      this.element.template[_xfa_object.$resolvePrototypes](this[_ids]);
+      this.element.template[_xfa_object.$resolvePrototypes](this[_xfa_object.$ids]);
+
+      this.element.template[_xfa_object.$ids] = this[_xfa_object.$ids];
     }
   }
 

+ 5 - 5
lib/core/xfa/config.js

@@ -128,7 +128,7 @@ class Area extends _xfa_object.XFAObject {
     this.level = (0, _utils.getInteger)({
       data: attributes.level,
       defaultValue: 0,
-      validator: n => n >= 1 && n <= 3
+      validate: n => n >= 1 && n <= 3
     });
     this.name = (0, _utils.getStringOption)(attributes.name, ["", "barcode", "coreinit", "deviceDriver", "font", "general", "layout", "merge", "script", "signature", "sourceSet", "templateCache"]);
   }
@@ -402,7 +402,7 @@ class Equate extends _xfa_object.XFAObject {
     this.force = (0, _utils.getInteger)({
       data: attributes.force,
       defaultValue: 1,
-      validator: n => n === 0
+      validate: n => n === 0
     });
     this.from = attributes.from || "";
     this.to = attributes.to || "";
@@ -735,12 +735,12 @@ class PageOffset extends _xfa_object.XFAObject {
     this.x = (0, _utils.getInteger)({
       data: attributes.x,
       defaultValue: "useXDCSetting",
-      validator: n => true
+      validate: n => true
     });
     this.y = (0, _utils.getInteger)({
       data: attributes.y,
       defaultValue: "useXDCSetting",
-      validator: n => true
+      validate: n => true
     });
   }
 
@@ -1158,7 +1158,7 @@ class TemplateCache extends _xfa_object.XFAObject {
     this.maxEntries = (0, _utils.getInteger)({
       data: attributes.maxEntries,
       defaultValue: 5,
-      validator: n => n >= 0
+      validate: n => n >= 0
     });
   }
 

+ 415 - 6
lib/core/xfa/html_utils.js

@@ -24,14 +24,25 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.addExtraDivForBorder = addExtraDivForBorder;
+exports.computeBbox = computeBbox;
+exports.fixDimensions = fixDimensions;
+exports.fixTextIndent = fixTextIndent;
+exports.getFonts = getFonts;
 exports.layoutClass = layoutClass;
+exports.layoutText = layoutText;
 exports.measureToString = measureToString;
 exports.toStyle = toStyle;
 
 var _xfa_object = require("./xfa_object.js");
 
+var _utils = require("./utils.js");
+
 var _util = require("../../shared/util.js");
 
+const wordNonWordRegex = new RegExp("([\\p{N}\\p{L}\\p{M}]+)|([^\\p{N}\\p{L}\\p{M}]+)", "gu");
+const wordFirstRegex = new RegExp("^[\\p{N}\\p{L}\\p{M}]", "u");
+
 function measureToString(m) {
   if (typeof m === "string") {
     return "0px";
@@ -42,6 +53,12 @@ function measureToString(m) {
 
 const converters = {
   anchorType(node, style) {
+    const parent = node[_xfa_object.$getParent]();
+
+    if (!parent || parent.layout && parent.layout !== "position") {
+      return;
+    }
+
     if (!("transform" in style)) {
       style.transform = "";
     }
@@ -82,8 +99,31 @@ const converters = {
   },
 
   dimensions(node, style) {
-    if (node.w) {
-      style.width = measureToString(node.w);
+    const parent = node[_xfa_object.$getParent]();
+
+    let width = node.w;
+    const height = node.h;
+
+    if (parent.layout && parent.layout.includes("row")) {
+      const extra = parent[_xfa_object.$extra];
+      const colSpan = node.colSpan;
+      let w;
+
+      if (colSpan === -1) {
+        w = extra.columnWidths.slice(extra.currentColumn).reduce((a, x) => a + x, 0);
+        extra.currentColumn = 0;
+      } else {
+        w = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, x) => a + x, 0);
+        extra.currentColumn = (extra.currentColumn + node.colSpan) % extra.columnWidths.length;
+      }
+
+      if (!isNaN(w)) {
+        width = node.w = w;
+      }
+    }
+
+    if (width !== "") {
+      style.width = measureToString(width);
     } else {
       style.width = "auto";
 
@@ -91,11 +131,13 @@ const converters = {
         style.maxWidth = measureToString(node.maxW);
       }
 
-      style.minWidth = measureToString(node.minW);
+      if (parent.layout === "position") {
+        style.minWidth = measureToString(node.minW);
+      }
     }
 
-    if (node.h) {
-      style.height = measureToString(node.h);
+    if (height !== "") {
+      style.height = measureToString(height);
     } else {
       style.height = "auto";
 
@@ -103,7 +145,9 @@ const converters = {
         style.maxHeight = measureToString(node.maxH);
       }
 
-      style.minHeight = measureToString(node.minH);
+      if (parent.layout === "position") {
+        style.minHeight = measureToString(node.minH);
+      }
     }
   },
 
@@ -141,10 +185,251 @@ const converters = {
         style.display = "none";
         break;
     }
+  },
+
+  hAlign(node, style) {
+    if (node[_xfa_object.$nodeName] === "para") {
+      switch (node.hAlign) {
+        case "justifyAll":
+          style.textAlign = "justify-all";
+          break;
+
+        case "radix":
+          style.textAlign = "left";
+          break;
+
+        default:
+          style.textAlign = node.hAlign;
+      }
+    } else {
+      switch (node.hAlign) {
+        case "right":
+        case "center":
+          style.justifyContent = node.hAlign;
+          break;
+      }
+    }
+  },
+
+  borderMarginPadding(node, style) {
+    const borderWidths = [0, 0, 0, 0];
+    const borderInsets = [0, 0, 0, 0];
+    const marginNode = node.margin ? [node.margin.topInset, node.margin.rightInset, node.margin.bottomInset, node.margin.leftInset] : [0, 0, 0, 0];
+    let borderMargin;
+
+    if (node.border) {
+      Object.assign(style, node.border[_xfa_object.$toStyle](borderWidths, borderInsets));
+      borderMargin = style.margin;
+      delete style.margin;
+    }
+
+    if (borderWidths.every(x => x === 0)) {
+      if (marginNode.every(x => x === 0)) {
+        return;
+      }
+
+      Object.assign(style, node.margin[_xfa_object.$toStyle]());
+      style.padding = style.margin;
+      delete style.margin;
+      delete style.outline;
+      delete style.outlineOffset;
+      return;
+    }
+
+    if (node.margin) {
+      Object.assign(style, node.margin[_xfa_object.$toStyle]());
+      style.padding = style.margin;
+      delete style.margin;
+    }
+
+    if (!style.borderWidth) {
+      return;
+    }
+
+    style.borderData = {
+      borderWidth: style.borderWidth,
+      borderColor: style.borderColor,
+      borderStyle: style.borderStyle,
+      margin: borderMargin
+    };
+    delete style.borderWidth;
+    delete style.borderColor;
+    delete style.borderStyle;
   }
 
 };
 
+function layoutText(text, fontSize, space) {
+  let width = 0;
+  let height = 0;
+  let totalWidth = 0;
+  const lineHeight = fontSize * 1.5;
+  const averageCharSize = fontSize * 0.4;
+  const maxCharOnLine = Math.floor(space.width / averageCharSize);
+  const chunks = text.match(wordNonWordRegex);
+  let treatedChars = 0;
+  let i = 0;
+  let chunk = chunks[0];
+
+  while (chunk) {
+    const w = chunk.length * averageCharSize;
+
+    if (width + w <= space.width) {
+      width += w;
+      treatedChars += chunk.length;
+      chunk = chunks[i++];
+      continue;
+    }
+
+    if (!wordFirstRegex.test(chunk) || chunk.length > maxCharOnLine) {
+      const numOfCharOnLine = Math.floor((space.width - width) / averageCharSize);
+      chunk = chunk.slice(numOfCharOnLine);
+      treatedChars += numOfCharOnLine;
+
+      if (height + lineHeight > space.height) {
+        return {
+          width: 0,
+          height: 0,
+          splitPos: treatedChars
+        };
+      }
+
+      totalWidth = Math.max(width, totalWidth);
+      width = 0;
+      height += lineHeight;
+      continue;
+    }
+
+    if (height + lineHeight > space.height) {
+      return {
+        width: 0,
+        height: 0,
+        splitPos: treatedChars
+      };
+    }
+
+    totalWidth = Math.max(width, totalWidth);
+    width = w;
+    height += lineHeight;
+    chunk = chunks[i++];
+  }
+
+  if (totalWidth === 0) {
+    totalWidth = width;
+  }
+
+  if (totalWidth !== 0) {
+    height += lineHeight;
+  }
+
+  return {
+    width: totalWidth,
+    height,
+    splitPos: -1
+  };
+}
+
+function computeBbox(node, html, availableSpace) {
+  let bbox;
+
+  if (node.w !== "" && node.h !== "") {
+    bbox = [node.x, node.y, node.w, node.h];
+  } else {
+    if (!availableSpace) {
+      return null;
+    }
+
+    let width = node.w;
+
+    if (width === "") {
+      if (node.maxW === 0) {
+        const parent = node[_xfa_object.$getParent]();
+
+        if (parent.layout === "position" && parent.w !== "") {
+          width = 0;
+        } else {
+          width = node.minW;
+        }
+      } else {
+        width = Math.min(node.maxW, availableSpace.width);
+      }
+
+      html.attributes.style.width = measureToString(width);
+    }
+
+    let height = node.h;
+
+    if (height === "") {
+      if (node.maxH === 0) {
+        const parent = node[_xfa_object.$getParent]();
+
+        if (parent.layout === "position" && parent.h !== "") {
+          height = 0;
+        } else {
+          height = node.minH;
+        }
+      } else {
+        height = Math.min(node.maxH, availableSpace.height);
+      }
+
+      html.attributes.style.height = measureToString(height);
+    }
+
+    bbox = [node.x, node.y, width, height];
+  }
+
+  return bbox;
+}
+
+function fixDimensions(node) {
+  const parent = node[_xfa_object.$getParent]();
+
+  if (parent.layout && parent.layout.includes("row")) {
+    const extra = parent[_xfa_object.$extra];
+    const colSpan = node.colSpan;
+    let width;
+
+    if (colSpan === -1) {
+      width = extra.columnWidths.slice(extra.currentColumn).reduce((a, w) => a + w, 0);
+    } else {
+      width = extra.columnWidths.slice(extra.currentColumn, extra.currentColumn + colSpan).reduce((a, w) => a + w, 0);
+    }
+
+    if (!isNaN(width)) {
+      node.w = width;
+    }
+  }
+
+  if (parent.w && node.w) {
+    node.w = Math.min(parent.w, node.w);
+  }
+
+  if (parent.h && node.h) {
+    node.h = Math.min(parent.h, node.h);
+  }
+
+  if (parent.layout && parent.layout !== "position") {
+    node.x = node.y = 0;
+
+    if (parent.layout === "tb") {
+      if (parent.w !== "" && (node.w === "" || node.w === 0 || node.w > parent.w)) {
+        node.w = parent.w;
+      }
+    }
+  }
+
+  if (node.layout === "position") {
+    node.minW = node.minH = 0;
+    node.maxW = node.maxH = Infinity;
+  } else {
+    if (node.layout === "table") {
+      if (node.w === "" && Array.isArray(node.columnWidths)) {
+        node.w = node.columnWidths.reduce((a, x) => a + x, 0);
+      }
+    }
+  }
+}
+
 function layoutClass(node) {
   switch (node.layout) {
     case "position":
@@ -201,4 +486,128 @@ function toStyle(node, ...names) {
   }
 
   return style;
+}
+
+function addExtraDivForBorder(html) {
+  const style = html.attributes.style;
+  const data = style.borderData;
+  const children = [];
+  const attributes = {
+    class: "xfaWrapper",
+    style: Object.create(null)
+  };
+
+  for (const key of ["top", "left"]) {
+    if (style[key] !== undefined) {
+      attributes.style[key] = style[key];
+    }
+  }
+
+  delete style.top;
+  delete style.left;
+
+  if (style.position === "absolute") {
+    attributes.style.position = "absolute";
+  } else {
+    attributes.style.position = "relative";
+  }
+
+  delete style.position;
+
+  if (style.justifyContent) {
+    attributes.style.justifyContent = style.justifyContent;
+    delete style.justifyContent;
+  }
+
+  if (data) {
+    delete style.borderData;
+    let insets;
+
+    if (data.margin) {
+      insets = data.margin.split(" ");
+      delete data.margin;
+    } else {
+      insets = ["0px", "0px", "0px", "0px"];
+    }
+
+    let width = "100%";
+    let height = width;
+
+    if (insets[1] !== "0px" || insets[3] !== "0px") {
+      width = `calc(100% - ${parseInt(insets[1]) + parseInt(insets[3])}px`;
+    }
+
+    if (insets[0] !== "0px" || insets[2] !== "0px") {
+      height = `calc(100% - ${parseInt(insets[0]) + parseInt(insets[2])}px`;
+    }
+
+    const borderStyle = {
+      top: insets[0],
+      left: insets[3],
+      width,
+      height
+    };
+
+    for (const [k, v] of Object.entries(data)) {
+      borderStyle[k] = v;
+    }
+
+    if (style.transform) {
+      borderStyle.transform = style.transform;
+    }
+
+    const borderDiv = {
+      name: "div",
+      attributes: {
+        class: "xfaBorderDiv",
+        style: borderStyle
+      }
+    };
+    children.push(borderDiv);
+  }
+
+  children.push(html);
+  return {
+    name: "div",
+    attributes,
+    children
+  };
+}
+
+function fixTextIndent(styles) {
+  const indent = (0, _utils.getMeasurement)(styles.textIndent, "0px");
+
+  if (indent >= 0) {
+    return;
+  }
+
+  const align = styles.textAlign || "left";
+
+  if (align === "left" || align === "right") {
+    const name = "margin" + (align === "left" ? "Left" : "Right");
+    const margin = (0, _utils.getMeasurement)(styles[name], "0px");
+    styles[name] = `${margin - indent}pt`;
+  }
+}
+
+function getFonts(family) {
+  if (family.startsWith("'")) {
+    family = `"${family.slice(1, family.length - 1)}"`;
+  } else if (family.includes(" ") && !family.startsWith('"')) {
+    family = `"${family}"`;
+  }
+
+  const fonts = [family];
+
+  switch (family) {
+    case `"Myriad Pro"`:
+      fonts.push(`"Roboto Condensed"`, `"Ubuntu Condensed"`, `"Microsoft Sans Serif"`, `"Apple Symbols"`, "Helvetica", `"sans serif"`);
+      break;
+
+    case "Arial":
+      fonts.push("Helvetica", `"Liberation Sans"`, "Arimo", `"sans serif"`);
+      break;
+  }
+
+  return fonts.join(",");
 }

+ 190 - 0
lib/core/xfa/layout.js

@@ -0,0 +1,190 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.addHTML = addHTML;
+exports.flushHTML = flushHTML;
+exports.getAvailableSpace = getAvailableSpace;
+
+var _xfa_object = require("./xfa_object.js");
+
+var _html_utils = require("./html_utils.js");
+
+function flushHTML(node) {
+  const attributes = node[_xfa_object.$extra].attributes;
+  const html = {
+    name: "div",
+    attributes,
+    children: node[_xfa_object.$extra].children
+  };
+
+  if (node[_xfa_object.$extra].failingNode) {
+    const htmlFromFailing = node[_xfa_object.$extra].failingNode[_xfa_object.$flushHTML]();
+
+    if (htmlFromFailing) {
+      html.children.push(htmlFromFailing);
+    }
+  }
+
+  if (html.children.length === 0) {
+    return null;
+  }
+
+  node[_xfa_object.$extra].children = [];
+  delete node[_xfa_object.$extra].line;
+  return html;
+}
+
+function addHTML(node, html, bbox) {
+  const extra = node[_xfa_object.$extra];
+  const availableSpace = extra.availableSpace;
+
+  switch (node.layout) {
+    case "position":
+      {
+        const [x, y, w, h] = bbox;
+        extra.width = Math.max(extra.width, x + w);
+        extra.height = Math.max(extra.height, y + h);
+        extra.children.push(html);
+        break;
+      }
+
+    case "lr-tb":
+    case "rl-tb":
+      if (!extra.line || extra.attempt === 1) {
+        extra.line = {
+          name: "div",
+          attributes: {
+            class: node.layout === "lr-tb" ? "xfaLr" : "xfaRl"
+          },
+          children: []
+        };
+        extra.children.push(extra.line);
+      }
+
+      extra.line.children.push(html);
+
+      if (extra.attempt === 0) {
+        const [,, w, h] = bbox;
+        extra.currentWidth += w;
+        extra.height = Math.max(extra.height, extra.prevHeight + h);
+      } else {
+        const [,, w, h] = bbox;
+        extra.width = Math.max(extra.width, extra.currentWidth);
+        extra.currentWidth = w;
+        extra.prevHeight = extra.height;
+        extra.height += h;
+        extra.attempt = 0;
+      }
+
+      break;
+
+    case "rl-row":
+    case "row":
+      {
+        extra.children.push(html);
+        const [,, w, h] = bbox;
+        extra.width += w;
+        extra.height = Math.max(extra.height, h);
+        const height = (0, _html_utils.measureToString)(extra.height);
+
+        for (const child of extra.children) {
+          if (child.attributes.class === "xfaWrapper") {
+            child.children[child.children.length - 1].attributes.style.height = height;
+          } else {
+            child.attributes.style.height = height;
+          }
+        }
+
+        break;
+      }
+
+    case "table":
+      {
+        const [,, w, h] = bbox;
+        extra.width = Math.min(availableSpace.width, Math.max(extra.width, w));
+        extra.height += h;
+        extra.children.push(html);
+        break;
+      }
+
+    case "tb":
+      {
+        const [,,, h] = bbox;
+        extra.width = availableSpace.width;
+        extra.height += h;
+        extra.children.push(html);
+        break;
+      }
+  }
+}
+
+function getAvailableSpace(node) {
+  const availableSpace = node[_xfa_object.$extra].availableSpace;
+
+  switch (node.layout) {
+    case "lr-tb":
+    case "rl-tb":
+      switch (node[_xfa_object.$extra].attempt) {
+        case 0:
+          return {
+            width: availableSpace.width - node[_xfa_object.$extra].currentWidth,
+            height: availableSpace.height - node[_xfa_object.$extra].prevHeight
+          };
+
+        case 1:
+          return {
+            width: availableSpace.width,
+            height: availableSpace.height - node[_xfa_object.$extra].height
+          };
+
+        default:
+          return {
+            width: Infinity,
+            height: availableSpace.height - node[_xfa_object.$extra].prevHeight
+          };
+      }
+
+    case "rl-row":
+    case "row":
+      const width = node[_xfa_object.$extra].columnWidths.slice(node[_xfa_object.$extra].currentColumn).reduce((a, x) => a + x);
+
+      return {
+        width,
+        height: availableSpace.height
+      };
+
+    case "table":
+    case "tb":
+      return {
+        width: availableSpace.width,
+        height: availableSpace.height - node[_xfa_object.$extra].height
+      };
+
+    case "position":
+    default:
+      return availableSpace;
+  }
+}

+ 9 - 0
lib/core/xfa/parser.js

@@ -43,6 +43,7 @@ class XFAParser extends _xml_parser.XMLParserBase {
     this._current = this._builder.buildRoot(this._ids);
     this._errorCode = _xml_parser.XMLParserErrorCode.NoError;
     this._whiteRegex = /^\s+$/;
+    this._nbsps = /\xa0+/g;
   }
 
   parse(data) {
@@ -58,6 +59,14 @@ class XFAParser extends _xml_parser.XMLParserBase {
   }
 
   onText(text) {
+    text = text.replace(this._nbsps, match => match.slice(1) + " ");
+
+    if (this._current[_xfa_object.$acceptWhitespace]()) {
+      this._current[_xfa_object.$onText](text);
+
+      return;
+    }
+
     if (this._whiteRegex.test(text)) {
       return;
     }

+ 25 - 7
lib/core/xfa/som.js

@@ -291,7 +291,8 @@ function createDataNode(root, container, expr) {
 
   for (let ii = parsed.length; i < ii; i++) {
     const {
-      cacheName,
+      name,
+      operator,
       index
     } = parsed[i];
 
@@ -300,14 +301,31 @@ function createDataNode(root, container, expr) {
       return createNodes(root, parsed.slice(i));
     }
 
-    const cached = somCache.get(root);
+    let children;
 
-    if (!cached) {
-      (0, _util.warn)(`XFA - createDataNode must be called after searchNode.`);
-      return null;
-    }
+    switch (operator) {
+      case operators.dot:
+        children = root[_xfa_object.$getChildrenByName](name, false);
+        break;
+
+      case operators.dotDot:
+        children = root[_xfa_object.$getChildrenByName](name, true);
+        break;
+
+      case operators.dotHash:
+        children = root[_xfa_object.$getChildrenByClass](name);
 
-    const children = cached.get(cacheName);
+        if (children instanceof _xfa_object.XFAObjectArray) {
+          children = children.children;
+        } else {
+          children = [children];
+        }
+
+        break;
+
+      default:
+        break;
+    }
 
     if (children.length === 0) {
       return createNodes(root, parsed.slice(i));

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 535 - 140
lib/core/xfa/template.js


+ 21 - 2
lib/core/xfa/utils.js

@@ -33,11 +33,13 @@ exports.getMeasurement = getMeasurement;
 exports.getRatio = getRatio;
 exports.getRelevant = getRelevant;
 exports.getStringOption = getStringOption;
+exports.HTMLResult = void 0;
 const dimConverters = {
   pt: x => x,
   cm: x => x / 2.54 * 72,
   mm: x => x / (10 * 2.54) * 72,
-  in: x => x * 72
+  in: x => x * 72,
+  px: x => x
 };
 const measurementPattern = /([+-]?[0-9]+\.?[0-9]*)(.*)/;
 
@@ -238,4 +240,21 @@ function getBBox(data) {
     width,
     height
   };
-}
+}
+
+class HTMLResult {
+  constructor(success, html, bbox) {
+    this.success = success;
+    this.html = html;
+    this.bbox = bbox;
+  }
+
+  static success(html, bbox = null) {
+    return new HTMLResult(true, html, bbox);
+  }
+
+}
+
+exports.HTMLResult = HTMLResult;
+HTMLResult.FAILURE = new HTMLResult(false, null, null);
+HTMLResult.EMPTY = new HTMLResult(true, null, null);

+ 105 - 14
lib/core/xfa/xfa_object.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.XmlObject = exports.XFAObjectArray = exports.XFAObject = exports.XFAAttribute = exports.StringObject = exports.OptionObject = exports.Option10 = exports.Option01 = exports.IntegerObject = exports.ContentObject = exports.$uid = exports.$toStyle = exports.$toHTML = exports.$text = exports.$setValue = exports.$setSetAttributes = exports.$setId = exports.$resolvePrototypes = exports.$removeChild = exports.$onText = exports.$onChildCheck = exports.$onChild = exports.$nsAttributes = exports.$nodeName = exports.$namespaceId = exports.$isTransparent = exports.$isDescendent = exports.$isDataValue = exports.$insertAt = exports.$indexOf = exports.$hasSettableValue = exports.$hasItem = exports.$global = exports.$getRealChildrenByNameIt = exports.$getParent = exports.$getChildrenByNameIt = exports.$getChildrenByName = exports.$getChildrenByClass = exports.$getChildren = exports.$getAttributeIt = exports.$finalize = exports.$extra = exports.$dump = exports.$data = exports.$content = exports.$consumed = exports.$clone = exports.$cleanup = exports.$clean = exports.$childrenToHTML = exports.$appendChild = void 0;
+exports.XmlObject = exports.XFAObjectArray = exports.XFAObject = exports.XFAAttribute = exports.StringObject = exports.OptionObject = exports.Option10 = exports.Option01 = exports.IntegerObject = exports.ContentObject = exports.$uid = exports.$toStyle = exports.$toHTML = exports.$text = exports.$setValue = exports.$setSetAttributes = exports.$setId = exports.$searchNode = exports.$resolvePrototypes = exports.$removeChild = exports.$onText = exports.$onChildCheck = exports.$onChild = exports.$nsAttributes = exports.$nodeName = exports.$namespaceId = exports.$isTransparent = exports.$isDescendent = exports.$isDataValue = exports.$insertAt = exports.$indexOf = exports.$ids = exports.$hasSettableValue = exports.$hasItem = exports.$global = exports.$getRealChildrenByNameIt = exports.$getParent = exports.$getNextPage = exports.$getDataValue = exports.$getChildrenByNameIt = exports.$getChildrenByName = exports.$getChildrenByClass = exports.$getChildren = exports.$getAvailableSpace = exports.$getAttributeIt = exports.$flushHTML = exports.$finalize = exports.$extra = exports.$dump = exports.$data = exports.$content = exports.$consumed = exports.$clone = exports.$cleanup = exports.$clean = exports.$childrenToHTML = exports.$break = exports.$appendChild = exports.$addHTML = exports.$acceptWhitespace = void 0;
 
 var _utils = require("./utils.js");
 
@@ -32,8 +32,14 @@ var _util = require("../../shared/util.js");
 
 var _namespaces = require("./namespaces.js");
 
+const $acceptWhitespace = Symbol();
+exports.$acceptWhitespace = $acceptWhitespace;
+const $addHTML = Symbol();
+exports.$addHTML = $addHTML;
 const $appendChild = Symbol();
 exports.$appendChild = $appendChild;
+const $break = Symbol();
+exports.$break = $break;
 const $childrenToHTML = Symbol();
 exports.$childrenToHTML = $childrenToHTML;
 const $clean = Symbol();
@@ -54,18 +60,26 @@ const $extra = Symbol("extra");
 exports.$extra = $extra;
 const $finalize = Symbol();
 exports.$finalize = $finalize;
+const $flushHTML = Symbol();
+exports.$flushHTML = $flushHTML;
 const $getAttributeIt = Symbol();
 exports.$getAttributeIt = $getAttributeIt;
+const $getAvailableSpace = Symbol();
+exports.$getAvailableSpace = $getAvailableSpace;
 const $getChildrenByClass = Symbol();
 exports.$getChildrenByClass = $getChildrenByClass;
 const $getChildrenByName = Symbol();
 exports.$getChildrenByName = $getChildrenByName;
 const $getChildrenByNameIt = Symbol();
 exports.$getChildrenByNameIt = $getChildrenByNameIt;
+const $getDataValue = Symbol();
+exports.$getDataValue = $getDataValue;
 const $getRealChildrenByNameIt = Symbol();
 exports.$getRealChildrenByNameIt = $getRealChildrenByNameIt;
 const $getChildren = Symbol();
 exports.$getChildren = $getChildren;
+const $getNextPage = Symbol();
+exports.$getNextPage = $getNextPage;
 const $getParent = Symbol();
 exports.$getParent = $getParent;
 const $global = Symbol();
@@ -74,6 +88,8 @@ const $hasItem = Symbol();
 exports.$hasItem = $hasItem;
 const $hasSettableValue = Symbol();
 exports.$hasSettableValue = $hasSettableValue;
+const $ids = Symbol();
+exports.$ids = $ids;
 const $indexOf = Symbol();
 exports.$indexOf = $indexOf;
 const $insertAt = Symbol();
@@ -101,6 +117,8 @@ const $removeChild = Symbol();
 exports.$removeChild = $removeChild;
 const $resolvePrototypes = Symbol();
 exports.$resolvePrototypes = $resolvePrototypes;
+const $searchNode = Symbol();
+exports.$searchNode = $searchNode;
 const $setId = Symbol();
 exports.$setId = $setId;
 const $setSetAttributes = Symbol();
@@ -130,6 +148,8 @@ const _dataValue = Symbol();
 
 const _defaultValue = Symbol();
 
+const _filteredChildrenGenerator = Symbol();
+
 const _getPrototype = Symbol();
 
 const _getUnsetAttributes = Symbol();
@@ -197,6 +217,10 @@ class XFAObject {
     return this.hasOwnProperty(child[$nodeName]) && child[$namespaceId] === this[$namespaceId];
   }
 
+  [$acceptWhitespace]() {
+    return false;
+  }
+
   [$setId](ids) {
     if (this.id && this[$namespaceId] === _namespaces.NamespaceIds.template.id) {
       ids.set(this.id, this);
@@ -341,24 +365,75 @@ class XFAObject {
   }
 
   [$toHTML]() {
+    return _utils.HTMLResult.EMPTY;
+  }
+
+  *[_filteredChildrenGenerator](filter, include) {
+    for (const node of this[$getChildren]()) {
+      if (!filter || include === filter.has(node[$nodeName])) {
+        const availableSpace = this[$getAvailableSpace]();
+        const res = node[$toHTML](availableSpace);
+
+        if (!res.success) {
+          this[$extra].failingNode = node;
+        }
+
+        yield res;
+      }
+    }
+  }
+
+  [$flushHTML]() {
     return null;
   }
 
+  [$addHTML](html, bbox) {
+    this[$extra].children.push(html);
+  }
+
+  [$getAvailableSpace]() {}
+
   [$childrenToHTML]({
     filter = null,
     include = true
   }) {
-    const res = [];
-    this[$getChildren]().forEach(node => {
-      if (!filter || include === filter.has(node[$nodeName])) {
-        const html = node[$toHTML]();
+    if (!this[$extra].generator) {
+      this[$extra].generator = this[_filteredChildrenGenerator](filter, include);
+    } else {
+      const availableSpace = this[$getAvailableSpace]();
+      const res = this[$extra].failingNode[$toHTML](availableSpace);
 
-        if (html) {
-          res.push(html);
-        }
+      if (!res.success) {
+        return false;
       }
-    });
-    return res;
+
+      if (res.html) {
+        this[$addHTML](res.html, res.bbox);
+      }
+
+      delete this[$extra].failingNode;
+    }
+
+    while (true) {
+      const gen = this[$extra].generator.next();
+
+      if (gen.done) {
+        break;
+      }
+
+      const res = gen.value;
+
+      if (!res.success) {
+        return false;
+      }
+
+      if (res.html) {
+        this[$addHTML](res.html, res.bbox);
+      }
+    }
+
+    this[$extra].generator = null;
+    return true;
   }
 
   [$setSetAttributes](attributes) {
@@ -729,13 +804,13 @@ class XmlObject extends XFAObject {
 
   [$toHTML]() {
     if (this[$nodeName] === "#text") {
-      return {
+      return _utils.HTMLResult.success({
         name: "#text",
         value: this[$content]
-      };
+      });
     }
 
-    return null;
+    return _utils.HTMLResult.EMPTY;
   }
 
   [$getChildren](name = null) {
@@ -800,12 +875,28 @@ class XmlObject extends XFAObject {
 
   [$isDataValue]() {
     if (this[_dataValue] === null) {
-      return this[_children].length === 0;
+      return this[_children].length === 0 || this[_children][0][$namespaceId] === _namespaces.NamespaceIds.xhtml.id;
     }
 
     return this[_dataValue];
   }
 
+  [$getDataValue]() {
+    if (this[_dataValue] === null) {
+      if (this[_children].length === 0) {
+        return this[$content].trim();
+      }
+
+      if (this[_children][0][$namespaceId] === _namespaces.NamespaceIds.xhtml.id) {
+        return this[_children][0][$text]().trim();
+      }
+
+      return null;
+    }
+
+    return this[$content].trim();
+  }
+
   [$dump]() {
     const dumped = Object.create(null);
 

+ 186 - 41
lib/core/xfa/xhtml.js

@@ -26,12 +26,58 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.XhtmlNamespace = void 0;
 
+var _xfa_object = require("./xfa_object.js");
+
 var _namespaces = require("./namespaces.js");
 
-var _xfa_object = require("./xfa_object.js");
+var _html_utils = require("./html_utils.js");
+
+var _utils = require("./utils.js");
 
 const XHTML_NS_ID = _namespaces.NamespaceIds.xhtml.id;
-const VALID_STYLES = new Set(["color", "font", "font-family", "font-size", "font-stretch", "font-style", "font-weight", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "letter-spacing", "line-height", "orphans", "page-break-after", "page-break-before", "page-break-inside", "tab-interval", "tab-stop", "text-decoration", "text-indent", "vertical-align", "widows", "kerning-mode", "xfa-font-horizontal-scale", "xfa-font-vertical-scale", "xfa-tab-stops"]);
+const VALID_STYLES = new Set(["color", "font", "font-family", "font-size", "font-stretch", "font-style", "font-weight", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "letter-spacing", "line-height", "orphans", "page-break-after", "page-break-before", "page-break-inside", "tab-interval", "tab-stop", "text-align", "text-decoration", "text-indent", "vertical-align", "widows", "kerning-mode", "xfa-font-horizontal-scale", "xfa-font-vertical-scale", "xfa-spacerun", "xfa-tab-stops"]);
+const StyleMapping = new Map([["page-break-after", "breakAfter"], ["page-break-before", "breakBefore"], ["page-break-inside", "breakInside"], ["kerning-mode", value => value === "none" ? "none" : "normal"], ["xfa-font-horizontal-scale", value => `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-font-vertical-scale", value => `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-spacerun", ""], ["xfa-tab-stops", ""], ["font-size", value => (0, _html_utils.measureToString)(1 * (0, _utils.getMeasurement)(value))], ["letter-spacing", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["line-height", value => (0, _html_utils.measureToString)(0.99 * (0, _utils.getMeasurement)(value))], ["margin", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-bottom", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-left", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-right", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-top", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["text-indent", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["font-family", value => (0, _html_utils.getFonts)(value)]]);
+const spacesRegExp = /\s+/g;
+const crlfRegExp = /[\r\n]+/g;
+
+function mapStyle(styleStr) {
+  const style = Object.create(null);
+
+  if (!styleStr) {
+    return style;
+  }
+
+  for (const [key, value] of styleStr.split(";").map(s => s.split(":", 2))) {
+    const mapping = StyleMapping.get(key);
+
+    if (mapping === "") {
+      continue;
+    }
+
+    let newValue = value;
+
+    if (mapping) {
+      if (typeof mapping === "string") {
+        newValue = mapping;
+      } else {
+        newValue = mapping(value);
+      }
+    }
+
+    if (key.endsWith("scale")) {
+      if (style.transform) {
+        style.transform = `${style[key]} ${newValue}`;
+      } else {
+        style.transform = newValue;
+      }
+    } else {
+      style[key.replaceAll(/-([a-zA-Z])/g, (_, x) => x.toUpperCase())] = newValue;
+    }
+  }
+
+  (0, _html_utils.fixTextIndent)(style);
+  return style;
+}
 
 function checkStyle(style) {
   if (!style) {
@@ -41,111 +87,210 @@ function checkStyle(style) {
   return style.trim().split(/\s*;\s*/).filter(s => !!s).map(s => s.split(/\s*:\s*/, 2)).filter(([key]) => VALID_STYLES.has(key)).map(kv => kv.join(":")).join(";");
 }
 
-class A extends _xfa_object.XmlObject {
+const NoWhites = new Set(["body", "html"]);
+
+class XhtmlObject extends _xfa_object.XmlObject {
+  constructor(attributes, name) {
+    super(XHTML_NS_ID, name);
+    this.style = checkStyle(attributes.style);
+  }
+
+  [_xfa_object.$acceptWhitespace]() {
+    return !NoWhites.has(this[_xfa_object.$nodeName]);
+  }
+
+  [_xfa_object.$onText](str) {
+    str = str.replace(crlfRegExp, "");
+
+    if (!this.style.includes("xfa-spacerun:yes")) {
+      str = str.replace(spacesRegExp, " ");
+    }
+
+    if (str) {
+      this[_xfa_object.$content] += str;
+    }
+  }
+
+  [_xfa_object.$toHTML](availableSpace) {
+    const children = [];
+    this[_xfa_object.$extra] = {
+      children
+    };
+
+    this[_xfa_object.$childrenToHTML]({});
+
+    if (children.length === 0 && !this[_xfa_object.$content]) {
+      return _utils.HTMLResult.EMPTY;
+    }
+
+    return _utils.HTMLResult.success({
+      name: this[_xfa_object.$nodeName],
+      attributes: {
+        href: this.href,
+        style: mapStyle(this.style)
+      },
+      children,
+      value: this[_xfa_object.$content] || ""
+    });
+  }
+
+}
+
+class A extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "a");
+    super(attributes, "a");
     this.href = attributes.href || "";
-    this.style = checkStyle(attributes.style);
   }
 
 }
 
-class B extends _xfa_object.XmlObject {
+class B extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "b");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "b");
   }
 
 }
 
-class Body extends _xfa_object.XmlObject {
+class Body extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "body");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "body");
+  }
+
+  [_xfa_object.$toHTML](availableSpace) {
+    const res = super[_xfa_object.$toHTML](availableSpace);
+
+    const {
+      html
+    } = res;
+
+    if (!html) {
+      return _utils.HTMLResult.EMPTY;
+    }
+
+    html.name = "div";
+    html.attributes.class = "xfaRich";
+    return res;
   }
 
 }
 
-class Br extends _xfa_object.XmlObject {
+class Br extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "br");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "br");
   }
 
   [_xfa_object.$text]() {
     return "\n";
   }
 
+  [_xfa_object.$toHTML](availableSpace) {
+    return _utils.HTMLResult.success({
+      name: "br"
+    });
+  }
+
 }
 
-class Html extends _xfa_object.XmlObject {
+class Html extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "html");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "html");
+  }
+
+  [_xfa_object.$toHTML](availableSpace) {
+    const children = [];
+    this[_xfa_object.$extra] = {
+      children
+    };
+
+    this[_xfa_object.$childrenToHTML]({});
+
+    if (children.length === 0) {
+      return _utils.HTMLResult.success({
+        name: "div",
+        attributes: {
+          class: "xfaRich",
+          style: {}
+        },
+        value: this[_xfa_object.$content] || ""
+      });
+    }
+
+    if (children.length === 1) {
+      const child = children[0];
+
+      if (child.attributes && child.attributes.class === "xfaRich") {
+        return _utils.HTMLResult.success(child);
+      }
+    }
+
+    return _utils.HTMLResult.success({
+      name: "div",
+      attributes: {
+        class: "xfaRich",
+        style: {}
+      },
+      children
+    });
   }
 
 }
 
-class I extends _xfa_object.XmlObject {
+class I extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "i");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "i");
   }
 
 }
 
-class Li extends _xfa_object.XmlObject {
+class Li extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "li");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "li");
   }
 
 }
 
-class Ol extends _xfa_object.XmlObject {
+class Ol extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "ol");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "ol");
   }
 
 }
 
-class P extends _xfa_object.XmlObject {
+class P extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "p");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "p");
+  }
+
+  [_xfa_object.$text]() {
+    return super[_xfa_object.$text]() + "\n";
   }
 
 }
 
-class Span extends _xfa_object.XmlObject {
+class Span extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "span");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "span");
   }
 
 }
 
-class Sub extends _xfa_object.XmlObject {
+class Sub extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "sub");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "sub");
   }
 
 }
 
-class Sup extends _xfa_object.XmlObject {
+class Sup extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "sup");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "sup");
   }
 
 }
 
-class Ul extends _xfa_object.XmlObject {
+class Ul extends XhtmlObject {
   constructor(attributes) {
-    super(XHTML_NS_ID, "ul");
-    this.style = checkStyle(attributes.style);
+    super(attributes, "ul");
   }
 
 }

+ 1 - 1
lib/core/xml_parser.js

@@ -160,7 +160,7 @@ class XMLParserBase {
       }
     }
 
-    while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== ">" && s[pos] !== "/") {
+    while (pos < s.length && !isWhitespace(s, pos) && s[pos] !== ">" && s[pos] !== "?" && s[pos] !== "/") {
       ++pos;
     }
 

+ 833 - 0
lib/core/xref.js

@@ -0,0 +1,833 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 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.XRef = 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 _crypto = require("./crypto.js");
+
+class XRef {
+  constructor(stream, pdfManager) {
+    this.stream = stream;
+    this.pdfManager = pdfManager;
+    this.entries = [];
+    this.xrefstms = Object.create(null);
+    this._cacheMap = new Map();
+    this.stats = {
+      streamTypes: Object.create(null),
+      fontTypes: Object.create(null)
+    };
+    this._newRefNum = null;
+  }
+
+  getNewRef() {
+    if (this._newRefNum === null) {
+      this._newRefNum = this.entries.length;
+    }
+
+    return _primitives.Ref.get(this._newRefNum++, 0);
+  }
+
+  resetNewRef() {
+    this._newRefNum = null;
+  }
+
+  setStartXRef(startXRef) {
+    this.startXRefQueue = [startXRef];
+  }
+
+  parse(recoveryMode = false) {
+    let trailerDict;
+
+    if (!recoveryMode) {
+      trailerDict = this.readXRef();
+    } else {
+      (0, _util.warn)("Indexing all PDF objects");
+      trailerDict = this.indexObjects();
+    }
+
+    trailerDict.assignXref(this);
+    this.trailer = trailerDict;
+    let encrypt;
+
+    try {
+      encrypt = trailerDict.get("Encrypt");
+    } catch (ex) {
+      if (ex instanceof _core_utils.MissingDataException) {
+        throw ex;
+      }
+
+      (0, _util.warn)(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
+    }
+
+    if ((0, _primitives.isDict)(encrypt)) {
+      const ids = trailerDict.get("ID");
+      const fileId = ids && ids.length ? ids[0] : "";
+      encrypt.suppressEncryption = true;
+      this.encrypt = new _crypto.CipherTransformFactory(encrypt, fileId, this.pdfManager.password);
+    }
+
+    let root;
+
+    try {
+      root = trailerDict.get("Root");
+    } catch (ex) {
+      if (ex instanceof _core_utils.MissingDataException) {
+        throw ex;
+      }
+
+      (0, _util.warn)(`XRef.parse - Invalid "Root" reference: "${ex}".`);
+    }
+
+    if ((0, _primitives.isDict)(root) && root.has("Pages")) {
+      this.root = root;
+    } else {
+      if (!recoveryMode) {
+        throw new _core_utils.XRefParseException();
+      }
+
+      throw new _util.FormatError("Invalid root reference");
+    }
+  }
+
+  processXRefTable(parser) {
+    if (!("tableState" in this)) {
+      this.tableState = {
+        entryNum: 0,
+        streamPos: parser.lexer.stream.pos,
+        parserBuf1: parser.buf1,
+        parserBuf2: parser.buf2
+      };
+    }
+
+    const obj = this.readXRefTable(parser);
+
+    if (!(0, _primitives.isCmd)(obj, "trailer")) {
+      throw new _util.FormatError("Invalid XRef table: could not find trailer dictionary");
+    }
+
+    let dict = parser.getObj();
+
+    if (!(0, _primitives.isDict)(dict) && dict.dict) {
+      dict = dict.dict;
+    }
+
+    if (!(0, _primitives.isDict)(dict)) {
+      throw new _util.FormatError("Invalid XRef table: could not parse trailer dictionary");
+    }
+
+    delete this.tableState;
+    return dict;
+  }
+
+  readXRefTable(parser) {
+    const stream = parser.lexer.stream;
+    const tableState = this.tableState;
+    stream.pos = tableState.streamPos;
+    parser.buf1 = tableState.parserBuf1;
+    parser.buf2 = tableState.parserBuf2;
+    let obj;
+
+    while (true) {
+      if (!("firstEntryNum" in tableState) || !("entryCount" in tableState)) {
+        if ((0, _primitives.isCmd)(obj = parser.getObj(), "trailer")) {
+          break;
+        }
+
+        tableState.firstEntryNum = obj;
+        tableState.entryCount = parser.getObj();
+      }
+
+      let first = tableState.firstEntryNum;
+      const count = tableState.entryCount;
+
+      if (!Number.isInteger(first) || !Number.isInteger(count)) {
+        throw new _util.FormatError("Invalid XRef table: wrong types in subsection header");
+      }
+
+      for (let i = tableState.entryNum; i < count; i++) {
+        tableState.streamPos = stream.pos;
+        tableState.entryNum = i;
+        tableState.parserBuf1 = parser.buf1;
+        tableState.parserBuf2 = parser.buf2;
+        const entry = {};
+        entry.offset = parser.getObj();
+        entry.gen = parser.getObj();
+        const type = parser.getObj();
+
+        if (type instanceof _primitives.Cmd) {
+          switch (type.cmd) {
+            case "f":
+              entry.free = true;
+              break;
+
+            case "n":
+              entry.uncompressed = true;
+              break;
+          }
+        }
+
+        if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {
+          throw new _util.FormatError(`Invalid entry in XRef subsection: ${first}, ${count}`);
+        }
+
+        if (i === 0 && entry.free && first === 1) {
+          first = 0;
+        }
+
+        if (!this.entries[i + first]) {
+          this.entries[i + first] = entry;
+        }
+      }
+
+      tableState.entryNum = 0;
+      tableState.streamPos = stream.pos;
+      tableState.parserBuf1 = parser.buf1;
+      tableState.parserBuf2 = parser.buf2;
+      delete tableState.firstEntryNum;
+      delete tableState.entryCount;
+    }
+
+    if (this.entries[0] && !this.entries[0].free) {
+      throw new _util.FormatError("Invalid XRef table: unexpected first object");
+    }
+
+    return obj;
+  }
+
+  processXRefStream(stream) {
+    if (!("streamState" in this)) {
+      const streamParameters = stream.dict;
+      const byteWidths = streamParameters.get("W");
+      let range = streamParameters.get("Index");
+
+      if (!range) {
+        range = [0, streamParameters.get("Size")];
+      }
+
+      this.streamState = {
+        entryRanges: range,
+        byteWidths,
+        entryNum: 0,
+        streamPos: stream.pos
+      };
+    }
+
+    this.readXRefStream(stream);
+    delete this.streamState;
+    return stream.dict;
+  }
+
+  readXRefStream(stream) {
+    let i, j;
+    const streamState = this.streamState;
+    stream.pos = streamState.streamPos;
+    const byteWidths = streamState.byteWidths;
+    const typeFieldWidth = byteWidths[0];
+    const offsetFieldWidth = byteWidths[1];
+    const generationFieldWidth = byteWidths[2];
+    const entryRanges = streamState.entryRanges;
+
+    while (entryRanges.length > 0) {
+      const first = entryRanges[0];
+      const n = entryRanges[1];
+
+      if (!Number.isInteger(first) || !Number.isInteger(n)) {
+        throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
+      }
+
+      if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {
+        throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
+      }
+
+      for (i = streamState.entryNum; i < n; ++i) {
+        streamState.entryNum = i;
+        streamState.streamPos = stream.pos;
+        let type = 0,
+            offset = 0,
+            generation = 0;
+
+        for (j = 0; j < typeFieldWidth; ++j) {
+          type = type << 8 | stream.getByte();
+        }
+
+        if (typeFieldWidth === 0) {
+          type = 1;
+        }
+
+        for (j = 0; j < offsetFieldWidth; ++j) {
+          offset = offset << 8 | stream.getByte();
+        }
+
+        for (j = 0; j < generationFieldWidth; ++j) {
+          generation = generation << 8 | stream.getByte();
+        }
+
+        const entry = {};
+        entry.offset = offset;
+        entry.gen = generation;
+
+        switch (type) {
+          case 0:
+            entry.free = true;
+            break;
+
+          case 1:
+            entry.uncompressed = true;
+            break;
+
+          case 2:
+            break;
+
+          default:
+            throw new _util.FormatError(`Invalid XRef entry type: ${type}`);
+        }
+
+        if (!this.entries[first + i]) {
+          this.entries[first + i] = entry;
+        }
+      }
+
+      streamState.entryNum = 0;
+      streamState.streamPos = stream.pos;
+      entryRanges.splice(0, 2);
+    }
+  }
+
+  indexObjects() {
+    const TAB = 0x9,
+          LF = 0xa,
+          CR = 0xd,
+          SPACE = 0x20;
+    const PERCENT = 0x25,
+          LT = 0x3c;
+
+    function readToken(data, offset) {
+      let token = "",
+          ch = data[offset];
+
+      while (ch !== LF && ch !== CR && ch !== LT) {
+        if (++offset >= data.length) {
+          break;
+        }
+
+        token += String.fromCharCode(ch);
+        ch = data[offset];
+      }
+
+      return token;
+    }
+
+    function skipUntil(data, offset, what) {
+      const length = what.length,
+            dataLength = data.length;
+      let skipped = 0;
+
+      while (offset < dataLength) {
+        let i = 0;
+
+        while (i < length && data[offset + i] === what[i]) {
+          ++i;
+        }
+
+        if (i >= length) {
+          break;
+        }
+
+        offset++;
+        skipped++;
+      }
+
+      return skipped;
+    }
+
+    const objRegExp = /^(\d+)\s+(\d+)\s+obj\b/;
+    const endobjRegExp = /\bendobj[\b\s]$/;
+    const nestedObjRegExp = /\s+(\d+\s+\d+\s+obj[\b\s<])$/;
+    const CHECK_CONTENT_LENGTH = 25;
+    const trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
+    const startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);
+    const objBytes = new Uint8Array([111, 98, 106]);
+    const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
+    this.entries.length = 0;
+    const stream = this.stream;
+    stream.pos = 0;
+    const buffer = stream.getBytes(),
+          length = buffer.length;
+    let position = stream.start;
+    const trailers = [],
+          xrefStms = [];
+
+    while (position < length) {
+      let ch = buffer[position];
+
+      if (ch === TAB || ch === LF || ch === CR || ch === SPACE) {
+        ++position;
+        continue;
+      }
+
+      if (ch === PERCENT) {
+        do {
+          ++position;
+
+          if (position >= length) {
+            break;
+          }
+
+          ch = buffer[position];
+        } while (ch !== LF && ch !== CR);
+
+        continue;
+      }
+
+      const token = readToken(buffer, position);
+      let m;
+
+      if (token.startsWith("xref") && (token.length === 4 || /\s/.test(token[4]))) {
+        position += skipUntil(buffer, position, trailerBytes);
+        trailers.push(position);
+        position += skipUntil(buffer, position, startxrefBytes);
+      } else if (m = objRegExp.exec(token)) {
+        const num = m[1] | 0,
+              gen = m[2] | 0;
+
+        if (!this.entries[num] || this.entries[num].gen === gen) {
+          this.entries[num] = {
+            offset: position - stream.start,
+            gen,
+            uncompressed: true
+          };
+        }
+
+        let contentLength,
+            startPos = position + token.length;
+
+        while (startPos < buffer.length) {
+          const endPos = startPos + skipUntil(buffer, startPos, objBytes) + 4;
+          contentLength = endPos - position;
+          const checkPos = Math.max(endPos - CHECK_CONTENT_LENGTH, startPos);
+          const tokenStr = (0, _util.bytesToString)(buffer.subarray(checkPos, endPos));
+
+          if (endobjRegExp.test(tokenStr)) {
+            break;
+          } else {
+            const objToken = nestedObjRegExp.exec(tokenStr);
+
+            if (objToken && objToken[1]) {
+              (0, _util.warn)('indexObjects: Found new "obj" inside of another "obj", ' + 'caused by missing "endobj" -- trying to recover.');
+              contentLength -= objToken[1].length;
+              break;
+            }
+          }
+
+          startPos = endPos;
+        }
+
+        const content = buffer.subarray(position, position + contentLength);
+        const xrefTagOffset = skipUntil(content, 0, xrefBytes);
+
+        if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {
+          xrefStms.push(position - stream.start);
+          this.xrefstms[position - stream.start] = 1;
+        }
+
+        position += contentLength;
+      } else if (token.startsWith("trailer") && (token.length === 7 || /\s/.test(token[7]))) {
+        trailers.push(position);
+        position += skipUntil(buffer, position, startxrefBytes);
+      } else {
+        position += token.length + 1;
+      }
+    }
+
+    for (let i = 0, ii = xrefStms.length; i < ii; ++i) {
+      this.startXRefQueue.push(xrefStms[i]);
+      this.readXRef(true);
+    }
+
+    let trailerDict;
+
+    for (let i = 0, ii = trailers.length; i < ii; ++i) {
+      stream.pos = trailers[i];
+      const parser = new _parser.Parser({
+        lexer: new _parser.Lexer(stream),
+        xref: this,
+        allowStreams: true,
+        recoveryMode: true
+      });
+      const obj = parser.getObj();
+
+      if (!(0, _primitives.isCmd)(obj, "trailer")) {
+        continue;
+      }
+
+      const dict = parser.getObj();
+
+      if (!(0, _primitives.isDict)(dict)) {
+        continue;
+      }
+
+      try {
+        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) {
+        continue;
+      }
+
+      if (dict.has("ID")) {
+        return dict;
+      }
+
+      trailerDict = dict;
+    }
+
+    if (trailerDict) {
+      return trailerDict;
+    }
+
+    throw new _util.InvalidPDFException("Invalid PDF structure.");
+  }
+
+  readXRef(recoveryMode = false) {
+    const stream = this.stream;
+    const startXRefParsedCache = new Set();
+
+    try {
+      while (this.startXRefQueue.length) {
+        const startXRef = this.startXRefQueue[0];
+
+        if (startXRefParsedCache.has(startXRef)) {
+          (0, _util.warn)("readXRef - skipping XRef table since it was already parsed.");
+          this.startXRefQueue.shift();
+          continue;
+        }
+
+        startXRefParsedCache.add(startXRef);
+        stream.pos = startXRef + stream.start;
+        const parser = new _parser.Parser({
+          lexer: new _parser.Lexer(stream),
+          xref: this,
+          allowStreams: true
+        });
+        let obj = parser.getObj();
+        let dict;
+
+        if ((0, _primitives.isCmd)(obj, "xref")) {
+          dict = this.processXRefTable(parser);
+
+          if (!this.topDict) {
+            this.topDict = dict;
+          }
+
+          obj = dict.get("XRefStm");
+
+          if (Number.isInteger(obj)) {
+            const pos = obj;
+
+            if (!(pos in this.xrefstms)) {
+              this.xrefstms[pos] = 1;
+              this.startXRefQueue.push(pos);
+            }
+          }
+        } else if (Number.isInteger(obj)) {
+          if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), "obj") || !(0, _primitives.isStream)(obj = parser.getObj())) {
+            throw new _util.FormatError("Invalid XRef stream");
+          }
+
+          dict = this.processXRefStream(obj);
+
+          if (!this.topDict) {
+            this.topDict = dict;
+          }
+
+          if (!dict) {
+            throw new _util.FormatError("Failed to read XRef stream");
+          }
+        } else {
+          throw new _util.FormatError("Invalid XRef stream header");
+        }
+
+        obj = dict.get("Prev");
+
+        if (Number.isInteger(obj)) {
+          this.startXRefQueue.push(obj);
+        } else if ((0, _primitives.isRef)(obj)) {
+          this.startXRefQueue.push(obj.num);
+        }
+
+        this.startXRefQueue.shift();
+      }
+
+      return this.topDict;
+    } catch (e) {
+      if (e instanceof _core_utils.MissingDataException) {
+        throw e;
+      }
+
+      (0, _util.info)("(while reading XRef): " + e);
+    }
+
+    if (recoveryMode) {
+      return undefined;
+    }
+
+    throw new _core_utils.XRefParseException();
+  }
+
+  getEntry(i) {
+    const xrefEntry = this.entries[i];
+
+    if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
+      return xrefEntry;
+    }
+
+    return null;
+  }
+
+  fetchIfRef(obj, suppressEncryption = false) {
+    if (obj instanceof _primitives.Ref) {
+      return this.fetch(obj, suppressEncryption);
+    }
+
+    return obj;
+  }
+
+  fetch(ref, suppressEncryption = false) {
+    if (!(ref instanceof _primitives.Ref)) {
+      throw new Error("ref object is not a reference");
+    }
+
+    const num = ref.num;
+
+    const cacheEntry = this._cacheMap.get(num);
+
+    if (cacheEntry !== undefined) {
+      if (cacheEntry instanceof _primitives.Dict && !cacheEntry.objId) {
+        cacheEntry.objId = ref.toString();
+      }
+
+      return cacheEntry;
+    }
+
+    let xrefEntry = this.getEntry(num);
+
+    if (xrefEntry === null) {
+      this._cacheMap.set(num, xrefEntry);
+
+      return xrefEntry;
+    }
+
+    if (xrefEntry.uncompressed) {
+      xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
+    } else {
+      xrefEntry = this.fetchCompressed(ref, xrefEntry, suppressEncryption);
+    }
+
+    if ((0, _primitives.isDict)(xrefEntry)) {
+      xrefEntry.objId = ref.toString();
+    } else if ((0, _primitives.isStream)(xrefEntry)) {
+      xrefEntry.dict.objId = ref.toString();
+    }
+
+    return xrefEntry;
+  }
+
+  fetchUncompressed(ref, xrefEntry, suppressEncryption = false) {
+    const gen = ref.gen;
+    let num = ref.num;
+
+    if (xrefEntry.gen !== gen) {
+      throw new _core_utils.XRefEntryException(`Inconsistent generation in XRef: ${ref}`);
+    }
+
+    const stream = this.stream.makeSubStream(xrefEntry.offset + this.stream.start);
+    const parser = new _parser.Parser({
+      lexer: new _parser.Lexer(stream),
+      xref: this,
+      allowStreams: true
+    });
+    const obj1 = parser.getObj();
+    const obj2 = parser.getObj();
+    const obj3 = parser.getObj();
+
+    if (obj1 !== num || obj2 !== gen || !(obj3 instanceof _primitives.Cmd)) {
+      throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
+    }
+
+    if (obj3.cmd !== "obj") {
+      if (obj3.cmd.startsWith("obj")) {
+        num = parseInt(obj3.cmd.substring(3), 10);
+
+        if (!Number.isNaN(num)) {
+          return num;
+        }
+      }
+
+      throw new _core_utils.XRefEntryException(`Bad (uncompressed) XRef entry: ${ref}`);
+    }
+
+    if (this.encrypt && !suppressEncryption) {
+      xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num, gen));
+    } else {
+      xrefEntry = parser.getObj();
+    }
+
+    if (!(0, _primitives.isStream)(xrefEntry)) {
+      this._cacheMap.set(num, xrefEntry);
+    }
+
+    return xrefEntry;
+  }
+
+  fetchCompressed(ref, xrefEntry, suppressEncryption = false) {
+    const tableOffset = xrefEntry.offset;
+    const stream = this.fetch(_primitives.Ref.get(tableOffset, 0));
+
+    if (!(0, _primitives.isStream)(stream)) {
+      throw new _util.FormatError("bad ObjStm stream");
+    }
+
+    const first = stream.dict.get("First");
+    const n = stream.dict.get("N");
+
+    if (!Number.isInteger(first) || !Number.isInteger(n)) {
+      throw new _util.FormatError("invalid first and n parameters for ObjStm stream");
+    }
+
+    let parser = new _parser.Parser({
+      lexer: new _parser.Lexer(stream),
+      xref: this,
+      allowStreams: true
+    });
+    const nums = new Array(n);
+    const offsets = new Array(n);
+
+    for (let i = 0; i < n; ++i) {
+      const num = parser.getObj();
+
+      if (!Number.isInteger(num)) {
+        throw new _util.FormatError(`invalid object number in the ObjStm stream: ${num}`);
+      }
+
+      const offset = parser.getObj();
+
+      if (!Number.isInteger(offset)) {
+        throw new _util.FormatError(`invalid object offset in the ObjStm stream: ${offset}`);
+      }
+
+      nums[i] = num;
+      offsets[i] = offset;
+    }
+
+    const start = (stream.start || 0) + first;
+    const entries = new Array(n);
+
+    for (let i = 0; i < n; ++i) {
+      const length = i < n - 1 ? offsets[i + 1] - offsets[i] : undefined;
+
+      if (length < 0) {
+        throw new _util.FormatError("Invalid offset in the ObjStm stream.");
+      }
+
+      parser = new _parser.Parser({
+        lexer: new _parser.Lexer(stream.makeSubStream(start + offsets[i], length, stream.dict)),
+        xref: this,
+        allowStreams: true
+      });
+      const obj = parser.getObj();
+      entries[i] = obj;
+
+      if ((0, _primitives.isStream)(obj)) {
+        continue;
+      }
+
+      const num = nums[i],
+            entry = this.entries[num];
+
+      if (entry && entry.offset === tableOffset && entry.gen === i) {
+        this._cacheMap.set(num, obj);
+      }
+    }
+
+    xrefEntry = entries[xrefEntry.gen];
+
+    if (xrefEntry === undefined) {
+      throw new _core_utils.XRefEntryException(`Bad (compressed) XRef entry: ${ref}`);
+    }
+
+    return xrefEntry;
+  }
+
+  async fetchIfRefAsync(obj, suppressEncryption) {
+    if (obj instanceof _primitives.Ref) {
+      return this.fetchAsync(obj, suppressEncryption);
+    }
+
+    return obj;
+  }
+
+  async fetchAsync(ref, suppressEncryption) {
+    try {
+      return this.fetch(ref, suppressEncryption);
+    } catch (ex) {
+      if (!(ex instanceof _core_utils.MissingDataException)) {
+        throw ex;
+      }
+
+      await this.pdfManager.requestRange(ex.begin, ex.end);
+      return this.fetchAsync(ref, suppressEncryption);
+    }
+  }
+
+  getCatalogObj() {
+    return this.root;
+  }
+
+}
+
+exports.XRef = XRef;

+ 143 - 155
lib/display/annotation_layer.js

@@ -265,9 +265,10 @@ class AnnotationElement {
   }
 
   _renderQuadrilaterals(className) {
-    this.quadrilaterals.forEach(quadrilateral => {
+    for (const quadrilateral of this.quadrilaterals) {
       quadrilateral.className = className;
-    });
+    }
+
     return this.quadrilaterals;
   }
 
@@ -457,39 +458,79 @@ class WidgetAnnotationElement extends AnnotationElement {
     }
   }
 
-  _setColor(event) {
-    const {
-      detail,
-      target
-    } = event;
-    const {
-      style
-    } = target;
-
-    for (const name of ["bgColor", "fillColor", "fgColor", "textColor", "borderColor", "strokeColor"]) {
-      let color = detail[name];
+  _dispatchEventFromSandbox(actions, jsEvent) {
+    const setColor = (jsName, styleName, event) => {
+      const color = event.detail[jsName];
+      event.target.style[styleName] = _scripting_utils.ColorConverters[`${color[0]}_HTML`](color.slice(1));
+    };
 
-      if (!color) {
-        continue;
+    const commonActions = {
+      display: event => {
+        const hidden = event.detail.display % 2 === 1;
+        event.target.style.visibility = hidden ? "hidden" : "visible";
+        this.annotationStorage.setValue(this.data.id, {
+          hidden,
+          print: event.detail.display === 0 || event.detail.display === 3
+        });
+      },
+      print: event => {
+        this.annotationStorage.setValue(this.data.id, {
+          print: event.detail.print
+        });
+      },
+      hidden: event => {
+        event.target.style.visibility = event.detail.hidden ? "hidden" : "visible";
+        this.annotationStorage.setValue(this.data.id, {
+          hidden: event.detail.hidden
+        });
+      },
+      focus: event => {
+        setTimeout(() => event.target.focus({
+          preventScroll: false
+        }), 0);
+      },
+      userName: event => {
+        event.target.title = event.detail.userName;
+      },
+      readonly: event => {
+        if (event.detail.readonly) {
+          event.target.setAttribute("readonly", "");
+        } else {
+          event.target.removeAttribute("readonly");
+        }
+      },
+      required: event => {
+        if (event.detail.required) {
+          event.target.setAttribute("required", "");
+        } else {
+          event.target.removeAttribute("required");
+        }
+      },
+      bgColor: event => {
+        setColor("bgColor", "backgroundColor", event);
+      },
+      fillColor: event => {
+        setColor("fillColor", "backgroundColor", event);
+      },
+      fgColor: event => {
+        setColor("fgColor", "color", event);
+      },
+      textColor: event => {
+        setColor("textColor", "color", event);
+      },
+      borderColor: event => {
+        setColor("borderColor", "borderColor", event);
+      },
+      strokeColor: event => {
+        setColor("strokeColor", "borderColor", event);
       }
+    };
 
-      color = _scripting_utils.ColorConverters[`${color[0]}_HTML`](color.slice(1));
-
-      switch (name) {
-        case "bgColor":
-        case "fillColor":
-          style.backgroundColor = color;
-          break;
+    for (const name of Object.keys(jsEvent.detail)) {
+      const action = actions[name] || commonActions[name];
 
-        case "fgColor":
-        case "textColor":
-          style.color = color;
-          break;
-
-        case "borderColor":
-        case "strokeColor":
-          style.borderColor = color;
-          break;
+      if (action) {
+        action(jsEvent);
       }
     }
   }
@@ -504,6 +545,19 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
     });
   }
 
+  setPropertyOnSiblings(base, key, value, keyInStorage) {
+    const storage = this.annotationStorage;
+
+    for (const element of document.getElementsByName(base.name)) {
+      if (element !== base) {
+        element[key] = value;
+        const data = Object.create(null);
+        data[keyInStorage] = value;
+        storage.setValue(element.getAttribute("id"), data);
+      }
+    }
+  }
+
   render() {
     const storage = this.annotationStorage;
     const id = this.data.id;
@@ -534,10 +588,11 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
 
       elementData.userValue = textContent;
       element.setAttribute("id", id);
-      element.addEventListener("input", function (event) {
+      element.addEventListener("input", event => {
         storage.setValue(id, {
           value: event.target.value
         });
+        this.setPropertyOnSiblings(element, "value", event.target.value, "value");
       });
 
       let blurListener = event => {
@@ -545,7 +600,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
           event.target.value = elementData.formattedValue;
         }
 
-        event.target.setSelectionRange(0, 0);
+        event.target.scrollLeft = 0;
         elementData.beforeInputSelectionRange = null;
       };
 
@@ -555,13 +610,10 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
             event.target.value = elementData.userValue;
           }
         });
-        element.addEventListener("updatefromsandbox", event => {
-          const {
-            detail
-          } = event;
+        element.addEventListener("updatefromsandbox", jsEvent => {
           const actions = {
-            value() {
-              elementData.userValue = detail.value || "";
+            value(event) {
+              elementData.userValue = event.detail.value || "";
               storage.setValue(id, {
                 value: elementData.userValue.toString()
               });
@@ -571,8 +623,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
               }
             },
 
-            valueAsString() {
-              elementData.formattedValue = detail.valueAsString || "";
+            valueAsString(event) {
+              elementData.formattedValue = event.detail.valueAsString || "";
 
               if (event.target !== document.activeElement) {
                 event.target.value = elementData.formattedValue;
@@ -583,29 +635,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
               });
             },
 
-            focus() {
-              setTimeout(() => event.target.focus({
-                preventScroll: false
-              }), 0);
-            },
-
-            userName() {
-              event.target.title = detail.userName;
-            },
-
-            hidden() {
-              event.target.style.visibility = detail.hidden ? "hidden" : "visible";
-              storage.setValue(id, {
-                hidden: detail.hidden
-              });
-            },
-
-            editable() {
-              event.target.disabled = !detail.editable;
-            },
-
-            selRange() {
-              const [selStart, selEnd] = detail.selRange;
+            selRange(event) {
+              const [selStart, selEnd] = event.detail.selRange;
 
               if (selStart >= 0 && selEnd < event.target.value.length) {
                 event.target.setSelectionRange(selStart, selEnd);
@@ -613,9 +644,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
             }
 
           };
-          Object.keys(detail).filter(name => name in actions).forEach(name => actions[name]());
 
-          this._setColor(event);
+          this._dispatchEventFromSandbox(actions, jsEvent);
         });
         element.addEventListener("keydown", event => {
           elementData.beforeInputValue = event.target.value;
@@ -770,9 +800,17 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
     const storage = this.annotationStorage;
     const data = this.data;
     const id = data.id;
-    const value = storage.getValue(id, {
+    let value = storage.getValue(id, {
       value: data.fieldValue && (data.exportValue && data.exportValue === data.fieldValue || !data.exportValue && data.fieldValue !== "Off")
     }).value;
+
+    if (typeof value === "string") {
+      value = value !== "Off";
+      storage.setValue(id, {
+        value
+      });
+    }
+
     this.container.className = "buttonWidgetAnnotation checkBox";
     const element = document.createElement("input");
     element.disabled = data.readOnly;
@@ -802,39 +840,18 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
     });
 
     if (this.enableScripting && this.hasJSActions) {
-      element.addEventListener("updatefromsandbox", event => {
-        const {
-          detail
-        } = event;
+      element.addEventListener("updatefromsandbox", jsEvent => {
         const actions = {
-          value() {
-            event.target.checked = detail.value !== "Off";
+          value(event) {
+            event.target.checked = event.detail.value !== "Off";
             storage.setValue(id, {
               value: event.target.checked
             });
-          },
-
-          focus() {
-            setTimeout(() => event.target.focus({
-              preventScroll: false
-            }), 0);
-          },
-
-          hidden() {
-            event.target.style.visibility = detail.hidden ? "hidden" : "visible";
-            storage.setValue(id, {
-              hidden: detail.hidden
-            });
-          },
-
-          editable() {
-            event.target.disabled = !detail.editable;
           }
 
         };
-        Object.keys(detail).filter(name => name in actions).forEach(name => actions[name]());
 
-        this._setColor(event);
+        this._dispatchEventFromSandbox(actions, jsEvent);
       });
 
       this._setEventListeners(element, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
@@ -858,9 +875,17 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
     const storage = this.annotationStorage;
     const data = this.data;
     const id = data.id;
-    const value = storage.getValue(id, {
+    let value = storage.getValue(id, {
       value: data.fieldValue === data.buttonValue
     }).value;
+
+    if (typeof value === "string") {
+      value = value !== data.buttonValue;
+      storage.setValue(id, {
+        value
+      });
+    }
+
     const element = document.createElement("input");
     element.disabled = data.readOnly;
     element.type = "radio";
@@ -891,13 +916,10 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
 
     if (this.enableScripting && this.hasJSActions) {
       const pdfButtonValue = data.buttonValue;
-      element.addEventListener("updatefromsandbox", event => {
-        const {
-          detail
-        } = event;
+      element.addEventListener("updatefromsandbox", jsEvent => {
         const actions = {
-          value() {
-            const checked = pdfButtonValue === detail.value;
+          value(event) {
+            const checked = pdfButtonValue === event.detail.value;
 
             for (const radio of document.getElementsByName(event.target.name)) {
               const radioId = radio.getAttribute("id");
@@ -906,29 +928,11 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
                 value: radio.checked
               });
             }
-          },
-
-          focus() {
-            setTimeout(() => event.target.focus({
-              preventScroll: false
-            }), 0);
-          },
-
-          hidden() {
-            event.target.style.visibility = detail.hidden ? "hidden" : "visible";
-            storage.setValue(id, {
-              hidden: detail.hidden
-            });
-          },
-
-          editable() {
-            event.target.disabled = !detail.editable;
           }
 
         };
-        Object.keys(detail).filter(name => name in actions).forEach(name => actions[name]());
 
-        this._setColor(event);
+        this._dispatchEventFromSandbox(actions, jsEvent);
       });
 
       this._setEventListeners(element, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
@@ -1015,14 +1019,11 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
     };
 
     if (this.enableScripting && this.hasJSActions) {
-      selectElement.addEventListener("updatefromsandbox", event => {
-        const {
-          detail
-        } = event;
+      selectElement.addEventListener("updatefromsandbox", jsEvent => {
         const actions = {
-          value() {
+          value(event) {
             const options = selectElement.options;
-            const value = detail.value;
+            const value = event.detail.value;
             const values = new Set(Array.isArray(value) ? value : [value]);
             Array.prototype.forEach.call(options, option => {
               option.selected = values.has(option.value);
@@ -1032,13 +1033,13 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          multipleSelection() {
+          multipleSelection(event) {
             selectElement.multiple = true;
           },
 
-          remove() {
+          remove(event) {
             const options = selectElement.options;
-            const index = detail.remove;
+            const index = event.detail.remove;
             options[index].selected = false;
             selectElement.remove(index);
 
@@ -1056,7 +1057,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          clear() {
+          clear(event) {
             while (selectElement.length !== 0) {
               selectElement.remove(0);
             }
@@ -1067,12 +1068,12 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          insert() {
+          insert(event) {
             const {
               index,
               displayValue,
               exportValue
-            } = detail.insert;
+            } = event.detail.insert;
             const optionElement = document.createElement("option");
             optionElement.textContent = displayValue;
             optionElement.value = exportValue;
@@ -1083,10 +1084,10 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          items() {
+          items(event) {
             const {
               items
-            } = detail;
+            } = event.detail;
 
             while (selectElement.length !== 0) {
               selectElement.remove(0);
@@ -1113,8 +1114,8 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          indices() {
-            const indices = new Set(detail.indices);
+          indices(event) {
+            const indices = new Set(event.detail.indices);
             const options = event.target.options;
             Array.prototype.forEach.call(options, (option, i) => {
               option.selected = indices.has(i);
@@ -1124,27 +1125,13 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
             });
           },
 
-          focus() {
-            setTimeout(() => event.target.focus({
-              preventScroll: false
-            }), 0);
-          },
-
-          hidden() {
-            event.target.style.visibility = detail.hidden ? "hidden" : "visible";
-            storage.setValue(id, {
-              hidden: detail.hidden
-            });
-          },
-
-          editable() {
-            event.target.disabled = !detail.editable;
+          editable(event) {
+            event.target.disabled = !event.detail.editable;
           }
 
         };
-        Object.keys(detail).filter(name => name in actions).forEach(name => actions[name]());
 
-        this._setColor(event);
+        this._dispatchEventFromSandbox(actions, jsEvent);
       });
       selectElement.addEventListener("input", event => {
         const exportValue = getValue(event, true);
@@ -1281,11 +1268,12 @@ class PopupElement {
       this.trigger = [this.trigger];
     }
 
-    this.trigger.forEach(element => {
+    for (const element of this.trigger) {
       element.addEventListener("click", this._toggle.bind(this));
       element.addEventListener("mouseover", this._show.bind(this, false));
       element.addEventListener("mouseout", this._hide.bind(this, false));
-    });
+    }
+
     popup.addEventListener("click", this._hide.bind(this, true));
     wrapper.appendChild(popup);
     return wrapper;
@@ -1807,9 +1795,9 @@ class AnnotationLayer {
       const elements = parameters.div.querySelectorAll(`[data-annotation-id="${data.id}"]`);
 
       if (elements) {
-        elements.forEach(element => {
+        for (const element of elements) {
           element.style.transform = transform;
-        });
+        }
       }
     }
 

+ 0 - 14
lib/display/annotation_storage.js

@@ -26,8 +26,6 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.AnnotationStorage = void 0;
 
-var _display_utils = require("./display_utils.js");
-
 var _util = require("../shared/util.js");
 
 class AnnotationStorage {
@@ -44,18 +42,6 @@ class AnnotationStorage {
     return obj !== undefined ? obj : defaultValue;
   }
 
-  getOrCreateValue(key, defaultValue) {
-    (0, _display_utils.deprecated)("Use getValue instead.");
-
-    if (this._storage.has(key)) {
-      return this._storage.get(key);
-    }
-
-    this._storage.set(key, defaultValue);
-
-    return defaultValue;
-  }
-
   setValue(key, value) {
     const obj = this._storage.get(key);
 

+ 57 - 39
lib/display/api.js

@@ -54,8 +54,6 @@ var _optional_content_config = require("./optional_content_config.js");
 
 var _transport_stream = require("./transport_stream.js");
 
-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;
@@ -261,7 +259,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
 
   return worker.messageHandler.sendWithPromise("GetDocRequest", {
     docId,
-    apiVersion: '2.8.335',
+    apiVersion: '2.9.359',
     source: {
       data: source.data,
       url: source.url,
@@ -405,7 +403,7 @@ class PDFDocumentProxy {
   }
 
   get annotationStorage() {
-    return (0, _util.shadow)(this, "annotationStorage", new _annotation_storage.AnnotationStorage());
+    return this._transport.annotationStorage;
   }
 
   get numPages() {
@@ -516,8 +514,16 @@ class PDFDocumentProxy {
     return this._transport.loadingTask;
   }
 
-  saveDocument(annotationStorage) {
-    return this._transport.saveDocument(annotationStorage);
+  saveDocument() {
+    if (arguments.length > 0) {
+      (0, _display_utils.deprecated)("saveDocument no longer accepts any options.");
+    }
+
+    if (this._transport.annotationStorage.size <= 0) {
+      (0, _display_utils.deprecated)("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead.");
+    }
+
+    return this._transport.saveDocument();
   }
 
   getFieldObjects() {
@@ -612,17 +618,21 @@ class PDFPageProxy {
     canvasContext,
     viewport,
     intent = "display",
-    enableWebGL = false,
     renderInteractiveForms = false,
     transform = null,
     imageLayer = null,
     canvasFactory = null,
     background = null,
-    annotationStorage = null,
+    includeAnnotationStorage = false,
     optionalContentConfigPromise = null
   }) {
     var _intentState;
 
+    if (arguments[0]?.annotationStorage !== undefined) {
+      (0, _display_utils.deprecated)("render no longer accepts an `annotationStorage` option, " + "please use the `includeAnnotationStorage`-boolean instead.");
+      includeAnnotationStorage || (includeAnnotationStorage = !!arguments[0].annotationStorage);
+    }
+
     if (this._stats) {
       this._stats.time("Overall");
     }
@@ -650,9 +660,7 @@ class PDFPageProxy {
     const canvasFactoryInstance = canvasFactory || new DefaultCanvasFactory({
       ownerDocument: this._ownerDocument
     });
-    const webGLContext = new _webgl.WebGLContext({
-      enable: enableWebGL
-    });
+    const annotationStorage = includeAnnotationStorage ? this._transport.annotationStorage.serializable : null;
 
     if (!intentState.displayReadyCapability) {
       intentState.displayReadyCapability = (0, _util.createPromiseCapability)();
@@ -670,7 +678,7 @@ class PDFPageProxy {
         pageIndex: this._pageIndex,
         intent: renderingIntent,
         renderInteractiveForms: renderInteractiveForms === true,
-        annotationStorage: annotationStorage?.serializable || null
+        annotationStorage
       });
     }
 
@@ -715,7 +723,6 @@ class PDFPageProxy {
       operatorList: intentState.operatorList,
       pageIndex: this._pageIndex,
       canvasFactory: canvasFactoryInstance,
-      webGLContext,
       useRequestAnimationFrame: renderingIntent !== "print",
       pdfBug: this._pdfBug
     });
@@ -788,13 +795,15 @@ class PDFPageProxy {
 
   streamTextContent({
     normalizeWhitespace = false,
-    disableCombineTextItems = false
+    disableCombineTextItems = false,
+    includeMarkedContent = false
   } = {}) {
     const TEXT_CONTENT_CHUNK_SIZE = 100;
     return this._transport.messageHandler.sendWithStream("GetTextContent", {
       pageIndex: this._pageIndex,
       normalizeWhitespace: normalizeWhitespace === true,
-      combineTextItems: disableCombineTextItems !== true
+      combineTextItems: disableCombineTextItems !== true,
+      includeMarkedContent: includeMarkedContent === true
     }, {
       highWaterMark: TEXT_CONTENT_CHUNK_SIZE,
 
@@ -833,6 +842,10 @@ class PDFPageProxy {
     });
   }
 
+  getStructTree() {
+    return this._structTreePromise || (this._structTreePromise = this._transport.getStructTree(this._pageIndex));
+  }
+
   _destroy() {
     this.destroyed = true;
     this._transport.pageCache[this._pageIndex] = null;
@@ -859,6 +872,7 @@ class PDFPageProxy {
     this._annotationsPromise = null;
     this._jsActionsPromise = null;
     this._xfaPromise = null;
+    this._structTreePromise = null;
     this.pendingCleanup = false;
     return Promise.all(waitOn);
   }
@@ -888,6 +902,7 @@ class PDFPageProxy {
     this._annotationsPromise = null;
     this._jsActionsPromise = null;
     this._xfaPromise = null;
+    this._structTreePromise = null;
 
     if (resetStats && this._stats) {
       this._stats = new _display_utils.StatTimer();
@@ -1095,7 +1110,11 @@ class LoopbackPort {
         return result;
       }
 
-      result = Array.isArray(value) ? [] : {};
+      if (value instanceof URL) {
+        throw new Error(`LoopbackPort.postMessage - cannot clone: ${value}`);
+      }
+
+      result = Array.isArray(value) ? [] : Object.create(null);
       cloned.set(value, result);
 
       for (const i in value) {
@@ -1478,8 +1497,8 @@ class WorkerTransport {
     this.setupMessageHandler();
   }
 
-  get loadingTaskSettled() {
-    return this.loadingTask._capability.settled;
+  get annotationStorage() {
+    return (0, _util.shadow)(this, "annotationStorage", new _annotation_storage.AnnotationStorage());
   }
 
   destroy() {
@@ -1495,25 +1514,22 @@ class WorkerTransport {
     }
 
     const waitOn = [];
-    this.pageCache.forEach(function (page) {
+
+    for (const page of this.pageCache) {
       if (page) {
         waitOn.push(page._destroy());
       }
-    });
+    }
+
     this.pageCache.length = 0;
     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);
+    if (this.hasOwnProperty("annotationStorage")) {
+      this.annotationStorage.resetModified();
     }
 
+    const terminated = this.messageHandler.sendWithPromise("Terminate", null);
+    waitOn.push(terminated);
     Promise.all(waitOn).then(() => {
       this.commonObjs.clear();
       this.fontLoader.clear();
@@ -1907,15 +1923,13 @@ class WorkerTransport {
     });
   }
 
-  saveDocument(annotationStorage) {
+  saveDocument() {
     return this.messageHandler.sendWithPromise("SaveDocument", {
       numPages: this._numPages,
-      annotationStorage: annotationStorage?.serializable || null,
+      annotationStorage: this.annotationStorage.serializable,
       filename: this._fullReader?.filename ?? null
     }).finally(() => {
-      if (annotationStorage) {
-        annotationStorage.resetModified();
-      }
+      this.annotationStorage.resetModified();
     });
   }
 
@@ -1989,6 +2003,12 @@ class WorkerTransport {
     });
   }
 
+  getStructTree(pageIndex) {
+    return this.messageHandler.sendWithPromise("GetStructTree", {
+      pageIndex
+    });
+  }
+
   getOutline() {
     return this.messageHandler.sendWithPromise("GetOutline", null);
   }
@@ -2142,7 +2162,6 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
       operatorList,
       pageIndex,
       canvasFactory,
-      webGLContext,
       useRequestAnimationFrame = false,
       pdfBug = false
     }) {
@@ -2154,7 +2173,6 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
       this.operatorList = operatorList;
       this._pageIndex = pageIndex;
       this.canvasFactory = canvasFactory;
-      this.webGLContext = webGLContext;
       this._pdfBug = pdfBug;
       this.running = false;
       this.graphicsReadyCallback = null;
@@ -2203,7 +2221,7 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
         imageLayer,
         background
       } = this.params;
-      this.gfx = new _canvas.CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.webGLContext, imageLayer, optionalContentConfig);
+      this.gfx = new _canvas.CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, imageLayer, optionalContentConfig);
       this.gfx.beginDrawing({
         transform,
         viewport,
@@ -2304,7 +2322,7 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
   return InternalRenderTask;
 }();
 
-const version = '2.8.335';
+const version = '2.9.359';
 exports.version = version;
-const build = '228adbf67';
+const build = 'e667c8cbc';
 exports.build = build;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 336 - 325
lib/display/canvas.js


+ 4 - 4
lib/display/content_disposition.js

@@ -26,6 +26,8 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.getFilenameFromContentDispositionHeader = getFilenameFromContentDispositionHeader;
 
+var _util = require("../shared/util.js");
+
 function getFilenameFromContentDispositionHeader(contentDisposition) {
   let needsEncodingFixup = true;
   let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition);
@@ -69,10 +71,8 @@ function getFilenameFromContentDispositionHeader(contentDisposition) {
         const decoder = new TextDecoder(encoding, {
           fatal: true
         });
-        const bytes = Array.from(value, function (ch) {
-          return ch.charCodeAt(0) & 0xff;
-        });
-        value = decoder.decode(new Uint8Array(bytes));
+        const buffer = (0, _util.stringToBytes)(value);
+        value = decoder.decode(buffer);
         needsEncodingFixup = false;
       } catch (e) {
         if (/^utf-?8$/i.test(encoding)) {

+ 2 - 4
lib/display/fetch_stream.js

@@ -95,11 +95,9 @@ class PDFFetchStream {
       this._fullRequestReader.cancel(reason);
     }
 
-    const readers = this._rangeRequestReaders.slice(0);
-
-    readers.forEach(function (reader) {
+    for (const reader of this._rangeRequestReaders.slice(0)) {
       reader.cancel(reason);
-    });
+    }
   }
 
 }

+ 43 - 26
lib/display/font_loader.js

@@ -66,9 +66,10 @@ class BaseFontLoader {
   }
 
   clear() {
-    this.nativeFontFaces.forEach(nativeFontFace => {
+    for (const nativeFontFace of this.nativeFontFaces) {
       this._document.fonts.delete(nativeFontFace);
-    });
+    }
+
     this.nativeFontFaces.length = 0;
 
     if (this.styleElement) {
@@ -267,8 +268,8 @@ exports.FontLoader = FontLoader;
       this.insertRule(rule);
       const names = [];
 
-      for (i = 0, ii = fonts.length; i < ii; i++) {
-        names.push(fonts[i].loadedName);
+      for (const font of fonts) {
+        names.push(font.loadedName);
       }
 
       names.push(loadTestFontId);
@@ -280,11 +281,11 @@ exports.FontLoader = FontLoader;
       div.style.position = "absolute";
       div.style.top = div.style.left = "0px";
 
-      for (i = 0, ii = names.length; i < ii; ++i) {
+      for (const name of names) {
         const span = this._document.createElement("span");
 
         span.textContent = "Hi";
-        span.style.fontFamily = names[i];
+        span.style.fontFamily = name;
         div.appendChild(span);
       }
 
@@ -326,7 +327,21 @@ class FontFaceObject {
       return null;
     }
 
-    const nativeFontFace = new FontFace(this.loadedName, this.data, {});
+    let nativeFontFace;
+
+    if (!this.cssFontInfo) {
+      nativeFontFace = new FontFace(this.loadedName, this.data, {});
+    } else {
+      const css = {
+        weight: this.cssFontInfo.fontWeight
+      };
+
+      if (this.cssFontInfo.italicAngle) {
+        css.style = `oblique ${this.cssFontInfo.italicAngle}deg`;
+      }
+
+      nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css);
+    }
 
     if (this.fontRegistry) {
       this.fontRegistry.registerFont(this);
@@ -340,9 +355,21 @@ class FontFaceObject {
       return null;
     }
 
-    const data = (0, _util.bytesToString)(new Uint8Array(this.data));
+    const data = (0, _util.bytesToString)(this.data);
     const url = `url(data:${this.mimetype};base64,${btoa(data)});`;
-    const rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
+    let rule;
+
+    if (!this.cssFontInfo) {
+      rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
+    } else {
+      let css = `font-weight: ${this.cssFontInfo.fontWeight};`;
+
+      if (this.cssFontInfo.italicAngle) {
+        css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`;
+      }
+
+      rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`;
+    }
 
     if (this.fontRegistry) {
       this.fontRegistry.registerFont(this, url);
@@ -356,7 +383,7 @@ class FontFaceObject {
       return this.compiledGlyphs[character];
     }
 
-    let cmds, current;
+    let cmds;
 
     try {
       cmds = objs.get(this.loadedName + "_path_" + character);
@@ -374,28 +401,18 @@ class FontFaceObject {
     }
 
     if (this.isEvalSupported && _util.IsEvalSupportedCached.value) {
-      let args,
-          js = "";
+      const jsBuf = [];
 
-      for (let i = 0, ii = cmds.length; i < ii; i++) {
-        current = cmds[i];
-
-        if (current.args !== undefined) {
-          args = current.args.join(",");
-        } else {
-          args = "";
-        }
-
-        js += "c." + current.cmd + "(" + args + ");\n";
+      for (const current of cmds) {
+        const args = current.args !== undefined ? current.args.join(",") : "";
+        jsBuf.push("c.", current.cmd, "(", args, ");\n");
       }
 
-      return this.compiledGlyphs[character] = new Function("c", "size", js);
+      return this.compiledGlyphs[character] = new Function("c", "size", jsBuf.join(""));
     }
 
     return this.compiledGlyphs[character] = function (c, size) {
-      for (let i = 0, ii = cmds.length; i < ii; i++) {
-        current = cmds[i];
-
+      for (const current of cmds) {
         if (current.cmd === "scale") {
           current.args = [size, -size];
         }

+ 18 - 20
lib/display/network.js

@@ -252,11 +252,9 @@ class PDFNetworkStream {
       this._fullRequestReader.cancel(reason);
     }
 
-    const readers = this._rangeRequestReaders.slice(0);
-
-    readers.forEach(function (reader) {
+    for (const reader of this._rangeRequestReaders.slice(0)) {
       reader.cancel(reason);
-    });
+    }
   }
 
 }
@@ -346,14 +344,14 @@ class PDFNetworkStreamFullRequestReader {
       return;
     }
 
-    this._requests.forEach(function (requestCapability) {
+    for (const requestCapability of this._requests) {
       requestCapability.resolve({
         value: undefined,
         done: true
       });
-    });
+    }
 
-    this._requests = [];
+    this._requests.length = 0;
   }
 
   _onError(status) {
@@ -363,12 +361,12 @@ class PDFNetworkStreamFullRequestReader {
 
     this._headersReceivedCapability.reject(exception);
 
-    this._requests.forEach(function (requestCapability) {
+    for (const requestCapability of this._requests) {
       requestCapability.reject(exception);
-    });
+    }
 
-    this._requests = [];
-    this._cachedChunks = [];
+    this._requests.length = 0;
+    this._cachedChunks.length = 0;
   }
 
   _onProgress(data) {
@@ -433,14 +431,14 @@ class PDFNetworkStreamFullRequestReader {
 
     this._headersReceivedCapability.reject(reason);
 
-    this._requests.forEach(function (requestCapability) {
+    for (const requestCapability of this._requests) {
       requestCapability.resolve({
         value: undefined,
         done: true
       });
-    });
+    }
 
-    this._requests = [];
+    this._requests.length = 0;
 
     if (this._manager.isPendingRequest(this._fullRequestId)) {
       this._manager.abortRequest(this._fullRequestId);
@@ -488,14 +486,14 @@ class PDFNetworkStreamRangeRequestReader {
 
     this._done = true;
 
-    this._requests.forEach(function (requestCapability) {
+    for (const requestCapability of this._requests) {
       requestCapability.resolve({
         value: undefined,
         done: true
       });
-    });
+    }
 
-    this._requests = [];
+    this._requests.length = 0;
 
     this._close();
   }
@@ -539,14 +537,14 @@ class PDFNetworkStreamRangeRequestReader {
   cancel(reason) {
     this._done = true;
 
-    this._requests.forEach(function (requestCapability) {
+    for (const requestCapability of this._requests) {
       requestCapability.resolve({
         value: undefined,
         done: true
       });
-    });
+    }
 
-    this._requests = [];
+    this._requests.length = 0;
 
     if (this._manager.isPendingRequest(this._requestId)) {
       this._manager.abortRequest(this._requestId);

+ 2 - 4
lib/display/node_stream.js

@@ -98,11 +98,9 @@ class PDFNodeStream {
       this._fullRequestReader.cancel(reason);
     }
 
-    const readers = this._rangeRequestReaders.slice(0);
-
-    readers.forEach(function (reader) {
+    for (const reader of this._rangeRequestReaders.slice(0)) {
       reader.cancel(reason);
-    });
+    }
   }
 
 }

+ 49 - 1
lib/display/optional_content_config.js

@@ -71,6 +71,54 @@ class OptionalContentConfig {
     }
   }
 
+  _evaluateVisibilityExpression(array) {
+    const length = array.length;
+
+    if (length < 2) {
+      return true;
+    }
+
+    const operator = array[0];
+
+    for (let i = 1; i < length; i++) {
+      const element = array[i];
+      let state;
+
+      if (Array.isArray(element)) {
+        state = this._evaluateVisibilityExpression(element);
+      } else if (this._groups.has(element)) {
+        state = this._groups.get(element).visible;
+      } else {
+        (0, _util.warn)(`Optional content group not found: ${element}`);
+        return true;
+      }
+
+      switch (operator) {
+        case "And":
+          if (!state) {
+            return false;
+          }
+
+          break;
+
+        case "Or":
+          if (state) {
+            return true;
+          }
+
+          break;
+
+        case "Not":
+          return !state;
+
+        default:
+          return true;
+      }
+    }
+
+    return operator === "And";
+  }
+
   isVisible(group) {
     if (group.type === "OCG") {
       if (!this._groups.has(group.id)) {
@@ -81,7 +129,7 @@ class OptionalContentConfig {
       return this._groups.get(group.id).visible;
     } else if (group.type === "OCMD") {
       if (group.expression) {
-        (0, _util.warn)("Visibility expression not supported yet.");
+        return this._evaluateVisibilityExpression(group.expression);
       }
 
       if (!group.policy || group.policy === "AnyOn") {

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels