2
0
Brendan Dahl 3 жил өмнө
parent
commit
a7fc0d33a1
100 өөрчлөгдсөн 5190 нэмэгдсэн , 2655 устгасан
  1. 3 2
      README.md
  2. 1 1
      bower.json
  3. 408 181
      build/pdf.js
  4. 0 0
      build/pdf.js.map
  5. 0 0
      build/pdf.min.js
  6. 2 2
      build/pdf.sandbox.js
  7. 0 0
      build/pdf.sandbox.js.map
  8. 0 0
      build/pdf.sandbox.min.js
  9. 293 93
      build/pdf.worker.js
  10. 0 0
      build/pdf.worker.js.map
  11. 0 0
      build/pdf.worker.min.js
  12. 148 19
      image_decoders/pdf.image_decoders.js
  13. 0 0
      image_decoders/pdf.image_decoders.js.map
  14. 0 0
      image_decoders/pdf.image_decoders.min.js
  15. 416 191
      legacy/build/pdf.js
  16. 0 0
      legacy/build/pdf.js.map
  17. 0 0
      legacy/build/pdf.min.js
  18. 2 2
      legacy/build/pdf.sandbox.js
  19. 0 0
      legacy/build/pdf.sandbox.js.map
  20. 0 0
      legacy/build/pdf.sandbox.min.js
  21. 359 176
      legacy/build/pdf.worker.js
  22. 0 0
      legacy/build/pdf.worker.js.map
  23. 0 0
      legacy/build/pdf.worker.min.js
  24. 412 187
      legacy/image_decoders/pdf.image_decoders.js
  25. 0 0
      legacy/image_decoders/pdf.image_decoders.js.map
  26. 0 0
      legacy/image_decoders/pdf.image_decoders.min.js
  27. 59 20
      legacy/web/pdf_viewer.css
  28. 340 480
      legacy/web/pdf_viewer.js
  29. 0 0
      legacy/web/pdf_viewer.js.map
  30. 53 20
      lib/core/annotation.js
  31. 1 1
      lib/core/bidi.js
  32. 193 40
      lib/core/catalog.js
  33. 4 0
      lib/core/ccitt.js
  34. 11 3
      lib/core/chunked_stream.js
  35. 8 1
      lib/core/cmap.js
  36. 8 8
      lib/core/colorspace.js
  37. 50 1
      lib/core/core_utils.js
  38. 3 1
      lib/core/crypto.js
  39. 141 58
      lib/core/document.js
  40. 1 1
      lib/core/encodings.js
  41. 144 151
      lib/core/evaluator.js
  42. 48 0
      lib/core/fonts.js
  43. 1 1
      lib/core/fonts_utils.js
  44. 1 1
      lib/core/function.js
  45. 10 10
      lib/core/image.js
  46. 2 2
      lib/core/jpeg_stream.js
  47. 1 1
      lib/core/jpg.js
  48. 77 68
      lib/core/parser.js
  49. 3 3
      lib/core/pattern.js
  50. 2 1
      lib/core/pdf_manager.js
  51. 1 1
      lib/core/predictor_stream.js
  52. 3 1
      lib/core/primitives.js
  53. 2 1
      lib/core/standard_fonts.js
  54. 6 0
      lib/core/struct_tree.js
  55. 1 1
      lib/core/unicode.js
  56. 6 23
      lib/core/worker.js
  57. 5 0
      lib/core/xfa/bind.js
  58. 2 2
      lib/core/xfa/builder.js
  59. 78 6
      lib/core/xfa/factory.js
  60. 1 1
      lib/core/xfa/fonts.js
  61. 1 1
      lib/core/xfa/formcalc_lexer.js
  62. 7 2
      lib/core/xfa/html_utils.js
  63. 1 1
      lib/core/xfa/layout.js
  64. 5 4
      lib/core/xfa/parser.js
  65. 24 10
      lib/core/xfa/template.js
  66. 1 1
      lib/core/xfa/utils.js
  67. 8 4
      lib/core/xfa/xfa_object.js
  68. 37 8
      lib/core/xfa/xhtml.js
  69. 91 40
      lib/core/xref.js
  70. 147 36
      lib/display/annotation_layer.js
  71. 136 128
      lib/display/api.js
  72. 1 1
      lib/display/base_factory.js
  73. 410 159
      lib/display/canvas.js
  74. 1 1
      lib/display/display_utils.js
  75. 1 2
      lib/display/font_loader.js
  76. 9 6
      lib/display/metadata.js
  77. 1 1
      lib/display/node_utils.js
  78. 39 36
      lib/display/pattern_helper.js
  79. 4 0
      lib/display/svg.js
  80. 1 1
      lib/display/text_layer.js
  81. 14 5
      lib/display/xfa_layer.js
  82. 70 70
      lib/pdf.js
  83. 2 2
      lib/pdf.sandbox.js
  84. 2 2
      lib/pdf.worker.js
  85. 4 15
      lib/shared/message_handler.js
  86. 78 4
      lib/shared/util.js
  87. 18 18
      lib/test/unit/annotation_spec.js
  88. 328 17
      lib/test/unit/api_spec.js
  89. 108 0
      lib/test/unit/base_viewer_spec.js
  90. 18 0
      lib/test/unit/bidi_spec.js
  91. 259 0
      lib/test/unit/event_utils_spec.js
  92. 1 1
      lib/test/unit/jasmine-boot.js
  93. 25 36
      lib/test/unit/pdf_find_controller_spec.js
  94. 1 1
      lib/test/unit/primitives_spec.js
  95. 2 0
      lib/test/unit/struct_tree_spec.js
  96. 6 5
      lib/test/unit/test_utils.js
  97. 9 271
      lib/test/unit/ui_utils_spec.js
  98. 5 0
      lib/test/unit/util_spec.js
  99. 2 2
      lib/test/unit/xfa_parser_spec.js
  100. 3 2
      lib/test/unit/xfa_serialize_data_spec.js

+ 3 - 2
README.md

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

+ 1 - 1
bower.json

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

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 408 - 181
build/pdf.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 2
build/pdf.sandbox.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.sandbox.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.sandbox.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 293 - 93
build/pdf.worker.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.worker.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
build/pdf.worker.min.js


+ 148 - 19
image_decoders/pdf.image_decoders.js

@@ -42,6 +42,7 @@ return /******/ (() => { // webpackBootstrap
 Object.defineProperty(exports, "__esModule", ({
 Object.defineProperty(exports, "__esModule", ({
   value: true
   value: true
 }));
 }));
+exports.VerbosityLevel = exports.Util = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.UNSUPPORTED_FEATURES = exports.TextRenderingMode = exports.StreamType = exports.RenderingIntentFlag = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.PageActionEventType = exports.OPS = exports.MissingPDFException = exports.IsLittleEndianCached = exports.IsEvalSupportedCached = exports.InvalidPDFException = exports.ImageKind = exports.IDENTITY_MATRIX = exports.FormatError = exports.FontType = exports.FONT_IDENTITY_MATRIX = exports.DocumentActionEventType = exports.CMapCompressionType = exports.BaseException = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMode = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.AnnotationActionEventType = exports.AbortException = void 0;
 exports.arrayByteLength = arrayByteLength;
 exports.arrayByteLength = arrayByteLength;
 exports.arraysToBytes = arraysToBytes;
 exports.arraysToBytes = arraysToBytes;
 exports.assert = assert;
 exports.assert = assert;
@@ -73,7 +74,6 @@ exports.stringToUTF8String = stringToUTF8String;
 exports.unreachable = unreachable;
 exports.unreachable = unreachable;
 exports.utf8StringToString = utf8StringToString;
 exports.utf8StringToString = utf8StringToString;
 exports.warn = warn;
 exports.warn = warn;
-exports.VerbosityLevel = exports.Util = exports.UNSUPPORTED_FEATURES = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.RenderingIntentFlag = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.PageActionEventType = exports.OPS = exports.MissingPDFException = exports.IsLittleEndianCached = exports.IsEvalSupportedCached = exports.InvalidPDFException = exports.ImageKind = exports.IDENTITY_MATRIX = exports.FormatError = exports.FontType = exports.FONT_IDENTITY_MATRIX = exports.DocumentActionEventType = exports.CMapCompressionType = exports.BaseException = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMode = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.AnnotationActionEventType = exports.AbortException = void 0;
 
 
 __w_pdfjs_require__(2);
 __w_pdfjs_require__(2);
 
 
@@ -610,14 +610,19 @@ class AbortException extends BaseException {
 }
 }
 
 
 exports.AbortException = AbortException;
 exports.AbortException = AbortException;
-const NullCharactersRegExp = /\x00/g;
+const NullCharactersRegExp = /\x00+/g;
+const InvisibleCharactersRegExp = /[\x01-\x1F]/g;
 
 
-function removeNullCharacters(str) {
+function removeNullCharacters(str, replaceInvisible = false) {
   if (typeof str !== "string") {
   if (typeof str !== "string") {
     warn("The argument for removeNullCharacters must be a string.");
     warn("The argument for removeNullCharacters must be a string.");
     return str;
     return str;
   }
   }
 
 
+  if (replaceInvisible) {
+    str = str.replace(InvisibleCharactersRegExp, " ");
+  }
+
   return str.replace(NullCharactersRegExp, "");
   return str.replace(NullCharactersRegExp, "");
 }
 }
 
 
@@ -844,6 +849,75 @@ class Util {
     return result;
     return result;
   }
   }
 
 
+  static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3) {
+    const tvalues = [],
+          bounds = [[], []];
+    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;
+
+    for (let i = 0; i < 2; ++i) {
+      if (i === 0) {
+        b = 6 * x0 - 12 * x1 + 6 * x2;
+        a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
+        c = 3 * x1 - 3 * x0;
+      } else {
+        b = 6 * y0 - 12 * y1 + 6 * y2;
+        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
+        c = 3 * y1 - 3 * y0;
+      }
+
+      if (Math.abs(a) < 1e-12) {
+        if (Math.abs(b) < 1e-12) {
+          continue;
+        }
+
+        t = -c / b;
+
+        if (0 < t && t < 1) {
+          tvalues.push(t);
+        }
+
+        continue;
+      }
+
+      b2ac = b * b - 4 * c * a;
+      sqrtb2ac = Math.sqrt(b2ac);
+
+      if (b2ac < 0) {
+        continue;
+      }
+
+      t1 = (-b + sqrtb2ac) / (2 * a);
+
+      if (0 < t1 && t1 < 1) {
+        tvalues.push(t1);
+      }
+
+      t2 = (-b - sqrtb2ac) / (2 * a);
+
+      if (0 < t2 && t2 < 1) {
+        tvalues.push(t2);
+      }
+    }
+
+    let j = tvalues.length,
+        mt;
+    const jlen = j;
+
+    while (j--) {
+      t = tvalues[j];
+      mt = 1 - t;
+      bounds[0][j] = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
+      bounds[1][j] = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
+    }
+
+    bounds[0][jlen] = x0;
+    bounds[1][jlen] = y0;
+    bounds[0][jlen + 1] = x3;
+    bounds[1][jlen + 1] = y3;
+    bounds[0].length = bounds[1].length = jlen + 2;
+    return [Math.min(...bounds[0]), Math.min(...bounds[1]), Math.max(...bounds[0]), Math.max(...bounds[1])];
+  }
+
 }
 }
 
 
 exports.Util = Util;
 exports.Util = Util;
@@ -965,7 +1039,7 @@ function createPromiseCapability() {
 }
 }
 
 
 function createObjectURL(data, contentType = "", forceDataSchema = false) {
 function createObjectURL(data, contentType = "", forceDataSchema = false) {
-  if (URL.createObjectURL && !forceDataSchema) {
+  if (URL.createObjectURL && typeof Blob !== "undefined" && !forceDataSchema) {
     return URL.createObjectURL(new Blob([data], {
     return URL.createObjectURL(new Blob([data], {
       type: contentType
       type: contentType
     }));
     }));
@@ -3219,6 +3293,7 @@ exports.Jbig2Image = Jbig2Image;
 Object.defineProperty(exports, "__esModule", ({
 Object.defineProperty(exports, "__esModule", ({
   value: true
   value: true
 }));
 }));
+exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = exports.DocStats = void 0;
 exports.collectActions = collectActions;
 exports.collectActions = collectActions;
 exports.encodeToXmlString = encodeToXmlString;
 exports.encodeToXmlString = encodeToXmlString;
 exports.escapePDFName = escapePDFName;
 exports.escapePDFName = escapePDFName;
@@ -3234,7 +3309,6 @@ exports.readUint32 = readUint32;
 exports.recoverJsURL = recoverJsURL;
 exports.recoverJsURL = recoverJsURL;
 exports.toRomanNumerals = toRomanNumerals;
 exports.toRomanNumerals = toRomanNumerals;
 exports.validateCSSFont = validateCSSFont;
 exports.validateCSSFont = validateCSSFont;
-exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = void 0;
 
 
 var _util = __w_pdfjs_require__(1);
 var _util = __w_pdfjs_require__(1);
 
 
@@ -3310,6 +3384,55 @@ class XRefParseException extends _util.BaseException {
 
 
 exports.XRefParseException = XRefParseException;
 exports.XRefParseException = XRefParseException;
 
 
+class DocStats {
+  constructor(handler) {
+    this._handler = handler;
+    this._streamTypes = new Set();
+    this._fontTypes = new Set();
+  }
+
+  _send() {
+    const streamTypes = Object.create(null),
+          fontTypes = Object.create(null);
+
+    for (const type of this._streamTypes) {
+      streamTypes[type] = true;
+    }
+
+    for (const type of this._fontTypes) {
+      fontTypes[type] = true;
+    }
+
+    this._handler.send("DocStats", {
+      streamTypes,
+      fontTypes
+    });
+  }
+
+  addStreamType(type) {
+    if (this._streamTypes.has(type)) {
+      return;
+    }
+
+    this._streamTypes.add(type);
+
+    this._send();
+  }
+
+  addFontType(type) {
+    if (this._fontTypes.has(type)) {
+      return;
+    }
+
+    this._fontTypes.add(type);
+
+    this._send();
+  }
+
+}
+
+exports.DocStats = DocStats;
+
 function getInheritableProperty({
 function getInheritableProperty({
   dict,
   dict,
   key,
   key,
@@ -3655,6 +3778,7 @@ function recoverJsURL(str) {
 Object.defineProperty(exports, "__esModule", ({
 Object.defineProperty(exports, "__esModule", ({
   value: true
   value: true
 }));
 }));
+exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF = exports.Dict = exports.Cmd = exports.CIRCULAR_REF = void 0;
 exports.clearPrimitiveCaches = clearPrimitiveCaches;
 exports.clearPrimitiveCaches = clearPrimitiveCaches;
 exports.isCmd = isCmd;
 exports.isCmd = isCmd;
 exports.isDict = isDict;
 exports.isDict = isDict;
@@ -3662,12 +3786,13 @@ exports.isName = isName;
 exports.isRef = isRef;
 exports.isRef = isRef;
 exports.isRefsEqual = isRefsEqual;
 exports.isRefsEqual = isRefsEqual;
 exports.isStream = isStream;
 exports.isStream = isStream;
-exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF = exports.Dict = exports.Cmd = void 0;
 
 
 var _util = __w_pdfjs_require__(1);
 var _util = __w_pdfjs_require__(1);
 
 
 var _base_stream = __w_pdfjs_require__(7);
 var _base_stream = __w_pdfjs_require__(7);
 
 
+const CIRCULAR_REF = Symbol("CIRCULAR_REF");
+exports.CIRCULAR_REF = CIRCULAR_REF;
 const EOF = Symbol("EOF");
 const EOF = Symbol("EOF");
 exports.EOF = EOF;
 exports.EOF = EOF;
 
 
@@ -4927,6 +5052,10 @@ class CCITTFaxDecoder {
       c = 0;
       c = 0;
 
 
       do {
       do {
+        if (typeof this.outputBits !== "number") {
+          throw new _util.FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.');
+        }
+
         if (this.outputBits > bits) {
         if (this.outputBits > bits) {
           c <<= bits;
           c <<= bits;
 
 
@@ -6387,7 +6516,7 @@ class JpegImage {
       y = data[i + 2];
       y = data[i + 2];
       k = data[i + 3];
       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.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.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.0003189131175883281 * 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);
       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);
     }
     }
 
 
@@ -8818,34 +8947,34 @@ var exports = __webpack_exports__;
 Object.defineProperty(exports, "__esModule", ({
 Object.defineProperty(exports, "__esModule", ({
   value: true
   value: true
 }));
 }));
-Object.defineProperty(exports, "getVerbosityLevel", ({
+Object.defineProperty(exports, "Jbig2Image", ({
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.getVerbosityLevel;
+    return _jbig.Jbig2Image;
   }
   }
 }));
 }));
-Object.defineProperty(exports, "setVerbosityLevel", ({
+Object.defineProperty(exports, "JpegImage", ({
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.setVerbosityLevel;
+    return _jpg.JpegImage;
   }
   }
 }));
 }));
-Object.defineProperty(exports, "Jbig2Image", ({
+Object.defineProperty(exports, "JpxImage", ({
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _jbig.Jbig2Image;
+    return _jpx.JpxImage;
   }
   }
 }));
 }));
-Object.defineProperty(exports, "JpegImage", ({
+Object.defineProperty(exports, "getVerbosityLevel", ({
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _jpg.JpegImage;
+    return _util.getVerbosityLevel;
   }
   }
 }));
 }));
-Object.defineProperty(exports, "JpxImage", ({
+Object.defineProperty(exports, "setVerbosityLevel", ({
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _jpx.JpxImage;
+    return _util.setVerbosityLevel;
   }
   }
 }));
 }));
 
 
@@ -8857,8 +8986,8 @@ var _jpg = __w_pdfjs_require__(10);
 
 
 var _jpx = __w_pdfjs_require__(11);
 var _jpx = __w_pdfjs_require__(11);
 
 
-const pdfjsVersion = '2.11.338';
-const pdfjsBuild = 'dedff3c98';
+const pdfjsVersion = '2.12.313';
+const pdfjsBuild = 'a2ae56f39';
 })();
 })();
 
 
 /******/ 	return __webpack_exports__;
 /******/ 	return __webpack_exports__;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
image_decoders/pdf.image_decoders.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
image_decoders/pdf.image_decoders.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 416 - 191
legacy/build/pdf.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 2
legacy/build/pdf.sandbox.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.sandbox.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.sandbox.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 359 - 176
legacy/build/pdf.worker.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.worker.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/build/pdf.worker.min.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 412 - 187
legacy/image_decoders/pdf.image_decoders.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/image_decoders/pdf.image_decoders.js.map


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/image_decoders/pdf.image_decoders.min.js


+ 59 - 20
legacy/web/pdf_viewer.css

@@ -23,6 +23,9 @@
   overflow: hidden;
   overflow: hidden;
   opacity: 0.2;
   opacity: 0.2;
   line-height: 1;
   line-height: 1;
+  -webkit-text-size-adjust: none;
+     -moz-text-size-adjust: none;
+          text-size-adjust: none;
 }
 }
 
 
 .textLayer span,
 .textLayer span,
@@ -34,6 +37,13 @@
   transform-origin: 0% 0%;
   transform-origin: 0% 0%;
 }
 }
 
 
+/* Only necessary in Google Chrome, see issue 14205, and most unfortunately
+ * the problem doesn't show up in "text" reference tests. */
+.textLayer span.markedContent {
+  top: 0;
+  height: 0;
+}
+
 .textLayer .highlight {
 .textLayer .highlight {
   margin: -1px;
   margin: -1px;
   padding: 1px;
   padding: 1px;
@@ -115,6 +125,13 @@
   height: 100%;
   height: 100%;
 }
 }
 
 
+.annotationLayer .buttonWidgetAnnotation.pushButton > canvas {
+  position: relative;
+  top: 0;
+  left: 0;
+  z-index: -1;
+}
+
 .annotationLayer .linkAnnotation > a:hover,
 .annotationLayer .linkAnnotation > a:hover,
 .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
 .annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
   opacity: 0.2;
   opacity: 0.2;
@@ -230,6 +247,16 @@
   padding-right: 0;
   padding-right: 0;
 }
 }
 
 
+.annotationLayer .textWidgetAnnotation input.comb:focus {
+  /*
+   * Letter spacing is placed on the right side of each character. Hence, the
+   * letter spacing of the last character may be placed outside the visible
+   * area, causing horizontal scrolling. We avoid this by extending the width
+   * when the element has focus and revert this when it loses focus.
+   */
+  width: 103%;
+}
+
 .annotationLayer .buttonWidgetAnnotation.checkBox input,
 .annotationLayer .buttonWidgetAnnotation.checkBox input,
 .annotationLayer .buttonWidgetAnnotation.radioButton input {
 .annotationLayer .buttonWidgetAnnotation.radioButton input {
   -webkit-appearance: none;
   -webkit-appearance: none;
@@ -267,17 +294,21 @@
   display: inline-block;
   display: inline-block;
 }
 }
 
 
-.annotationLayer .popup span {
+.annotationLayer .popupDate {
   display: inline-block;
   display: inline-block;
   margin-left: 5px;
   margin-left: 5px;
 }
 }
 
 
-.annotationLayer .popup p {
+.annotationLayer .popupContent {
   border-top: 1px solid rgba(51, 51, 51, 1);
   border-top: 1px solid rgba(51, 51, 51, 1);
   margin-top: 2px;
   margin-top: 2px;
   padding-top: 2px;
   padding-top: 2px;
 }
 }
 
 
+.annotationLayer .richText > * {
+  white-space: pre-wrap;
+}
+
 .annotationLayer .highlightAnnotation,
 .annotationLayer .highlightAnnotation,
 .annotationLayer .underlineAnnotation,
 .annotationLayer .underlineAnnotation,
 .annotationLayer .squigglyAnnotation,
 .annotationLayer .squigglyAnnotation,
@@ -510,6 +541,9 @@
 .xfaLink {
 .xfaLink {
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
 }
 }
 
 
 .xfaCheckbox,
 .xfaCheckbox,
@@ -620,11 +654,12 @@
 }
 }
 
 
 :root {
 :root {
-  --pdfViewer-padding-bottom: none;
+  --pdfViewer-padding-bottom: 0;
   --page-margin: 1px auto -8px;
   --page-margin: 1px auto -8px;
   --page-border: 9px solid transparent;
   --page-border: 9px solid transparent;
   --spreadHorizontalWrapped-margin-LR: -3.5px;
   --spreadHorizontalWrapped-margin-LR: -3.5px;
   --zoom-factor: 1;
   --zoom-factor: 1;
+  --viewport-scale-factor: 1;
 }
 }
 
 
 @media screen and (forced-colors: active) {
 @media screen and (forced-colors: active) {
@@ -658,6 +693,12 @@
   background-color: rgba(255, 255, 255, 1);
   background-color: rgba(255, 255, 255, 1);
 }
 }
 
 
+.pdfViewer .dummyPage {
+  position: relative;
+  width: 0;
+  /* The height is set via JS, see `BaseViewer.#ensurePageViewVisible`. */
+}
+
 .pdfViewer.removePageBorders .page {
 .pdfViewer.removePageBorders .page {
   margin: 0 auto 10px;
   margin: 0 auto 10px;
   border: none;
   border: none;
@@ -693,6 +734,7 @@
 }
 }
 
 
 .spread .page,
 .spread .page,
+.spread .dummyPage,
 .pdfViewer.scrollHorizontal .page,
 .pdfViewer.scrollHorizontal .page,
 .pdfViewer.scrollWrapped .page,
 .pdfViewer.scrollWrapped .page,
 .pdfViewer.scrollHorizontal .spread,
 .pdfViewer.scrollHorizontal .spread,
@@ -733,29 +775,26 @@
   bottom: 0;
   bottom: 0;
   background: url("images/loading-icon.gif") center no-repeat;
   background: url("images/loading-icon.gif") center no-repeat;
 }
 }
-
-.pdfPresentationMode .pdfViewer {
-  margin-left: 0;
-  margin-right: 0;
+.pdfViewer .page .loadingIcon.notVisible {
+  background: none;
 }
 }
 
 
-.pdfPresentationMode .pdfViewer .page,
-.pdfPresentationMode .pdfViewer .spread {
-  display: block;
+.pdfViewer.enablePermissions .textLayer span {
+  -webkit-user-select: none !important;
+     -moz-user-select: none !important;
+          user-select: none !important;
+  cursor: not-allowed;
 }
 }
 
 
-.pdfPresentationMode .pdfViewer .page,
-.pdfPresentationMode .pdfViewer.removePageBorders .page {
-  margin-left: auto;
-  margin-right: auto;
+.pdfPresentationMode .pdfViewer {
+  padding-bottom: 0;
 }
 }
 
 
-.pdfPresentationMode:-webkit-full-screen .pdfViewer .page {
-  margin-bottom: 100%;
-  border: 0;
+.pdfPresentationMode .spread {
+  margin: 0;
 }
 }
 
 
-.pdfPresentationMode:fullscreen .pdfViewer .page {
-  margin-bottom: 100%;
-  border: 0;
+.pdfPresentationMode .pdfViewer .page {
+  margin: 0 auto;
+  border: 2px solid transparent;
 }
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 340 - 480
legacy/web/pdf_viewer.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
legacy/web/pdf_viewer.js.map


+ 53 - 20
lib/core/annotation.js

@@ -24,8 +24,8 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.getQuadPoints = getQuadPoints;
 exports.MarkupAnnotation = exports.AnnotationFactory = exports.AnnotationBorderStyle = exports.Annotation = void 0;
 exports.MarkupAnnotation = exports.AnnotationFactory = exports.AnnotationBorderStyle = exports.Annotation = void 0;
+exports.getQuadPoints = getQuadPoints;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -51,6 +51,8 @@ var _stream = require("./stream.js");
 
 
 var _writer = require("./writer.js");
 var _writer = require("./writer.js");
 
 
+var _factory = require("./xfa/factory.js");
+
 class AnnotationFactory {
 class AnnotationFactory {
   static create(xref, ref, pdfManager, idFactory, collectFields) {
   static create(xref, ref, pdfManager, idFactory, 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]));
     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]));
@@ -314,7 +316,8 @@ class Annotation {
       id: params.id,
       id: params.id,
       modificationDate: this.modificationDate,
       modificationDate: this.modificationDate,
       rect: this.rectangle,
       rect: this.rectangle,
-      subtype: params.subtype
+      subtype: params.subtype,
+      hasOwnCanvas: false
     };
     };
 
 
     if (params.collectFields) {
     if (params.collectFields) {
@@ -473,7 +476,7 @@ class Annotation {
         this.borderStyle.setWidth(array[2], this.rectangle);
         this.borderStyle.setWidth(array[2], this.rectangle);
 
 
         if (array.length === 4) {
         if (array.length === 4) {
-          this.borderStyle.setDashArray(array[3]);
+          this.borderStyle.setDashArray(array[3], true);
         }
         }
       }
       }
     } else {
     } else {
@@ -509,8 +512,8 @@ class Annotation {
     this.appearance = normalAppearanceState.get(as.name);
     this.appearance = normalAppearanceState.get(as.name);
   }
   }
 
 
-  loadResources(keys) {
-    return this.appearance.dict.getAsync("Resources").then(resources => {
+  loadResources(keys, appearance) {
+    return appearance.dict.getAsync("Resources").then(resources => {
       if (!resources) {
       if (!resources) {
         return undefined;
         return undefined;
       }
       }
@@ -522,21 +525,28 @@ class Annotation {
     });
     });
   }
   }
 
 
-  getOperatorList(evaluator, task, renderForms, annotationStorage) {
-    if (!this.appearance) {
-      return Promise.resolve(new _operator_list.OperatorList());
+  getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
+    const data = this.data;
+    let appearance = this.appearance;
+    const isUsingOwnCanvas = data.hasOwnCanvas && intent & _util.RenderingIntentFlag.DISPLAY;
+
+    if (!appearance) {
+      if (!isUsingOwnCanvas) {
+        return Promise.resolve(new _operator_list.OperatorList());
+      }
+
+      appearance = new _stream.StringStream("");
+      appearance.dict = new _primitives.Dict();
     }
     }
 
 
-    const appearance = this.appearance;
-    const data = this.data;
     const appearanceDict = appearance.dict;
     const appearanceDict = appearance.dict;
-    const resourcesPromise = this.loadResources(["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"]);
+    const resourcesPromise = this.loadResources(["ExtGState", "ColorSpace", "Pattern", "Shading", "XObject", "Font"], appearance);
     const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1];
     const bbox = appearanceDict.getArray("BBox") || [0, 0, 1, 1];
     const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0];
     const matrix = appearanceDict.getArray("Matrix") || [1, 0, 0, 1, 0, 0];
     const transform = getTransformMatrix(data.rect, bbox, matrix);
     const transform = getTransformMatrix(data.rect, bbox, matrix);
     return resourcesPromise.then(resources => {
     return resourcesPromise.then(resources => {
       const opList = new _operator_list.OperatorList();
       const opList = new _operator_list.OperatorList();
-      opList.addOp(_util.OPS.beginAnnotation, [data.id, data.rect, transform, matrix]);
+      opList.addOp(_util.OPS.beginAnnotation, [data.id, data.rect, transform, matrix, isUsingOwnCanvas]);
       return evaluator.getOperatorList({
       return evaluator.getOperatorList({
         stream: appearance,
         stream: appearance,
         task,
         task,
@@ -685,7 +695,7 @@ class AnnotationBorderStyle {
     }
     }
   }
   }
 
 
-  setDashArray(dashArray) {
+  setDashArray(dashArray, forceStyle = false) {
     if (Array.isArray(dashArray) && dashArray.length > 0) {
     if (Array.isArray(dashArray) && dashArray.length > 0) {
       let isValid = true;
       let isValid = true;
       let allZeros = true;
       let allZeros = true;
@@ -703,6 +713,10 @@ class AnnotationBorderStyle {
 
 
       if (isValid && !allZeros) {
       if (isValid && !allZeros) {
         this.dashArray = dashArray;
         this.dashArray = dashArray;
+
+        if (forceStyle) {
+          this.setStyle(_primitives.Name.get("D"));
+        }
       } else {
       } else {
         this.width = 0;
         this.width = 0;
       }
       }
@@ -778,6 +792,10 @@ class MarkupAnnotation extends Annotation {
         this.data.color = null;
         this.data.color = null;
       }
       }
     }
     }
+
+    if (dict.has("RC")) {
+      this.data.richText = _factory.XFAFactory.getRichTextAsHtml(dict.get("RC"));
+    }
   }
   }
 
 
   setCreationDate(creationDate) {
   setCreationDate(creationDate) {
@@ -968,18 +986,18 @@ class WidgetAnnotation extends Annotation {
     return !!(this.data.fieldFlags & flag);
     return !!(this.data.fieldFlags & flag);
   }
   }
 
 
-  getOperatorList(evaluator, task, renderForms, annotationStorage) {
+  getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
     if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
     if (renderForms && !(this instanceof SignatureWidgetAnnotation)) {
       return Promise.resolve(new _operator_list.OperatorList());
       return Promise.resolve(new _operator_list.OperatorList());
     }
     }
 
 
     if (!this._hasText) {
     if (!this._hasText) {
-      return super.getOperatorList(evaluator, task, renderForms, annotationStorage);
+      return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
     }
     }
 
 
     return this._getAppearance(evaluator, task, annotationStorage).then(content => {
     return this._getAppearance(evaluator, task, annotationStorage).then(content => {
       if (this.appearance && content === null) {
       if (this.appearance && content === null) {
-        return super.getOperatorList(evaluator, task, renderForms, annotationStorage);
+        return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
       }
       }
 
 
       const operatorList = new _operator_list.OperatorList();
       const operatorList = new _operator_list.OperatorList();
@@ -1429,15 +1447,17 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
     } else if (this.data.radioButton) {
     } else if (this.data.radioButton) {
       this._processRadioButton(params);
       this._processRadioButton(params);
     } else if (this.data.pushButton) {
     } else if (this.data.pushButton) {
+      this.data.hasOwnCanvas = true;
+
       this._processPushButton(params);
       this._processPushButton(params);
     } else {
     } else {
       (0, _util.warn)("Invalid field flags for button widget annotation");
       (0, _util.warn)("Invalid field flags for button widget annotation");
     }
     }
   }
   }
 
 
-  async getOperatorList(evaluator, task, renderForms, annotationStorage) {
+  async getOperatorList(evaluator, task, intent, renderForms, annotationStorage) {
     if (this.data.pushButton) {
     if (this.data.pushButton) {
-      return super.getOperatorList(evaluator, task, false, annotationStorage);
+      return super.getOperatorList(evaluator, task, intent, false, annotationStorage);
     }
     }
 
 
     let value = null;
     let value = null;
@@ -1449,7 +1469,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
 
 
     if (value === null) {
     if (value === null) {
       if (this.appearance) {
       if (this.appearance) {
-        return super.getOperatorList(evaluator, task, renderForms, annotationStorage);
+        return super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
       }
       }
 
 
       if (this.data.checkBox) {
       if (this.data.checkBox) {
@@ -1464,7 +1484,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
     if (appearance) {
     if (appearance) {
       const savedAppearance = this.appearance;
       const savedAppearance = this.appearance;
       this.appearance = appearance;
       this.appearance = appearance;
-      const operatorList = super.getOperatorList(evaluator, task, renderForms, annotationStorage);
+      const operatorList = super.getOperatorList(evaluator, task, intent, renderForms, annotationStorage);
       this.appearance = savedAppearance;
       this.appearance = savedAppearance;
       return operatorList;
       return operatorList;
     }
     }
@@ -1997,6 +2017,10 @@ class PopupAnnotation extends Annotation {
     this.data.titleObj = this._title;
     this.data.titleObj = this._title;
     this.setContents(parentItem.get("Contents"));
     this.setContents(parentItem.get("Contents"));
     this.data.contentsObj = this._contents;
     this.data.contentsObj = this._contents;
+
+    if (parentItem.has("RC")) {
+      this.data.richText = _factory.XFAFactory.getRichTextAsHtml(parentItem.get("RC"));
+    }
   }
   }
 
 
 }
 }
@@ -2071,6 +2095,10 @@ class SquareAnnotation extends MarkupAnnotation {
 
 
       const fillAlpha = fillColor ? strokeAlpha : null;
       const fillAlpha = fillColor ? strokeAlpha : null;
 
 
+      if (this.borderStyle.width === 0 && !fillColor) {
+        return;
+      }
+
       this._setDefaultAppearance({
       this._setDefaultAppearance({
         xref: parameters.xref,
         xref: parameters.xref,
         extra: `${this.borderStyle.width} w`,
         extra: `${this.borderStyle.width} w`,
@@ -2116,6 +2144,11 @@ class CircleAnnotation extends MarkupAnnotation {
       }
       }
 
 
       const fillAlpha = fillColor ? strokeAlpha : null;
       const fillAlpha = fillColor ? strokeAlpha : null;
+
+      if (this.borderStyle.width === 0 && !fillColor) {
+        return;
+      }
+
       const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));
       const controlPointsDistance = 4 / 3 * Math.tan(Math.PI / (2 * 4));
 
 
       this._setDefaultAppearance({
       this._setDefaultAppearance({

+ 1 - 1
lib/core/bidi.js

@@ -128,7 +128,7 @@ function bidi(str, startLevel = -1, vertical = false) {
   }
   }
 
 
   if (startLevel === -1) {
   if (startLevel === -1) {
-    if (numBidi / strLength < 0.3) {
+    if (numBidi / strLength < 0.3 && strLength > 4) {
       isLTR = true;
       isLTR = true;
       startLevel = 0;
       startLevel = 0;
     } else {
     } else {

+ 193 - 40
lib/core/catalog.js

@@ -34,6 +34,8 @@ var _util = require("../shared/util.js");
 
 
 var _name_number_tree = require("./name_number_tree.js");
 var _name_number_tree = require("./name_number_tree.js");
 
 
+var _base_stream = require("./base_stream.js");
+
 var _colorspace = require("./colorspace.js");
 var _colorspace = require("./colorspace.js");
 
 
 var _file_spec = require("./file_spec.js");
 var _file_spec = require("./file_spec.js");
@@ -58,10 +60,12 @@ class Catalog {
     this.xref = xref;
     this.xref = xref;
     this._catDict = xref.getCatalogObj();
     this._catDict = xref.getCatalogObj();
 
 
-    if (!(0, _primitives.isDict)(this._catDict)) {
+    if (!(this._catDict instanceof _primitives.Dict)) {
       throw new _util.FormatError("Catalog object is not a dictionary.");
       throw new _util.FormatError("Catalog object is not a dictionary.");
     }
     }
 
 
+    this.toplevelPagesDict;
+    this._actualNumPages = null;
     this.fontCache = new _primitives.RefSetCache();
     this.fontCache = new _primitives.RefSetCache();
     this.builtInCMapCache = new Map();
     this.builtInCMapCache = new Map();
     this.standardFontDataCache = new Map();
     this.standardFontDataCache = new Map();
@@ -74,21 +78,19 @@ class Catalog {
   get version() {
   get version() {
     const version = this._catDict.get("Version");
     const version = this._catDict.get("Version");
 
 
-    if (!(0, _primitives.isName)(version)) {
-      return (0, _util.shadow)(this, "version", null);
-    }
+    return (0, _util.shadow)(this, "version", version instanceof _primitives.Name ? version.name : null);
+  }
 
 
-    return (0, _util.shadow)(this, "version", version.name);
+  get lang() {
+    const lang = this._catDict.get("Lang");
+
+    return (0, _util.shadow)(this, "lang", typeof lang === "string" ? (0, _util.stringToPDFString)(lang) : null);
   }
   }
 
 
   get needsRendering() {
   get needsRendering() {
     const needsRendering = this._catDict.get("NeedsRendering");
     const needsRendering = this._catDict.get("NeedsRendering");
 
 
-    if (!(0, _util.isBool)(needsRendering)) {
-      return (0, _util.shadow)(this, "needsRendering", false);
-    }
-
-    return (0, _util.shadow)(this, "needsRendering", needsRendering);
+    return (0, _util.shadow)(this, "needsRendering", typeof needsRendering === "boolean" ? needsRendering : false);
   }
   }
 
 
   get collection() {
   get collection() {
@@ -140,33 +142,34 @@ class Catalog {
   get metadata() {
   get metadata() {
     const streamRef = this._catDict.getRaw("Metadata");
     const streamRef = this._catDict.getRaw("Metadata");
 
 
-    if (!(0, _primitives.isRef)(streamRef)) {
+    if (!(streamRef instanceof _primitives.Ref)) {
       return (0, _util.shadow)(this, "metadata", null);
       return (0, _util.shadow)(this, "metadata", null);
     }
     }
 
 
-    const suppressEncryption = !(this.xref.encrypt && this.xref.encrypt.encryptMetadata);
-    const stream = this.xref.fetch(streamRef, suppressEncryption);
     let metadata = null;
     let metadata = null;
 
 
-    if ((0, _primitives.isStream)(stream) && (0, _primitives.isDict)(stream.dict)) {
-      const type = stream.dict.get("Type");
-      const subtype = stream.dict.get("Subtype");
+    try {
+      const suppressEncryption = !(this.xref.encrypt && this.xref.encrypt.encryptMetadata);
+      const stream = this.xref.fetch(streamRef, suppressEncryption);
 
 
-      if ((0, _primitives.isName)(type, "Metadata") && (0, _primitives.isName)(subtype, "XML")) {
-        try {
+      if (stream instanceof _base_stream.BaseStream && stream.dict instanceof _primitives.Dict) {
+        const type = stream.dict.get("Type");
+        const subtype = stream.dict.get("Subtype");
+
+        if ((0, _primitives.isName)(type, "Metadata") && (0, _primitives.isName)(subtype, "XML")) {
           const data = (0, _util.stringToUTF8String)(stream.getString());
           const data = (0, _util.stringToUTF8String)(stream.getString());
 
 
           if (data) {
           if (data) {
             metadata = new _metadata_parser.MetadataParser(data).serializable;
             metadata = new _metadata_parser.MetadataParser(data).serializable;
           }
           }
-        } catch (e) {
-          if (e instanceof _core_utils.MissingDataException) {
-            throw e;
-          }
-
-          (0, _util.info)("Skipping invalid metadata.");
         }
         }
       }
       }
+    } catch (ex) {
+      if (ex instanceof _core_utils.MissingDataException) {
+        throw ex;
+      }
+
+      (0, _util.info)(`Skipping invalid Metadata: "${ex}".`);
     }
     }
 
 
     return (0, _util.shadow)(this, "metadata", metadata);
     return (0, _util.shadow)(this, "metadata", metadata);
@@ -568,14 +571,26 @@ class Catalog {
     };
     };
   }
   }
 
 
-  get numPages() {
+  setActualNumPages(num = null) {
+    this._actualNumPages = num;
+  }
+
+  get hasActualNumPages() {
+    return this._actualNumPages !== null;
+  }
+
+  get _pagesCount() {
     const obj = this.toplevelPagesDict.get("Count");
     const obj = this.toplevelPagesDict.get("Count");
 
 
     if (!Number.isInteger(obj)) {
     if (!Number.isInteger(obj)) {
       throw new _util.FormatError("Page count in top-level pages dictionary is not an integer.");
       throw new _util.FormatError("Page count in top-level pages dictionary is not an integer.");
     }
     }
 
 
-    return (0, _util.shadow)(this, "numPages", obj);
+    return (0, _util.shadow)(this, "_pagesCount", obj);
+  }
+
+  get numPages() {
+    return this.hasActualNumPages ? this._actualNumPages : this._pagesCount;
   }
   }
 
 
   get destinations() {
   get destinations() {
@@ -1152,21 +1167,27 @@ class Catalog {
 
 
   getPageDict(pageIndex) {
   getPageDict(pageIndex) {
     const capability = (0, _util.createPromiseCapability)();
     const capability = (0, _util.createPromiseCapability)();
-    const nodesToVisit = [this._catDict.getRaw("Pages")];
+    const nodesToVisit = [this.toplevelPagesDict];
     const visitedNodes = new _primitives.RefSet();
     const visitedNodes = new _primitives.RefSet();
+
+    const pagesRef = this._catDict.getRaw("Pages");
+
+    if (pagesRef instanceof _primitives.Ref) {
+      visitedNodes.put(pagesRef);
+    }
+
     const xref = this.xref,
     const xref = this.xref,
           pageKidsCountCache = this.pageKidsCountCache;
           pageKidsCountCache = this.pageKidsCountCache;
-    let count,
-        currentPageIndex = 0;
+    let currentPageIndex = 0;
 
 
     function next() {
     function next() {
       while (nodesToVisit.length) {
       while (nodesToVisit.length) {
         const currentNode = nodesToVisit.pop();
         const currentNode = nodesToVisit.pop();
 
 
-        if ((0, _primitives.isRef)(currentNode)) {
-          count = pageKidsCountCache.get(currentNode);
+        if (currentNode instanceof _primitives.Ref) {
+          const count = pageKidsCountCache.get(currentNode);
 
 
-          if (count > 0 && currentPageIndex + count < pageIndex) {
+          if (count >= 0 && currentPageIndex + count <= pageIndex) {
             currentPageIndex += count;
             currentPageIndex += count;
             continue;
             continue;
           }
           }
@@ -1179,11 +1200,11 @@ class Catalog {
           visitedNodes.put(currentNode);
           visitedNodes.put(currentNode);
           xref.fetchAsync(currentNode).then(function (obj) {
           xref.fetchAsync(currentNode).then(function (obj) {
             if ((0, _primitives.isDict)(obj, "Page") || (0, _primitives.isDict)(obj) && !obj.has("Kids")) {
             if ((0, _primitives.isDict)(obj, "Page") || (0, _primitives.isDict)(obj) && !obj.has("Kids")) {
-              if (pageIndex === currentPageIndex) {
-                if (currentNode && !pageKidsCountCache.has(currentNode)) {
-                  pageKidsCountCache.put(currentNode, 1);
-                }
+              if (currentNode && !pageKidsCountCache.has(currentNode)) {
+                pageKidsCountCache.put(currentNode, 1);
+              }
 
 
+              if (pageIndex === currentPageIndex) {
                 capability.resolve([obj, currentNode]);
                 capability.resolve([obj, currentNode]);
               } else {
               } else {
                 currentPageIndex++;
                 currentPageIndex++;
@@ -1199,12 +1220,20 @@ class Catalog {
           return;
           return;
         }
         }
 
 
-        if (!(0, _primitives.isDict)(currentNode)) {
+        if (!(currentNode instanceof _primitives.Dict)) {
           capability.reject(new _util.FormatError("Page dictionary kid reference points to wrong type of object."));
           capability.reject(new _util.FormatError("Page dictionary kid reference points to wrong type of object."));
           return;
           return;
         }
         }
 
 
-        count = currentNode.get("Count");
+        let count;
+
+        try {
+          count = currentNode.get("Count");
+        } catch (ex) {
+          if (ex instanceof _core_utils.MissingDataException) {
+            throw ex;
+          }
+        }
 
 
         if (Number.isInteger(count) && count >= 0) {
         if (Number.isInteger(count) && count >= 0) {
           const objId = currentNode.objId;
           const objId = currentNode.objId;
@@ -1219,10 +1248,28 @@ class Catalog {
           }
           }
         }
         }
 
 
-        const kids = currentNode.get("Kids");
+        let kids;
+
+        try {
+          kids = currentNode.get("Kids");
+        } catch (ex) {
+          if (ex instanceof _core_utils.MissingDataException) {
+            throw ex;
+          }
+        }
 
 
         if (!Array.isArray(kids)) {
         if (!Array.isArray(kids)) {
-          if ((0, _primitives.isName)(currentNode.get("Type"), "Page") || !currentNode.has("Type") && currentNode.has("Contents")) {
+          let type;
+
+          try {
+            type = currentNode.get("Type");
+          } catch (ex) {
+            if (ex instanceof _core_utils.MissingDataException) {
+              throw ex;
+            }
+          }
+
+          if ((0, _primitives.isName)(type, "Page") || !currentNode.has("Type") && currentNode.has("Contents")) {
             if (currentPageIndex === pageIndex) {
             if (currentPageIndex === pageIndex) {
               capability.resolve([currentNode, null]);
               capability.resolve([currentNode, null]);
               return;
               return;
@@ -1248,6 +1295,112 @@ class Catalog {
     return capability.promise;
     return capability.promise;
   }
   }
 
 
+  getAllPageDicts(recoveryMode = false) {
+    const queue = [{
+      currentNode: this.toplevelPagesDict,
+      posInKids: 0
+    }];
+    const visitedNodes = new _primitives.RefSet();
+
+    const pagesRef = this._catDict.getRaw("Pages");
+
+    if (pagesRef instanceof _primitives.Ref) {
+      visitedNodes.put(pagesRef);
+    }
+
+    const map = new Map();
+    let pageIndex = 0;
+
+    function addPageDict(pageDict, pageRef) {
+      map.set(pageIndex++, [pageDict, pageRef]);
+    }
+
+    function addPageError(error) {
+      map.set(pageIndex++, [error, null]);
+    }
+
+    while (queue.length > 0) {
+      const queueItem = queue[queue.length - 1];
+      const {
+        currentNode,
+        posInKids
+      } = queueItem;
+      let kids;
+
+      try {
+        kids = currentNode.get("Kids");
+      } catch (ex) {
+        if (ex instanceof _core_utils.MissingDataException) {
+          throw ex;
+        }
+
+        if (ex instanceof _core_utils.XRefEntryException && !recoveryMode) {
+          throw ex;
+        }
+
+        addPageError(ex);
+        break;
+      }
+
+      if (!Array.isArray(kids)) {
+        addPageError(new _util.FormatError("Page dictionary kids object is not an array."));
+        break;
+      }
+
+      if (posInKids >= kids.length) {
+        queue.pop();
+        continue;
+      }
+
+      const kidObj = kids[posInKids];
+      let obj;
+
+      if (kidObj instanceof _primitives.Ref) {
+        try {
+          obj = this.xref.fetch(kidObj);
+        } catch (ex) {
+          if (ex instanceof _core_utils.MissingDataException) {
+            throw ex;
+          }
+
+          if (ex instanceof _core_utils.XRefEntryException && !recoveryMode) {
+            throw ex;
+          }
+
+          addPageError(ex);
+          break;
+        }
+
+        if (visitedNodes.has(kidObj)) {
+          addPageError(new _util.FormatError("Pages tree contains circular reference."));
+          break;
+        }
+
+        visitedNodes.put(kidObj);
+      } else {
+        obj = kidObj;
+      }
+
+      if (!(obj instanceof _primitives.Dict)) {
+        addPageError(new _util.FormatError("Page dictionary kid reference points to wrong type of object."));
+        break;
+      }
+
+      if ((0, _primitives.isDict)(obj, "Page") || !obj.has("Kids")) {
+        addPageDict(obj, kidObj instanceof _primitives.Ref ? kidObj : null);
+      } else {
+        queue.push({
+          currentNode: obj,
+          posInKids: 0
+        });
+      }
+
+      queueItem.posInKids++;
+    }
+
+    return map;
+  }
+
   getPageIndex(pageRef) {
   getPageIndex(pageRef) {
     const cachedPageIndex = this.pageIndexCache.get(pageRef);
     const cachedPageIndex = this.pageIndexCache.get(pageRef);
 
 

+ 4 - 0
lib/core/ccitt.js

@@ -450,6 +450,10 @@ class CCITTFaxDecoder {
       c = 0;
       c = 0;
 
 
       do {
       do {
+        if (typeof this.outputBits !== "number") {
+          throw new _util.FormatError('Invalid /CCITTFaxDecode data, "outputBits" must be a number.');
+        }
+
         if (this.outputBits > bits) {
         if (this.outputBits > bits) {
           c <<= bits;
           c <<= bits;
 
 

+ 11 - 3
lib/core/chunked_stream.js

@@ -105,6 +105,10 @@ class ChunkedStream extends _stream.Stream {
 
 
     const chunk = Math.floor(pos / this.chunkSize);
     const chunk = Math.floor(pos / this.chunkSize);
 
 
+    if (chunk > this.numChunks) {
+      return;
+    }
+
     if (chunk === this.lastSuccessfulEnsureByteChunk) {
     if (chunk === this.lastSuccessfulEnsureByteChunk) {
       return;
       return;
     }
     }
@@ -125,9 +129,13 @@ class ChunkedStream extends _stream.Stream {
       return;
       return;
     }
     }
 
 
-    const chunkSize = this.chunkSize;
-    const beginChunk = Math.floor(begin / chunkSize);
-    const endChunk = Math.floor((end - 1) / chunkSize) + 1;
+    const beginChunk = Math.floor(begin / this.chunkSize);
+
+    if (beginChunk > this.numChunks) {
+      return;
+    }
+
+    const endChunk = Math.min(Math.floor((end - 1) / this.chunkSize) + 1, this.numChunks);
 
 
     for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
     for (let chunk = beginChunk; chunk < endChunk; ++chunk) {
       if (!this._loadedChunks.has(chunk)) {
       if (!this._loadedChunks.has(chunk)) {

+ 8 - 1
lib/core/cmap.js

@@ -74,7 +74,14 @@ class CMap {
 
 
     while (low <= high) {
     while (low <= high) {
       this._map[low++] = dstLow;
       this._map[low++] = dstLow;
-      dstLow = dstLow.substring(0, lastByte) + String.fromCharCode(dstLow.charCodeAt(lastByte) + 1);
+      const nextCharCode = dstLow.charCodeAt(lastByte) + 1;
+
+      if (nextCharCode > 0xff) {
+        dstLow = dstLow.substring(0, lastByte - 1) + String.fromCharCode(dstLow.charCodeAt(lastByte - 1) + 1) + "\x00";
+        continue;
+      }
+
+      dstLow = dstLow.substring(0, lastByte) + String.fromCharCode(nextCharCode);
     }
     }
   }
   }
 
 

+ 8 - 8
lib/core/colorspace.js

@@ -260,16 +260,16 @@ class ColorSpace {
 
 
     if ((0, _primitives.isName)(cs)) {
     if ((0, _primitives.isName)(cs)) {
       switch (cs.name) {
       switch (cs.name) {
-        case "DeviceGray":
         case "G":
         case "G":
+        case "DeviceGray":
           return this.singletons.gray;
           return this.singletons.gray;
 
 
-        case "DeviceRGB":
         case "RGB":
         case "RGB":
+        case "DeviceRGB":
           return this.singletons.rgb;
           return this.singletons.rgb;
 
 
-        case "DeviceCMYK":
         case "CMYK":
         case "CMYK":
+        case "DeviceCMYK":
           return this.singletons.cmyk;
           return this.singletons.cmyk;
 
 
         case "Pattern":
         case "Pattern":
@@ -302,16 +302,16 @@ class ColorSpace {
       let params, numComps, baseCS, whitePoint, blackPoint, gamma;
       let params, numComps, baseCS, whitePoint, blackPoint, gamma;
 
 
       switch (mode) {
       switch (mode) {
-        case "DeviceGray":
         case "G":
         case "G":
+        case "DeviceGray":
           return this.singletons.gray;
           return this.singletons.gray;
 
 
-        case "DeviceRGB":
         case "RGB":
         case "RGB":
+        case "DeviceRGB":
           return this.singletons.rgb;
           return this.singletons.rgb;
 
 
-        case "DeviceCMYK":
         case "CMYK":
         case "CMYK":
+        case "DeviceCMYK":
           return this.singletons.cmyk;
           return this.singletons.cmyk;
 
 
         case "CalGray":
         case "CalGray":
@@ -364,8 +364,8 @@ class ColorSpace {
 
 
           return new PatternCS(baseCS);
           return new PatternCS(baseCS);
 
 
-        case "Indexed":
         case "I":
         case "I":
+        case "Indexed":
           baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory);
           baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory);
           const hiVal = xref.fetchIfRef(cs[2]) + 1;
           const hiVal = xref.fetchIfRef(cs[2]) + 1;
           const lookup = xref.fetchIfRef(cs[3]);
           const lookup = xref.fetchIfRef(cs[3]);
@@ -678,7 +678,7 @@ const CalGrayCS = function CalGrayCSClosure() {
     const A = src[srcOffset] * scale;
     const A = src[srcOffset] * scale;
     const AG = A ** cs.G;
     const AG = A ** cs.G;
     const L = cs.YW * AG;
     const L = cs.YW * AG;
-    const val = Math.max(295.8 * L ** 0.333333333333333333 - 40.8, 0);
+    const val = Math.max(295.8 * L ** 0.3333333333333333 - 40.8, 0);
     dest[destOffset] = val;
     dest[destOffset] = val;
     dest[destOffset + 1] = val;
     dest[destOffset + 1] = val;
     dest[destOffset + 2] = val;
     dest[destOffset + 2] = val;

+ 50 - 1
lib/core/core_utils.js

@@ -24,6 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = exports.DocStats = void 0;
 exports.collectActions = collectActions;
 exports.collectActions = collectActions;
 exports.encodeToXmlString = encodeToXmlString;
 exports.encodeToXmlString = encodeToXmlString;
 exports.escapePDFName = escapePDFName;
 exports.escapePDFName = escapePDFName;
@@ -39,7 +40,6 @@ exports.readUint32 = readUint32;
 exports.recoverJsURL = recoverJsURL;
 exports.recoverJsURL = recoverJsURL;
 exports.toRomanNumerals = toRomanNumerals;
 exports.toRomanNumerals = toRomanNumerals;
 exports.validateCSSFont = validateCSSFont;
 exports.validateCSSFont = validateCSSFont;
-exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -115,6 +115,55 @@ class XRefParseException extends _util.BaseException {
 
 
 exports.XRefParseException = XRefParseException;
 exports.XRefParseException = XRefParseException;
 
 
+class DocStats {
+  constructor(handler) {
+    this._handler = handler;
+    this._streamTypes = new Set();
+    this._fontTypes = new Set();
+  }
+
+  _send() {
+    const streamTypes = Object.create(null),
+          fontTypes = Object.create(null);
+
+    for (const type of this._streamTypes) {
+      streamTypes[type] = true;
+    }
+
+    for (const type of this._fontTypes) {
+      fontTypes[type] = true;
+    }
+
+    this._handler.send("DocStats", {
+      streamTypes,
+      fontTypes
+    });
+  }
+
+  addStreamType(type) {
+    if (this._streamTypes.has(type)) {
+      return;
+    }
+
+    this._streamTypes.add(type);
+
+    this._send();
+  }
+
+  addFontType(type) {
+    if (this._fontTypes.has(type)) {
+      return;
+    }
+
+    this._fontTypes.add(type);
+
+    this._send();
+  }
+
+}
+
+exports.DocStats = DocStats;
+
 function getInheritableProperty({
 function getInheritableProperty({
   dict,
   dict,
   key,
   key,

+ 3 - 1
lib/core/crypto.js

@@ -24,8 +24,9 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.calculateSHA256 = exports.calculateMD5 = exports.PDF20 = exports.PDF17 = exports.CipherTransformFactory = exports.ARCFourCipher = exports.AES256Cipher = exports.AES128Cipher = void 0;
 exports.calculateSHA384 = calculateSHA384;
 exports.calculateSHA384 = calculateSHA384;
-exports.PDF20 = exports.PDF17 = exports.CipherTransformFactory = exports.calculateSHA512 = exports.calculateSHA256 = exports.calculateMD5 = exports.ARCFourCipher = exports.AES256Cipher = exports.AES128Cipher = void 0;
+exports.calculateSHA512 = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -1486,6 +1487,7 @@ const CipherTransformFactory = function CipherTransformFactoryClosure() {
         throw new _util.FormatError("unknown encryption method");
         throw new _util.FormatError("unknown encryption method");
       }
       }
 
 
+      this.filterName = filter.name;
       this.dict = dict;
       this.dict = dict;
       const algorithm = dict.get("V");
       const algorithm = dict.get("V");
 
 

+ 141 - 58
lib/core/document.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.PDFDocument = exports.Page = void 0;
+exports.Page = exports.PDFDocument = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -235,13 +235,9 @@ class Page {
   }
   }
 
 
   get xfaData() {
   get xfaData() {
-    if (this.xfaFactory) {
-      return (0, _util.shadow)(this, "xfaData", {
-        bbox: this.xfaFactory.getBoundingBox(this.pageIndex)
-      });
-    }
-
-    return (0, _util.shadow)(this, "xfaData", null);
+    return (0, _util.shadow)(this, "xfaData", this.xfaFactory ? {
+      bbox: this.xfaFactory.getBoundingBox(this.pageIndex)
+    } : null);
   }
   }
 
 
   save(handler, task, annotationStorage) {
   save(handler, task, annotationStorage) {
@@ -339,7 +335,7 @@ class Page {
 
 
       for (const annotation of annotations) {
       for (const annotation of annotations) {
         if (intentAny || intentDisplay && annotation.mustBeViewed(annotationStorage) || intentPrint && annotation.mustBePrinted(annotationStorage)) {
         if (intentAny || intentDisplay && annotation.mustBeViewed(annotationStorage) || intentPrint && annotation.mustBePrinted(annotationStorage)) {
-          opListPromises.push(annotation.getOperatorList(partialEvaluator, task, renderForms, annotationStorage).catch(function (reason) {
+          opListPromises.push(annotation.getOperatorList(partialEvaluator, task, intent, renderForms, annotationStorage).catch(function (reason) {
             (0, _util.warn)("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
             (0, _util.warn)("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
             return null;
             return null;
           }));
           }));
@@ -543,7 +539,7 @@ class PDFDocument {
     this.pdfManager = pdfManager;
     this.pdfManager = pdfManager;
     this.stream = stream;
     this.stream = stream;
     this.xref = new _xref.XRef(stream, pdfManager);
     this.xref = new _xref.XRef(stream, pdfManager);
-    this._pagePromises = [];
+    this._pagePromises = new Map();
     this._version = null;
     this._version = null;
     const idCounters = {
     const idCounters = {
       font: 0
       font: 0
@@ -677,12 +673,18 @@ class PDFDocument {
   }
   }
 
 
   get numPages() {
   get numPages() {
-    if (this.xfaFactory) {
-      return (0, _util.shadow)(this, "numPages", this.xfaFactory.numberPages);
+    let num = 0;
+
+    if (this.catalog.hasActualNumPages) {
+      num = this.catalog.numPages;
+    } else if (this.xfaFactory) {
+      num = this.xfaFactory.getNumPages();
+    } else if (this.linearization) {
+      num = this.linearization.numPages;
+    } else {
+      num = this.catalog.numPages;
     }
     }
 
 
-    const linearization = this.linearization;
-    const num = linearization ? linearization.numPages : this.catalog.numPages;
     return (0, _util.shadow)(this, "numPages", num);
     return (0, _util.shadow)(this, "numPages", num);
   }
   }
 
 
@@ -782,24 +784,21 @@ class PDFDocument {
   }
   }
 
 
   get xfaFactory() {
   get xfaFactory() {
+    let data;
+
     if (this.pdfManager.enableXfa && this.catalog.needsRendering && this.formInfo.hasXfa && !this.formInfo.hasAcroForm) {
     if (this.pdfManager.enableXfa && this.catalog.needsRendering && this.formInfo.hasXfa && !this.formInfo.hasAcroForm) {
-      const data = this.xfaData;
-      return (0, _util.shadow)(this, "xfaFactory", data ? new _factory.XFAFactory(data) : null);
+      data = this.xfaData;
     }
     }
 
 
-    return (0, _util.shadow)(this, "xfaFaxtory", null);
+    return (0, _util.shadow)(this, "xfaFactory", data ? new _factory.XFAFactory(data) : null);
   }
   }
 
 
   get isPureXfa() {
   get isPureXfa() {
-    return this.xfaFactory && this.xfaFactory.isValid();
+    return this.xfaFactory ? this.xfaFactory.isValid() : false;
   }
   }
 
 
   get htmlForXfa() {
   get htmlForXfa() {
-    if (this.xfaFactory) {
-      return this.xfaFactory.getPages();
-    }
-
-    return null;
+    return this.xfaFactory ? this.xfaFactory.getPages() : null;
   }
   }
 
 
   async loadXfaImages() {
   async loadXfaImages() {
@@ -971,11 +970,7 @@ class PDFDocument {
   }
   }
 
 
   async serializeXfaData(annotationStorage) {
   async serializeXfaData(annotationStorage) {
-    if (this.xfaFactory) {
-      return this.xfaFactory.serializeData(annotationStorage);
-    }
-
-    return null;
+    return this.xfaFactory ? this.xfaFactory.serializeData(annotationStorage) : null;
   }
   }
 
 
   get formInfo() {
   get formInfo() {
@@ -1036,6 +1031,8 @@ class PDFDocument {
 
 
     const docInfo = {
     const docInfo = {
       PDFFormatVersion: version,
       PDFFormatVersion: version,
+      Language: this.catalog.lang,
+      EncryptFilterName: this.xref.encrypt ? this.xref.encrypt.filterName : null,
       IsLinearized: !!this.linearization,
       IsLinearized: !!this.linearization,
       IsAcroFormPresent: this.formInfo.hasAcroForm,
       IsAcroFormPresent: this.formInfo.hasAcroForm,
       IsXFAPresent: this.formInfo.hasXfa,
       IsXFAPresent: this.formInfo.hasXfa,
@@ -1120,7 +1117,7 @@ class PDFDocument {
     return (0, _util.shadow)(this, "fingerprints", [hexString(hashOriginal), hashModified ? hexString(hashModified) : null]);
     return (0, _util.shadow)(this, "fingerprints", [hexString(hashOriginal), hashModified ? hexString(hashModified) : null]);
   }
   }
 
 
-  _getLinearizationPage(pageIndex) {
+  async _getLinearizationPage(pageIndex) {
     const {
     const {
       catalog,
       catalog,
       linearization
       linearization
@@ -1128,7 +1125,9 @@ class PDFDocument {
 
 
     const ref = _primitives.Ref.get(linearization.objectNumberFirst, 0);
     const ref = _primitives.Ref.get(linearization.objectNumberFirst, 0);
 
 
-    return this.xref.fetchAsync(ref).then(obj => {
+    try {
+      const obj = await this.xref.fetchAsync(ref);
+
       if ((0, _primitives.isDict)(obj, "Page") || (0, _primitives.isDict)(obj) && !obj.has("Type") && obj.has("Contents")) {
       if ((0, _primitives.isDict)(obj, "Page") || (0, _primitives.isDict)(obj) && !obj.has("Type") && obj.has("Contents")) {
         if (ref && !catalog.pageKidsCountCache.has(ref)) {
         if (ref && !catalog.pageKidsCountCache.has(ref)) {
           catalog.pageKidsCountCache.put(ref, 1);
           catalog.pageKidsCountCache.put(ref, 1);
@@ -1137,42 +1136,36 @@ class PDFDocument {
         return [obj, ref];
         return [obj, ref];
       }
       }
 
 
-      throw new _util.FormatError("The Linearization dictionary doesn't point " + "to a valid Page dictionary.");
-    }).catch(reason => {
+      throw new _util.FormatError("The Linearization dictionary doesn't point to a valid Page dictionary.");
+    } catch (reason) {
       (0, _util.info)(reason);
       (0, _util.info)(reason);
       return catalog.getPageDict(pageIndex);
       return catalog.getPageDict(pageIndex);
-    });
+    }
   }
   }
 
 
   getPage(pageIndex) {
   getPage(pageIndex) {
-    if (this._pagePromises[pageIndex] !== undefined) {
-      return this._pagePromises[pageIndex];
+    const cachedPromise = this._pagePromises.get(pageIndex);
+
+    if (cachedPromise) {
+      return cachedPromise;
     }
     }
 
 
     const {
     const {
       catalog,
       catalog,
-      linearization
+      linearization,
+      xfaFactory
     } = this;
     } = this;
+    let promise;
 
 
-    if (this.xfaFactory) {
-      return Promise.resolve(new Page({
-        pdfManager: this.pdfManager,
-        xref: this.xref,
-        pageIndex,
-        pageDict: _primitives.Dict.empty,
-        ref: null,
-        globalIdFactory: this._globalIdFactory,
-        fontCache: catalog.fontCache,
-        builtInCMapCache: catalog.builtInCMapCache,
-        standardFontDataCache: catalog.standardFontDataCache,
-        globalImageCache: catalog.globalImageCache,
-        nonBlendModesSet: catalog.nonBlendModesSet,
-        xfaFactory: this.xfaFactory
-      }));
+    if (xfaFactory) {
+      promise = Promise.resolve([_primitives.Dict.empty, null]);
+    } else if (linearization && linearization.pageFirst === pageIndex) {
+      promise = this._getLinearizationPage(pageIndex);
+    } else {
+      promise = catalog.getPageDict(pageIndex);
     }
     }
 
 
-    const promise = linearization && linearization.pageFirst === pageIndex ? this._getLinearizationPage(pageIndex) : catalog.getPageDict(pageIndex);
-    return this._pagePromises[pageIndex] = promise.then(([pageDict, ref]) => {
+    promise = promise.then(([pageDict, ref]) => {
       return new Page({
       return new Page({
         pdfManager: this.pdfManager,
         pdfManager: this.pdfManager,
         xref: this.xref,
         xref: this.xref,
@@ -1185,19 +1178,109 @@ class PDFDocument {
         standardFontDataCache: catalog.standardFontDataCache,
         standardFontDataCache: catalog.standardFontDataCache,
         globalImageCache: catalog.globalImageCache,
         globalImageCache: catalog.globalImageCache,
         nonBlendModesSet: catalog.nonBlendModesSet,
         nonBlendModesSet: catalog.nonBlendModesSet,
-        xfaFactory: null
+        xfaFactory
       });
       });
     });
     });
+
+    this._pagePromises.set(pageIndex, promise);
+
+    return promise;
   }
   }
 
 
-  checkFirstPage() {
-    return this.getPage(0).catch(async reason => {
+  async checkFirstPage(recoveryMode = false) {
+    if (recoveryMode) {
+      return;
+    }
+
+    try {
+      await this.getPage(0);
+    } catch (reason) {
       if (reason instanceof _core_utils.XRefEntryException) {
       if (reason instanceof _core_utils.XRefEntryException) {
-        this._pagePromises.length = 0;
+        this._pagePromises.delete(0);
+
         await this.cleanup();
         await this.cleanup();
         throw new _core_utils.XRefParseException();
         throw new _core_utils.XRefParseException();
       }
       }
-    });
+    }
+  }
+
+  async checkLastPage(recoveryMode = false) {
+    const {
+      catalog,
+      pdfManager
+    } = this;
+    catalog.setActualNumPages();
+    let numPages;
+
+    try {
+      await Promise.all([pdfManager.ensureDoc("xfaFactory"), pdfManager.ensureDoc("linearization"), pdfManager.ensureCatalog("numPages")]);
+
+      if (this.xfaFactory) {
+        return;
+      } else if (this.linearization) {
+        numPages = this.linearization.numPages;
+      } else {
+        numPages = catalog.numPages;
+      }
+
+      if (!Number.isInteger(numPages)) {
+        throw new _util.FormatError("Page count is not an integer.");
+      } else if (numPages <= 1) {
+        return;
+      }
+
+      await this.getPage(numPages - 1);
+    } catch (reason) {
+      this._pagePromises.delete(numPages - 1);
+
+      await this.cleanup();
+
+      if (reason instanceof _core_utils.XRefEntryException && !recoveryMode) {
+        throw new _core_utils.XRefParseException();
+      }
+
+      (0, _util.warn)(`checkLastPage - invalid /Pages tree /Count: ${numPages}.`);
+      let pagesTree;
+
+      try {
+        pagesTree = await pdfManager.ensureCatalog("getAllPageDicts", [recoveryMode]);
+      } catch (reasonAll) {
+        if (reasonAll instanceof _core_utils.XRefEntryException && !recoveryMode) {
+          throw new _core_utils.XRefParseException();
+        }
+
+        catalog.setActualNumPages(1);
+        return;
+      }
+
+      for (const [pageIndex, [pageDict, ref]] of pagesTree) {
+        let promise;
+
+        if (pageDict instanceof Error) {
+          promise = Promise.reject(pageDict);
+          promise.catch(() => {});
+        } else {
+          promise = Promise.resolve(new Page({
+            pdfManager,
+            xref: this.xref,
+            pageIndex,
+            pageDict,
+            ref,
+            globalIdFactory: this._globalIdFactory,
+            fontCache: catalog.fontCache,
+            builtInCMapCache: catalog.builtInCMapCache,
+            standardFontDataCache: catalog.standardFontDataCache,
+            globalImageCache: catalog.globalImageCache,
+            nonBlendModesSet: catalog.nonBlendModesSet,
+            xfaFactory: null
+          }));
+        }
+
+        this._pagePromises.set(pageIndex, promise);
+      }
+
+      catalog.setActualNumPages(pagesTree.size);
+    }
   }
   }
 
 
   fontFallback(id, handler) {
   fontFallback(id, handler) {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1 - 1
lib/core/encodings.js


+ 144 - 151
lib/core/evaluator.js

@@ -56,6 +56,8 @@ var _image_utils = require("./image_utils.js");
 
 
 var _stream = require("./stream.js");
 var _stream = require("./stream.js");
 
 
+var _base_stream = require("./base_stream.js");
+
 var _bidi = require("./bidi.js");
 var _bidi = require("./bidi.js");
 
 
 var _colorspace = require("./colorspace.js");
 var _colorspace = require("./colorspace.js");
@@ -498,7 +500,8 @@ class PartialEvaluator {
       operatorList.addOp(_util.OPS.beginGroup, [groupOptions]);
       operatorList.addOp(_util.OPS.beginGroup, [groupOptions]);
     }
     }
 
 
-    operatorList.addOp(_util.OPS.paintFormXObjectBegin, [matrix, bbox]);
+    const args = group ? [matrix, null] : [matrix, bbox];
+    operatorList.addOp(_util.OPS.paintFormXObjectBegin, args);
     return this.getOperatorList({
     return this.getOperatorList({
       stream: xobj,
       stream: xobj,
       task,
       task,
@@ -539,8 +542,8 @@ class PartialEvaluator {
   }) {
   }) {
     const dict = image.dict;
     const dict = image.dict;
     const imageRef = dict.objId;
     const imageRef = dict.objId;
-    const w = dict.get("Width", "W");
-    const h = dict.get("Height", "H");
+    const w = dict.get("W", "Width");
+    const h = dict.get("H", "Height");
 
 
     if (!(w && (0, _util.isNum)(w)) || !(h && (0, _util.isNum)(h))) {
     if (!(w && (0, _util.isNum)(w)) || !(h && (0, _util.isNum)(h))) {
       (0, _util.warn)("Image dimensions are missing, or not numbers.");
       (0, _util.warn)("Image dimensions are missing, or not numbers.");
@@ -564,20 +567,18 @@ class PartialEvaluator {
       operatorList.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]);
       operatorList.addOp(_util.OPS.beginMarkedContentProps, ["OC", optionalContent]);
     }
     }
 
 
-    const imageMask = dict.get("ImageMask", "IM") || false;
-    const interpolate = dict.get("Interpolate", "I");
+    const imageMask = dict.get("IM", "ImageMask") || false;
+    const interpolate = dict.get("I", "Interpolate");
     let imgData, args;
     let imgData, args;
 
 
     if (imageMask) {
     if (imageMask) {
-      const width = dict.get("Width", "W");
-      const height = dict.get("Height", "H");
-      const bitStrideLength = width + 7 >> 3;
-      const imgArray = image.getBytes(bitStrideLength * height, true);
-      const decode = dict.getArray("Decode", "D");
+      const bitStrideLength = w + 7 >> 3;
+      const imgArray = image.getBytes(bitStrideLength * h, true);
+      const decode = dict.getArray("D", "Decode");
       imgData = _image.PDFImage.createMask({
       imgData = _image.PDFImage.createMask({
         imgArray,
         imgArray,
-        width,
-        height,
+        width: w,
+        height: h,
         imageIsFromDecodeStream: image instanceof _decode_stream.DecodeStream,
         imageIsFromDecodeStream: image instanceof _decode_stream.DecodeStream,
         inverseDecode: !!decode && decode[0] > 0,
         inverseDecode: !!decode && decode[0] > 0,
         interpolate
         interpolate
@@ -600,7 +601,7 @@ class PartialEvaluator {
       return;
       return;
     }
     }
 
 
-    const softMask = dict.get("SMask", "SM") || false;
+    const softMask = dict.get("SM", "SMask") || false;
     const mask = dict.get("Mask") || false;
     const mask = dict.get("Mask") || false;
     const SMALL_IMAGE_DIMENSIONS = 200;
     const SMALL_IMAGE_DIMENSIONS = 200;
 
 
@@ -1099,8 +1100,7 @@ class PartialEvaluator {
     font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
     font.loadedName = `${this.idFactory.getDocId()}_${fontID}`;
     this.translateFont(preEvaluatedFont).then(translatedFont => {
     this.translateFont(preEvaluatedFont).then(translatedFont => {
       if (translatedFont.fontType !== undefined) {
       if (translatedFont.fontType !== undefined) {
-        const xrefFontStats = xref.stats.fontTypes;
-        xrefFontStats[translatedFont.fontType] = true;
+        xref.stats.addFontType(translatedFont.fontType);
       }
       }
 
 
       fontCapability.resolve(new TranslatedFont({
       fontCapability.resolve(new TranslatedFont({
@@ -1119,8 +1119,10 @@ class PartialEvaluator {
         const fontFile3 = descriptor && descriptor.get("FontFile3");
         const fontFile3 = descriptor && descriptor.get("FontFile3");
         const subtype = fontFile3 && fontFile3.get("Subtype");
         const subtype = fontFile3 && fontFile3.get("Subtype");
         const fontType = (0, _fonts_utils.getFontType)(preEvaluatedFont.type, subtype && subtype.name);
         const fontType = (0, _fonts_utils.getFontType)(preEvaluatedFont.type, subtype && subtype.name);
-        const xrefFontStats = xref.stats.fontTypes;
-        xrefFontStats[fontType] = true;
+
+        if (fontType !== undefined) {
+          xref.stats.addFontType(fontType);
+        }
       } catch (ex) {}
       } catch (ex) {}
 
 
       fontCapability.resolve(new TranslatedFont({
       fontCapability.resolve(new TranslatedFont({
@@ -1921,6 +1923,8 @@ class PartialEvaluator {
     resources = resources || _primitives.Dict.empty;
     resources = resources || _primitives.Dict.empty;
     stateManager = stateManager || new StateManager(new TextState());
     stateManager = stateManager || new StateManager(new TextState());
     const WhitespaceRegexp = /\s/g;
     const WhitespaceRegexp = /\s/g;
+    const DiacriticRegExp = new RegExp("^\\p{Mn}$", "u");
+    const NormalizedUnicodes = (0, _unicode.getNormalizedUnicodes)();
     const textContent = {
     const textContent = {
       items: [],
       items: [],
       styles: Object.create(null)
       styles: Object.create(null)
@@ -1933,21 +1937,20 @@ class PartialEvaluator {
       width: 0,
       width: 0,
       height: 0,
       height: 0,
       vertical: false,
       vertical: false,
-      lastCharSize: 0,
       prevTransform: null,
       prevTransform: null,
       textAdvanceScale: 0,
       textAdvanceScale: 0,
-      spaceWidth: 0,
       spaceInFlowMin: 0,
       spaceInFlowMin: 0,
       spaceInFlowMax: 0,
       spaceInFlowMax: 0,
       trackingSpaceMin: Infinity,
       trackingSpaceMin: Infinity,
+      negativeSpaceMax: -Infinity,
       transform: null,
       transform: null,
       fontName: null,
       fontName: null,
-      hasEOL: false,
-      isLastCharWhiteSpace: false
+      hasEOL: false
     };
     };
-    const TRACKING_SPACE_FACTOR = 0.3;
-    const SPACE_IN_FLOW_MIN_FACTOR = 0.3;
-    const SPACE_IN_FLOW_MAX_FACTOR = 1.3;
+    const TRACKING_SPACE_FACTOR = 0.1;
+    const NEGATIVE_SPACE_FACTOR = -0.2;
+    const SPACE_IN_FLOW_MIN_FACTOR = 0.1;
+    const SPACE_IN_FLOW_MAX_FACTOR = 0.6;
     const self = this;
     const self = this;
     const xref = this.xref;
     const xref = this.xref;
     const showSpacedTextBuffer = [];
     const showSpacedTextBuffer = [];
@@ -2006,19 +2009,10 @@ class PartialEvaluator {
       const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]);
       const scaleLineX = Math.hypot(textState.textLineMatrix[0], textState.textLineMatrix[1]);
       const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]);
       const scaleCtmX = Math.hypot(textState.ctm[0], textState.ctm[1]);
       textContentItem.textAdvanceScale = scaleCtmX * scaleLineX;
       textContentItem.textAdvanceScale = scaleCtmX * scaleLineX;
-      textContentItem.lastCharSize = textContentItem.lastCharSize || 0;
-      const spaceWidth = font.spaceWidth / 1000 * textState.fontSize;
-
-      if (spaceWidth) {
-        textContentItem.spaceWidth = spaceWidth;
-        textContentItem.trackingSpaceMin = spaceWidth * TRACKING_SPACE_FACTOR;
-        textContentItem.spaceInFlowMin = spaceWidth * SPACE_IN_FLOW_MIN_FACTOR;
-        textContentItem.spaceInFlowMax = spaceWidth * SPACE_IN_FLOW_MAX_FACTOR;
-      } else {
-        textContentItem.spaceWidth = 0;
-        textContentItem.trackingSpaceMin = Infinity;
-      }
-
+      textContentItem.trackingSpaceMin = textState.fontSize * TRACKING_SPACE_FACTOR;
+      textContentItem.negativeSpaceMax = textState.fontSize * NEGATIVE_SPACE_FACTOR;
+      textContentItem.spaceInFlowMin = textState.fontSize * SPACE_IN_FLOW_MIN_FACTOR;
+      textContentItem.spaceInFlowMax = textState.fontSize * SPACE_IN_FLOW_MAX_FACTOR;
       textContentItem.hasEOL = false;
       textContentItem.hasEOL = false;
       textContentItem.initialized = true;
       textContentItem.initialized = true;
       return textContentItem;
       return textContentItem;
@@ -2090,38 +2084,69 @@ class PartialEvaluator {
       });
       });
     }
     }
 
 
-    function compareWithLastPosition(fontSize) {
+    function compareWithLastPosition() {
       if (!combineTextItems || !textState.font || !textContentItem.prevTransform) {
       if (!combineTextItems || !textState.font || !textContentItem.prevTransform) {
         return;
         return;
       }
       }
 
 
       const currentTransform = getCurrentTextTransform();
       const currentTransform = getCurrentTextTransform();
-      const posX = currentTransform[4];
-      const posY = currentTransform[5];
-      const lastPosX = textContentItem.prevTransform[4];
-      const lastPosY = textContentItem.prevTransform[5];
+      let posX = currentTransform[4];
+      let posY = currentTransform[5];
+      let lastPosX = textContentItem.prevTransform[4];
+      let lastPosY = textContentItem.prevTransform[5];
 
 
       if (lastPosX === posX && lastPosY === posY) {
       if (lastPosX === posX && lastPosY === posY) {
         return;
         return;
       }
       }
 
 
-      const advanceX = (posX - lastPosX) / textContentItem.textAdvanceScale;
-      const advanceY = (posY - lastPosY) / textContentItem.textAdvanceScale;
-      const HALF_LAST_CHAR = -0.5 * textContentItem.lastCharSize;
+      let rotate = 0;
+
+      if (currentTransform[0] && currentTransform[1] === 0 && currentTransform[2] === 0) {
+        rotate = currentTransform[0] > 0 ? 0 : 180;
+      } else if (currentTransform[1] && currentTransform[0] === 0 && currentTransform[3] === 0) {
+        rotate += currentTransform[1] > 0 ? 90 : 270;
+      }
+
+      if (rotate !== 0) {
+        switch (rotate) {
+          case 90:
+            [posX, posY] = [posY, posX];
+            [lastPosX, lastPosY] = [lastPosY, lastPosX];
+            break;
+
+          case 180:
+            [posX, posY, lastPosX, lastPosY] = [-posX, -posY, -lastPosX, -lastPosY];
+            break;
+
+          case 270:
+            [posX, posY] = [-posY, -posX];
+            [lastPosX, lastPosY] = [-lastPosY, -lastPosX];
+            break;
+        }
+      }
 
 
       if (textState.font.vertical) {
       if (textState.font.vertical) {
-        if (Math.abs(advanceX) > textContentItem.width / textContentItem.textAdvanceScale) {
-          appendEOL();
+        const advanceY = (lastPosY - posY) / textContentItem.textAdvanceScale;
+        const advanceX = posX - lastPosX;
+
+        if (advanceY < textContentItem.negativeSpaceMax) {
+          if (Math.abs(advanceX) > 0.5 * textContentItem.width) {
+            appendEOL();
+            return;
+          }
+
+          flushTextContentItem();
           return;
           return;
         }
         }
 
 
-        if (HALF_LAST_CHAR > advanceY) {
+        if (Math.abs(advanceX) > textContentItem.height) {
+          appendEOL();
           return;
           return;
         }
         }
 
 
-        if (advanceY > textContentItem.trackingSpaceMin) {
+        if (advanceY <= textContentItem.trackingSpaceMin) {
           textContentItem.height += advanceY;
           textContentItem.height += advanceY;
-        } else if (!addFakeSpaces(advanceY, 0, textContentItem.prevTransform)) {
+        } else if (!addFakeSpaces(advanceY, textContentItem.prevTransform)) {
           if (textContentItem.str.length === 0) {
           if (textContentItem.str.length === 0) {
             textContent.items.push({
             textContent.items.push({
               str: " ",
               str: " ",
@@ -2132,7 +2157,6 @@ class PartialEvaluator {
               fontName: textContentItem.fontName,
               fontName: textContentItem.fontName,
               hasEOL: false
               hasEOL: false
             });
             });
-            textContentItem.isLastCharWhiteSpace = true;
           } else {
           } else {
             textContentItem.height += advanceY;
             textContentItem.height += advanceY;
           }
           }
@@ -2141,18 +2165,27 @@ class PartialEvaluator {
         return;
         return;
       }
       }
 
 
-      if (Math.abs(advanceY) > textContentItem.height / textContentItem.textAdvanceScale) {
-        appendEOL();
+      const advanceX = (posX - lastPosX) / textContentItem.textAdvanceScale;
+      const advanceY = posY - lastPosY;
+
+      if (advanceX < textContentItem.negativeSpaceMax) {
+        if (Math.abs(advanceY) > 0.5 * textContentItem.height) {
+          appendEOL();
+          return;
+        }
+
+        flushTextContentItem();
         return;
         return;
       }
       }
 
 
-      if (HALF_LAST_CHAR > advanceX) {
+      if (Math.abs(advanceY) > textContentItem.height) {
+        appendEOL();
         return;
         return;
       }
       }
 
 
       if (advanceX <= textContentItem.trackingSpaceMin) {
       if (advanceX <= textContentItem.trackingSpaceMin) {
         textContentItem.width += advanceX;
         textContentItem.width += advanceX;
-      } else if (!addFakeSpaces(advanceX, 0, textContentItem.prevTransform)) {
+      } else if (!addFakeSpaces(advanceX, textContentItem.prevTransform)) {
         if (textContentItem.str.length === 0) {
         if (textContentItem.str.length === 0) {
           textContent.items.push({
           textContent.items.push({
             str: " ",
             str: " ",
@@ -2163,7 +2196,6 @@ class PartialEvaluator {
             fontName: textContentItem.fontName,
             fontName: textContentItem.fontName,
             hasEOL: false
             hasEOL: false
           });
           });
-          textContentItem.isLastCharWhiteSpace = true;
         } else {
         } else {
           textContentItem.width += advanceX;
           textContentItem.width += advanceX;
         }
         }
@@ -2172,8 +2204,7 @@ class PartialEvaluator {
 
 
     function buildTextContentItem({
     function buildTextContentItem({
       chars,
       chars,
-      extraSpacing,
-      isFirstChunk
+      extraSpacing
     }) {
     }) {
       const font = textState.font;
       const font = textState.font;
 
 
@@ -2184,87 +2215,73 @@ class PartialEvaluator {
           if (!font.vertical) {
           if (!font.vertical) {
             textState.translateTextMatrix(charSpacing * textState.textHScale, 0);
             textState.translateTextMatrix(charSpacing * textState.textHScale, 0);
           } else {
           } else {
-            textState.translateTextMatrix(0, charSpacing);
+            textState.translateTextMatrix(0, -charSpacing);
           }
           }
         }
         }
 
 
         return;
         return;
       }
       }
 
 
-      const NormalizedUnicodes = (0, _unicode.getNormalizedUnicodes)();
       const glyphs = font.charsToGlyphs(chars);
       const glyphs = font.charsToGlyphs(chars);
       const scale = textState.fontMatrix[0] * textState.fontSize;
       const scale = textState.fontMatrix[0] * textState.fontSize;
 
 
-      if (isFirstChunk) {
-        compareWithLastPosition(scale);
-      }
-
-      let textChunk = ensureTextContentItem();
-      let size = 0;
-      let lastCharSize = 0;
-
       for (let i = 0, ii = glyphs.length; i < ii; i++) {
       for (let i = 0, ii = glyphs.length; i < ii; i++) {
         const glyph = glyphs[i];
         const glyph = glyphs[i];
-        let charSpacing = textState.charSpacing + (i === ii - 1 ? extraSpacing : 0);
-        let glyphUnicode = glyph.unicode;
+        let charSpacing = textState.charSpacing + (i + 1 === ii ? extraSpacing : 0);
+        let glyphWidth = glyph.width;
 
 
-        if (glyph.isSpace) {
-          charSpacing += textState.wordSpacing;
-          textChunk.isLastCharWhiteSpace = true;
-        } else {
-          glyphUnicode = NormalizedUnicodes[glyphUnicode] || glyphUnicode;
-          glyphUnicode = (0, _unicode.reverseIfRtl)(glyphUnicode);
-          textChunk.isLastCharWhiteSpace = false;
+        if (font.vertical) {
+          glyphWidth = glyph.vmetric ? glyph.vmetric[0] : -glyphWidth;
         }
         }
 
 
-        textChunk.str.push(glyphUnicode);
-        const glyphWidth = font.vertical && glyph.vmetric ? glyph.vmetric[0] : glyph.width;
         let scaledDim = glyphWidth * scale;
         let scaledDim = glyphWidth * scale;
+        let glyphUnicode = glyph.unicode;
+
+        if (glyphUnicode === " " && (i === 0 || i + 1 === ii || glyphs[i - 1].unicode === " " || glyphs[i + 1].unicode === " " || extraSpacing)) {
+          if (!font.vertical) {
+            charSpacing += scaledDim + textState.wordSpacing;
+            textState.translateTextMatrix(charSpacing * textState.textHScale, 0);
+          } else {
+            charSpacing += -scaledDim + textState.wordSpacing;
+            textState.translateTextMatrix(0, -charSpacing);
+          }
+
+          continue;
+        }
+
+        compareWithLastPosition();
+        const textChunk = ensureTextContentItem();
+
+        if (DiacriticRegExp.test(glyph.unicode)) {
+          scaledDim = 0;
+        }
 
 
         if (!font.vertical) {
         if (!font.vertical) {
           scaledDim *= textState.textHScale;
           scaledDim *= textState.textHScale;
           textState.translateTextMatrix(scaledDim, 0);
           textState.translateTextMatrix(scaledDim, 0);
+          textChunk.width += scaledDim;
         } else {
         } else {
           textState.translateTextMatrix(0, scaledDim);
           textState.translateTextMatrix(0, scaledDim);
           scaledDim = Math.abs(scaledDim);
           scaledDim = Math.abs(scaledDim);
+          textChunk.height += scaledDim;
         }
         }
 
 
-        size += scaledDim;
-
-        if (charSpacing) {
-          if (!font.vertical) {
-            charSpacing *= textState.textHScale;
-          }
+        if (scaledDim) {
+          textChunk.prevTransform = getCurrentTextTransform();
+        }
 
 
-          scaledDim += charSpacing;
-          const wasSplit = charSpacing > textContentItem.trackingSpaceMin && addFakeSpaces(charSpacing, size);
+        glyphUnicode = NormalizedUnicodes[glyphUnicode] || glyphUnicode;
+        glyphUnicode = (0, _unicode.reverseIfRtl)(glyphUnicode);
+        textChunk.str.push(glyphUnicode);
 
 
+        if (charSpacing) {
           if (!font.vertical) {
           if (!font.vertical) {
-            textState.translateTextMatrix(charSpacing, 0);
-          } else {
-            textState.translateTextMatrix(0, charSpacing);
-          }
-
-          if (wasSplit) {
-            textChunk = ensureTextContentItem();
-            size = 0;
+            textState.translateTextMatrix(charSpacing * textState.textHScale, 0);
           } else {
           } else {
-            size += charSpacing;
+            textState.translateTextMatrix(0, -charSpacing);
           }
           }
         }
         }
-
-        lastCharSize = scaledDim;
       }
       }
-
-      textChunk.lastCharSize = lastCharSize;
-
-      if (!font.vertical) {
-        textChunk.width += size;
-      } else {
-        textChunk.height += size;
-      }
-
-      textChunk.prevTransform = getCurrentTextTransform();
     }
     }
 
 
     function appendEOL() {
     function appendEOL() {
@@ -2282,16 +2299,12 @@ class PartialEvaluator {
           hasEOL: true
           hasEOL: true
         });
         });
       }
       }
-
-      textContentItem.isLastCharWhiteSpace = false;
-      textContentItem.lastCharSize = 0;
     }
     }
 
 
-    function addFakeSpaces(width, size, transf = null) {
+    function addFakeSpaces(width, transf) {
       if (textContentItem.spaceInFlowMin <= width && width <= textContentItem.spaceInFlowMax) {
       if (textContentItem.spaceInFlowMin <= width && width <= textContentItem.spaceInFlowMax) {
         if (textContentItem.initialized) {
         if (textContentItem.initialized) {
           textContentItem.str.push(" ");
           textContentItem.str.push(" ");
-          textContentItem.isLastCharWhiteSpace = true;
         }
         }
 
 
         return false;
         return false;
@@ -2299,29 +2312,19 @@ class PartialEvaluator {
 
 
       const fontName = textContentItem.fontName;
       const fontName = textContentItem.fontName;
       let height = 0;
       let height = 0;
-      width *= textContentItem.textAdvanceScale;
 
 
-      if (!textContentItem.vertical) {
-        textContentItem.width += size;
-      } else {
-        textContentItem.height += size;
+      if (textContentItem.vertical) {
         height = width;
         height = width;
         width = 0;
         width = 0;
       }
       }
 
 
       flushTextContentItem();
       flushTextContentItem();
-
-      if (textContentItem.isLastCharWhiteSpace) {
-        return true;
-      }
-
-      textContentItem.isLastCharWhiteSpace = true;
       textContent.items.push({
       textContent.items.push({
         str: " ",
         str: " ",
         dir: "ltr",
         dir: "ltr",
         width,
         width,
         height,
         height,
-        transform: transf ? transf : getCurrentTextTransform(),
+        transform: transf || getCurrentTextTransform(),
         fontName,
         fontName,
         hasEOL: false
         hasEOL: false
       });
       });
@@ -2407,17 +2410,14 @@ class PartialEvaluator {
             return;
             return;
 
 
           case _util.OPS.setTextRise:
           case _util.OPS.setTextRise:
-            flushTextContentItem();
             textState.textRise = args[0];
             textState.textRise = args[0];
             break;
             break;
 
 
           case _util.OPS.setHScale:
           case _util.OPS.setHScale:
-            flushTextContentItem();
             textState.textHScale = args[0] / 100;
             textState.textHScale = args[0] / 100;
             break;
             break;
 
 
           case _util.OPS.setLeading:
           case _util.OPS.setLeading:
-            flushTextContentItem();
             textState.leading = args[0];
             textState.leading = args[0];
             break;
             break;
 
 
@@ -2427,14 +2427,12 @@ class PartialEvaluator {
             break;
             break;
 
 
           case _util.OPS.setLeadingMoveText:
           case _util.OPS.setLeadingMoveText:
-            flushTextContentItem();
             textState.leading = -args[1];
             textState.leading = -args[1];
             textState.translateTextLineMatrix(args[0], args[1]);
             textState.translateTextLineMatrix(args[0], args[1]);
             textState.textMatrix = textState.textLineMatrix.slice();
             textState.textMatrix = textState.textLineMatrix.slice();
             break;
             break;
 
 
           case _util.OPS.nextLine:
           case _util.OPS.nextLine:
-            appendEOL();
             textState.carriageReturn();
             textState.carriageReturn();
             break;
             break;
 
 
@@ -2453,7 +2451,6 @@ class PartialEvaluator {
             break;
             break;
 
 
           case _util.OPS.beginText:
           case _util.OPS.beginText:
-            flushTextContentItem();
             textState.textMatrix = _util.IDENTITY_MATRIX.slice();
             textState.textMatrix = _util.IDENTITY_MATRIX.slice();
             textState.textLineMatrix = _util.IDENTITY_MATRIX.slice();
             textState.textLineMatrix = _util.IDENTITY_MATRIX.slice();
             break;
             break;
@@ -2466,7 +2463,6 @@ class PartialEvaluator {
 
 
             const spaceFactor = (textState.font.vertical ? 1 : -1) * textState.fontSize / 1000;
             const spaceFactor = (textState.font.vertical ? 1 : -1) * textState.fontSize / 1000;
             const elements = args[0];
             const elements = args[0];
-            let isFirstChunk = true;
 
 
             for (let i = 0, ii = elements.length; i < ii - 1; i++) {
             for (let i = 0, ii = elements.length; i < ii - 1; i++) {
               const item = elements[i];
               const item = elements[i];
@@ -2478,13 +2474,8 @@ class PartialEvaluator {
                 showSpacedTextBuffer.length = 0;
                 showSpacedTextBuffer.length = 0;
                 buildTextContentItem({
                 buildTextContentItem({
                   chars: str,
                   chars: str,
-                  extraSpacing: item * spaceFactor,
-                  isFirstChunk
+                  extraSpacing: item * spaceFactor
                 });
                 });
-
-                if (str && isFirstChunk) {
-                  isFirstChunk = false;
-                }
               }
               }
             }
             }
 
 
@@ -2499,8 +2490,7 @@ class PartialEvaluator {
               showSpacedTextBuffer.length = 0;
               showSpacedTextBuffer.length = 0;
               buildTextContentItem({
               buildTextContentItem({
                 chars: str,
                 chars: str,
-                extraSpacing: 0,
-                isFirstChunk
+                extraSpacing: 0
               });
               });
             }
             }
 
 
@@ -2514,8 +2504,7 @@ class PartialEvaluator {
 
 
             buildTextContentItem({
             buildTextContentItem({
               chars: args[0],
               chars: args[0],
-              extraSpacing: 0,
-              isFirstChunk: true
+              extraSpacing: 0
             });
             });
             break;
             break;
 
 
@@ -2525,13 +2514,10 @@ class PartialEvaluator {
               continue;
               continue;
             }
             }
 
 
-            textContentItem.hasEOL = true;
-            flushTextContentItem();
             textState.carriageReturn();
             textState.carriageReturn();
             buildTextContentItem({
             buildTextContentItem({
               chars: args[0],
               chars: args[0],
-              extraSpacing: 0,
-              isFirstChunk: true
+              extraSpacing: 0
             });
             });
             break;
             break;
 
 
@@ -2541,15 +2527,12 @@ class PartialEvaluator {
               continue;
               continue;
             }
             }
 
 
-            textContentItem.hasEOL = true;
-            flushTextContentItem();
             textState.wordSpacing = args[0];
             textState.wordSpacing = args[0];
             textState.charSpacing = args[1];
             textState.charSpacing = args[1];
             textState.carriageReturn();
             textState.carriageReturn();
             buildTextContentItem({
             buildTextContentItem({
               chars: args[2],
               chars: args[2],
-              extraSpacing: 0,
-              isFirstChunk: true
+              extraSpacing: 0
             });
             });
             break;
             break;
 
 
@@ -2801,7 +2784,7 @@ class PartialEvaluator {
 
 
       const cidToGidMap = dict.get("CIDToGIDMap");
       const cidToGidMap = dict.get("CIDToGIDMap");
 
 
-      if ((0, _primitives.isStream)(cidToGidMap)) {
+      if (cidToGidMap instanceof _base_stream.BaseStream) {
         cidToGidBytes = cidToGidMap.getBytes();
         cidToGidBytes = cidToGidMap.getBytes();
       }
       }
     }
     }
@@ -3401,6 +3384,16 @@ class PartialEvaluator {
 
 
           hash.update(widthsBuf.join());
           hash.update(widthsBuf.join());
         }
         }
+
+        const cidToGidMap = dict.getRaw("CIDToGIDMap") || baseDict.getRaw("CIDToGIDMap");
+
+        if (cidToGidMap instanceof _primitives.Name) {
+          hash.update(cidToGidMap.name);
+        } else if (cidToGidMap instanceof _primitives.Ref) {
+          hash.update(cidToGidMap.toString());
+        } else if (cidToGidMap instanceof _base_stream.BaseStream) {
+          hash.update(cidToGidMap.peekBytes());
+        }
       }
       }
     }
     }
 
 

+ 48 - 0
lib/core/fonts.js

@@ -1195,6 +1195,54 @@ class Font {
         }
         }
 
 
         hasShortCmap = true;
         hasShortCmap = true;
+      } else if (format === 2) {
+        const subHeaderKeys = [];
+        let maxSubHeaderKey = 0;
+
+        for (let i = 0; i < 256; i++) {
+          const subHeaderKey = file.getUint16() >> 3;
+          subHeaderKeys.push(subHeaderKey);
+          maxSubHeaderKey = Math.max(subHeaderKey, maxSubHeaderKey);
+        }
+
+        const subHeaders = [];
+
+        for (let i = 0; i <= maxSubHeaderKey; i++) {
+          subHeaders.push({
+            firstCode: file.getUint16(),
+            entryCount: file.getUint16(),
+            idDelta: signedInt16(file.getByte(), file.getByte()),
+            idRangePos: file.pos + file.getUint16()
+          });
+        }
+
+        for (let i = 0; i < 256; i++) {
+          if (subHeaderKeys[i] === 0) {
+            file.pos = subHeaders[0].idRangePos + 2 * i;
+            glyphId = file.getUint16();
+            mappings.push({
+              charCode: i,
+              glyphId
+            });
+          } else {
+            const s = subHeaders[subHeaderKeys[i]];
+
+            for (j = 0; j < s.entryCount; j++) {
+              const charCode = (i << 8) + j + s.firstCode;
+              file.pos = s.idRangePos + 2 * j;
+              glyphId = file.getUint16();
+
+              if (glyphId !== 0) {
+                glyphId = (glyphId + s.idDelta) % 65536;
+              }
+
+              mappings.push({
+                charCode,
+                glyphId
+              });
+            }
+          }
+        }
       } else if (format === 4) {
       } else if (format === 4) {
         const segCount = file.getUint16() >> 1;
         const segCount = file.getUint16() >> 1;
         file.skip(6);
         file.skip(6);

+ 1 - 1
lib/core/fonts_utils.js

@@ -24,11 +24,11 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.SEAC_ANALYSIS_ENABLED = exports.MacStandardGlyphOrdering = exports.FontFlags = void 0;
 exports.getFontType = getFontType;
 exports.getFontType = getFontType;
 exports.normalizeFontName = normalizeFontName;
 exports.normalizeFontName = normalizeFontName;
 exports.recoverGlyphName = recoverGlyphName;
 exports.recoverGlyphName = recoverGlyphName;
 exports.type1FontGlyphMapping = type1FontGlyphMapping;
 exports.type1FontGlyphMapping = type1FontGlyphMapping;
-exports.SEAC_ANALYSIS_ENABLED = exports.MacStandardGlyphOrdering = exports.FontFlags = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 

+ 1 - 1
lib/core/function.js

@@ -24,8 +24,8 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.isPDFFunction = isPDFFunction;
 exports.PostScriptEvaluator = exports.PostScriptCompiler = exports.PDFFunctionFactory = void 0;
 exports.PostScriptEvaluator = exports.PostScriptCompiler = exports.PDFFunctionFactory = void 0;
+exports.isPDFFunction = isPDFFunction;
 
 
 var _primitives = require("./primitives.js");
 var _primitives = require("./primitives.js");
 
 

+ 10 - 10
lib/core/image.js

@@ -102,7 +102,7 @@ class PDFImage {
   }) {
   }) {
     this.image = image;
     this.image = image;
     const dict = image.dict;
     const dict = image.dict;
-    const filter = dict.get("Filter");
+    const filter = dict.get("F", "Filter");
 
 
     if ((0, _primitives.isName)(filter)) {
     if ((0, _primitives.isName)(filter)) {
       switch (filter.name) {
       switch (filter.name) {
@@ -123,8 +123,8 @@ class PDFImage {
       }
       }
     }
     }
 
 
-    let width = dict.get("Width", "W");
-    let height = dict.get("Height", "H");
+    let width = dict.get("W", "Width");
+    let height = dict.get("H", "Height");
 
 
     if (Number.isInteger(image.width) && image.width > 0 && Number.isInteger(image.height) && image.height > 0 && (image.width !== width || image.height !== height)) {
     if (Number.isInteger(image.width) && image.width > 0 && Number.isInteger(image.height) && image.height > 0 && (image.width !== width || image.height !== height)) {
       (0, _util.warn)("PDFImage - using the Width/Height of the image data, " + "rather than the image dictionary.");
       (0, _util.warn)("PDFImage - using the Width/Height of the image data, " + "rather than the image dictionary.");
@@ -138,13 +138,13 @@ class PDFImage {
 
 
     this.width = width;
     this.width = width;
     this.height = height;
     this.height = height;
-    this.interpolate = dict.get("Interpolate", "I");
-    this.imageMask = dict.get("ImageMask", "IM") || false;
+    this.interpolate = dict.get("I", "Interpolate");
+    this.imageMask = dict.get("IM", "ImageMask") || false;
     this.matte = dict.get("Matte") || false;
     this.matte = dict.get("Matte") || false;
     let bitsPerComponent = image.bitsPerComponent;
     let bitsPerComponent = image.bitsPerComponent;
 
 
     if (!bitsPerComponent) {
     if (!bitsPerComponent) {
-      bitsPerComponent = dict.get("BitsPerComponent", "BPC");
+      bitsPerComponent = dict.get("BPC", "BitsPerComponent");
 
 
       if (!bitsPerComponent) {
       if (!bitsPerComponent) {
         if (this.imageMask) {
         if (this.imageMask) {
@@ -158,7 +158,7 @@ class PDFImage {
     this.bpc = bitsPerComponent;
     this.bpc = bitsPerComponent;
 
 
     if (!this.imageMask) {
     if (!this.imageMask) {
-      let colorSpace = dict.getRaw("ColorSpace") || dict.getRaw("CS");
+      let colorSpace = dict.getRaw("CS") || dict.getRaw("ColorSpace");
 
 
       if (!colorSpace) {
       if (!colorSpace) {
         (0, _util.info)("JPX images (which do not require color spaces)");
         (0, _util.info)("JPX images (which do not require color spaces)");
@@ -177,7 +177,7 @@ class PDFImage {
             break;
             break;
 
 
           default:
           default:
-            throw new Error(`JPX images with ${image.numComps} ` + "color components not supported.");
+            throw new Error(`JPX images with ${image.numComps} color components not supported.`);
         }
         }
       }
       }
 
 
@@ -191,7 +191,7 @@ class PDFImage {
       this.numComps = this.colorSpace.numComps;
       this.numComps = this.colorSpace.numComps;
     }
     }
 
 
-    this.decode = dict.getArray("Decode", "D");
+    this.decode = dict.getArray("D", "Decode");
     this.needsDecode = false;
     this.needsDecode = false;
 
 
     if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !_colorspace.ColorSpace.isDefaultDecode(this.decode, 1))) {
     if (this.decode && (this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode, bitsPerComponent) || isMask && !_colorspace.ColorSpace.isDefaultDecode(this.decode, 1))) {
@@ -221,7 +221,7 @@ class PDFImage {
     } else if (mask) {
     } else if (mask) {
       if ((0, _primitives.isStream)(mask)) {
       if ((0, _primitives.isStream)(mask)) {
         const maskDict = mask.dict,
         const maskDict = mask.dict,
-              imageMask = maskDict.get("ImageMask", "IM");
+              imageMask = maskDict.get("IM", "ImageMask");
 
 
         if (!imageMask) {
         if (!imageMask) {
           (0, _util.warn)("Ignoring /Mask in image without /ImageMask.");
           (0, _util.warn)("Ignoring /Mask in image without /ImageMask.");

+ 2 - 2
lib/core/jpeg_stream.js

@@ -67,10 +67,10 @@ class JpegStream extends _decode_stream.DecodeStream {
       decodeTransform: undefined,
       decodeTransform: undefined,
       colorTransform: undefined
       colorTransform: undefined
     };
     };
-    const decodeArr = this.dict.getArray("Decode", "D");
+    const decodeArr = this.dict.getArray("D", "Decode");
 
 
     if (this.forceRGB && Array.isArray(decodeArr)) {
     if (this.forceRGB && Array.isArray(decodeArr)) {
-      const bitsPerComponent = this.dict.get("BitsPerComponent") || 8;
+      const bitsPerComponent = this.dict.get("BPC", "BitsPerComponent") || 8;
       const decodeArrLength = decodeArr.length;
       const decodeArrLength = decodeArr.length;
       const transform = new Int32Array(decodeArrLength);
       const transform = new Int32Array(decodeArrLength);
       let transformNeeded = false;
       let transformNeeded = false;

+ 1 - 1
lib/core/jpg.js

@@ -1209,7 +1209,7 @@ class JpegImage {
       y = data[i + 2];
       y = data[i + 2];
       k = data[i + 3];
       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.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.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.0003189131175883281 * 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);
       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);
     }
     }
 
 

+ 77 - 68
lib/core/parser.js

@@ -483,7 +483,7 @@ class Parser {
       dictLength = stream.pos - lexer.beginInlineImagePos;
       dictLength = stream.pos - lexer.beginInlineImagePos;
     }
     }
 
 
-    const filter = dict.get("Filter", "F");
+    const filter = dict.get("F", "Filter");
     let filterName;
     let filterName;
 
 
     if ((0, _primitives.isName)(filter)) {
     if ((0, _primitives.isName)(filter)) {
@@ -499,14 +499,24 @@ class Parser {
     const startPos = stream.pos;
     const startPos = stream.pos;
     let length;
     let length;
 
 
-    if (filterName === "DCTDecode" || filterName === "DCT") {
-      length = this.findDCTDecodeInlineStreamEnd(stream);
-    } else if (filterName === "ASCII85Decode" || filterName === "A85") {
-      length = this.findASCII85DecodeInlineStreamEnd(stream);
-    } else if (filterName === "ASCIIHexDecode" || filterName === "AHx") {
-      length = this.findASCIIHexDecodeInlineStreamEnd(stream);
-    } else {
-      length = this.findDefaultInlineStreamEnd(stream);
+    switch (filterName) {
+      case "DCT":
+      case "DCTDecode":
+        length = this.findDCTDecodeInlineStreamEnd(stream);
+        break;
+
+      case "A85":
+      case "ASCII85Decode":
+        length = this.findASCII85DecodeInlineStreamEnd(stream);
+        break;
+
+      case "AHx":
+      case "ASCIIHexDecode":
+        length = this.findASCIIHexDecodeInlineStreamEnd(stream);
+        break;
+
+      default:
+        length = this.findDefaultInlineStreamEnd(stream);
     }
     }
 
 
     let imageStream = stream.makeSubStream(startPos, length, dict);
     let imageStream = stream.makeSubStream(startPos, length, dict);
@@ -594,7 +604,7 @@ class Parser {
     let length = dict.get("Length");
     let length = dict.get("Length");
 
 
     if (!Number.isInteger(length)) {
     if (!Number.isInteger(length)) {
-      (0, _util.info)(`Bad length "${length}" in stream`);
+      (0, _util.info)(`Bad length "${length && length.toString()}" in stream.`);
       length = 0;
       length = 0;
     }
     }
 
 
@@ -654,12 +664,12 @@ class Parser {
   }
   }
 
 
   filter(stream, dict, length) {
   filter(stream, dict, length) {
-    let filter = dict.get("Filter", "F");
-    let params = dict.get("DecodeParms", "DP");
+    let filter = dict.get("F", "Filter");
+    let params = dict.get("DP", "DecodeParms");
 
 
     if ((0, _primitives.isName)(filter)) {
     if ((0, _primitives.isName)(filter)) {
       if (Array.isArray(params)) {
       if (Array.isArray(params)) {
-        (0, _util.warn)("/DecodeParms should not contain an Array, " + "when /Filter contains a Name.");
+        (0, _util.warn)("/DecodeParms should not be an Array, when /Filter is a Name.");
       }
       }
 
 
       return this.makeFilter(stream, filter.name, length, params);
       return this.makeFilter(stream, filter.name, length, params);
@@ -698,67 +708,68 @@ class Parser {
       return new _stream.NullStream();
       return new _stream.NullStream();
     }
     }
 
 
-    try {
-      const xrefStreamStats = this.xref.stats.streamTypes;
-
-      if (name === "FlateDecode" || name === "Fl") {
-        xrefStreamStats[_util.StreamType.FLATE] = true;
-
-        if (params) {
-          return new _predictor_stream.PredictorStream(new _flate_stream.FlateStream(stream, maybeLength), maybeLength, params);
-        }
+    const xrefStats = this.xref.stats;
 
 
-        return new _flate_stream.FlateStream(stream, maybeLength);
-      }
-
-      if (name === "LZWDecode" || name === "LZW") {
-        xrefStreamStats[_util.StreamType.LZW] = true;
-        let earlyChange = 1;
+    try {
+      switch (name) {
+        case "Fl":
+        case "FlateDecode":
+          xrefStats.addStreamType(_util.StreamType.FLATE);
 
 
-        if (params) {
-          if (params.has("EarlyChange")) {
-            earlyChange = params.get("EarlyChange");
+          if (params) {
+            return new _predictor_stream.PredictorStream(new _flate_stream.FlateStream(stream, maybeLength), maybeLength, params);
           }
           }
 
 
-          return new _predictor_stream.PredictorStream(new _lzw_stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
-        }
-
-        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, params);
-      }
-
-      if (name === "JPXDecode" || name === "JPX") {
-        xrefStreamStats[_util.StreamType.JPX] = true;
-        return new _jpx_stream.JpxStream(stream, maybeLength, params);
-      }
+          return new _flate_stream.FlateStream(stream, maybeLength);
 
 
-      if (name === "ASCII85Decode" || name === "A85") {
-        xrefStreamStats[_util.StreamType.A85] = true;
-        return new _ascii_85_stream.Ascii85Stream(stream, maybeLength);
-      }
-
-      if (name === "ASCIIHexDecode" || name === "AHx") {
-        xrefStreamStats[_util.StreamType.AHX] = true;
-        return new _ascii_hex_stream.AsciiHexStream(stream, maybeLength);
-      }
+        case "LZW":
+        case "LZWDecode":
+          xrefStats.addStreamType(_util.StreamType.LZW);
+          let earlyChange = 1;
 
 
-      if (name === "CCITTFaxDecode" || name === "CCF") {
-        xrefStreamStats[_util.StreamType.CCF] = true;
-        return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params);
-      }
+          if (params) {
+            if (params.has("EarlyChange")) {
+              earlyChange = params.get("EarlyChange");
+            }
 
 
-      if (name === "RunLengthDecode" || name === "RL") {
-        xrefStreamStats[_util.StreamType.RLX] = true;
-        return new _run_length_stream.RunLengthStream(stream, maybeLength);
-      }
+            return new _predictor_stream.PredictorStream(new _lzw_stream.LZWStream(stream, maybeLength, earlyChange), maybeLength, params);
+          }
 
 
-      if (name === "JBIG2Decode") {
-        xrefStreamStats[_util.StreamType.JBIG] = true;
-        return new _jbig2_stream.Jbig2Stream(stream, maybeLength, params);
+          return new _lzw_stream.LZWStream(stream, maybeLength, earlyChange);
+
+        case "DCT":
+        case "DCTDecode":
+          xrefStats.addStreamType(_util.StreamType.DCT);
+          return new _jpeg_stream.JpegStream(stream, maybeLength, params);
+
+        case "JPX":
+        case "JPXDecode":
+          xrefStats.addStreamType(_util.StreamType.JPX);
+          return new _jpx_stream.JpxStream(stream, maybeLength, params);
+
+        case "A85":
+        case "ASCII85Decode":
+          xrefStats.addStreamType(_util.StreamType.A85);
+          return new _ascii_85_stream.Ascii85Stream(stream, maybeLength);
+
+        case "AHx":
+        case "ASCIIHexDecode":
+          xrefStats.addStreamType(_util.StreamType.AHX);
+          return new _ascii_hex_stream.AsciiHexStream(stream, maybeLength);
+
+        case "CCF":
+        case "CCITTFaxDecode":
+          xrefStats.addStreamType(_util.StreamType.CCF);
+          return new _ccitt_stream.CCITTFaxStream(stream, maybeLength, params);
+
+        case "RL":
+        case "RunLengthDecode":
+          xrefStats.addStreamType(_util.StreamType.RLX);
+          return new _run_length_stream.RunLengthStream(stream, maybeLength);
+
+        case "JBIG2Decode":
+          xrefStats.addStreamType(_util.StreamType.JBIG);
+          return new _jbig2_stream.Jbig2Stream(stream, maybeLength, params);
       }
       }
 
 
       (0, _util.warn)(`Filter "${name}" is not supported.`);
       (0, _util.warn)(`Filter "${name}" is not supported.`);
@@ -1069,8 +1080,6 @@ class Lexer {
 
 
     if (strBuf.length > 127) {
     if (strBuf.length > 127) {
       (0, _util.warn)(`Name token is longer than allowed by the spec: ${strBuf.length}`);
       (0, _util.warn)(`Name token is longer than allowed by the spec: ${strBuf.length}`);
-    } else if (strBuf.length === 0) {
-      (0, _util.warn)("Name token is empty.");
     }
     }
 
 
     return _primitives.Name.get(strBuf.join(""));
     return _primitives.Name.get(strBuf.join(""));

+ 3 - 3
lib/core/pattern.js

@@ -24,8 +24,8 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.getTilingPatternIR = getTilingPatternIR;
 exports.Pattern = void 0;
 exports.Pattern = void 0;
+exports.getTilingPatternIR = getTilingPatternIR;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -110,7 +110,7 @@ class RadialAxialShading extends BaseShading {
     this.shadingType = dict.get("ShadingType");
     this.shadingType = dict.get("ShadingType");
 
 
     const cs = _colorspace.ColorSpace.parse({
     const cs = _colorspace.ColorSpace.parse({
-      cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
+      cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
       xref,
       xref,
       resources,
       resources,
       pdfFunctionFactory,
       pdfFunctionFactory,
@@ -382,7 +382,7 @@ class MeshShading extends BaseShading {
     }
     }
 
 
     const cs = _colorspace.ColorSpace.parse({
     const cs = _colorspace.ColorSpace.parse({
-      cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
+      cs: dict.getRaw("CS") || dict.getRaw("ColorSpace"),
       xref,
       xref,
       resources,
       resources,
       pdfFunctionFactory,
       pdfFunctionFactory,

+ 2 - 1
lib/core/pdf_manager.js

@@ -136,11 +136,12 @@ class BasePdfManager {
 }
 }
 
 
 class LocalPdfManager extends BasePdfManager {
 class LocalPdfManager extends BasePdfManager {
-  constructor(docId, data, password, evaluatorOptions, enableXfa, docBaseUrl) {
+  constructor(docId, data, password, msgHandler, evaluatorOptions, enableXfa, docBaseUrl) {
     super();
     super();
     this._docId = docId;
     this._docId = docId;
     this._password = password;
     this._password = password;
     this._docBaseUrl = parseDocBaseUrl(docBaseUrl);
     this._docBaseUrl = parseDocBaseUrl(docBaseUrl);
+    this.msgHandler = msgHandler;
     this.evaluatorOptions = evaluatorOptions;
     this.evaluatorOptions = evaluatorOptions;
     this.enableXfa = enableXfa;
     this.enableXfa = enableXfa;
     const stream = new _stream.Stream(data);
     const stream = new _stream.Stream(data);

+ 1 - 1
lib/core/predictor_stream.js

@@ -59,7 +59,7 @@ class PredictorStream extends _decode_stream.DecodeStream {
     this.str = str;
     this.str = str;
     this.dict = str.dict;
     this.dict = str.dict;
     const colors = this.colors = params.get("Colors") || 1;
     const colors = this.colors = params.get("Colors") || 1;
-    const bits = this.bits = params.get("BitsPerComponent") || 8;
+    const bits = this.bits = params.get("BPC", "BitsPerComponent") || 8;
     const columns = this.columns = params.get("Columns") || 1;
     const columns = this.columns = params.get("Columns") || 1;
     this.pixBytes = colors * bits + 7 >> 3;
     this.pixBytes = colors * bits + 7 >> 3;
     this.rowBytes = columns * colors * bits + 7 >> 3;
     this.rowBytes = columns * colors * bits + 7 >> 3;

+ 3 - 1
lib/core/primitives.js

@@ -24,6 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF = exports.Dict = exports.Cmd = exports.CIRCULAR_REF = void 0;
 exports.clearPrimitiveCaches = clearPrimitiveCaches;
 exports.clearPrimitiveCaches = clearPrimitiveCaches;
 exports.isCmd = isCmd;
 exports.isCmd = isCmd;
 exports.isDict = isDict;
 exports.isDict = isDict;
@@ -31,12 +32,13 @@ exports.isName = isName;
 exports.isRef = isRef;
 exports.isRef = isRef;
 exports.isRefsEqual = isRefsEqual;
 exports.isRefsEqual = isRefsEqual;
 exports.isStream = isStream;
 exports.isStream = isStream;
-exports.RefSetCache = exports.RefSet = exports.Ref = exports.Name = exports.EOF = exports.Dict = exports.Cmd = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
 var _base_stream = require("./base_stream.js");
 var _base_stream = require("./base_stream.js");
 
 
+const CIRCULAR_REF = Symbol("CIRCULAR_REF");
+exports.CIRCULAR_REF = CIRCULAR_REF;
 const EOF = Symbol("EOF");
 const EOF = Symbol("EOF");
 exports.EOF = EOF;
 exports.EOF = EOF;
 
 

+ 2 - 1
lib/core/standard_fonts.js

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

+ 6 - 0
lib/core/struct_tree.js

@@ -339,6 +339,12 @@ class StructTreePage {
         obj.alt = (0, _util.stringToPDFString)(alt);
         obj.alt = (0, _util.stringToPDFString)(alt);
       }
       }
 
 
+      const lang = node.dict.get("Lang");
+
+      if ((0, _util.isString)(lang)) {
+        obj.lang = (0, _util.stringToPDFString)(lang);
+      }
+
       for (const kid of node.kids) {
       for (const kid of node.kids) {
         const kidElement = kid.type === StructElementType.ELEMENT ? kid.parentNode : null;
         const kidElement = kid.type === StructElementType.ELEMENT ? kid.parentNode : null;
 
 

+ 1 - 1
lib/core/unicode.js

@@ -24,11 +24,11 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.getNormalizedUnicodes = void 0;
 exports.getUnicodeForGlyph = getUnicodeForGlyph;
 exports.getUnicodeForGlyph = getUnicodeForGlyph;
 exports.getUnicodeRangeFor = getUnicodeRangeFor;
 exports.getUnicodeRangeFor = getUnicodeRangeFor;
 exports.mapSpecialUnicodeValues = mapSpecialUnicodeValues;
 exports.mapSpecialUnicodeValues = mapSpecialUnicodeValues;
 exports.reverseIfRtl = reverseIfRtl;
 exports.reverseIfRtl = reverseIfRtl;
-exports.getNormalizedUnicodes = void 0;
 
 
 var _core_utils = require("./core_utils.js");
 var _core_utils = require("./core_utils.js");
 
 

+ 6 - 23
lib/core/worker.js

@@ -80,17 +80,7 @@ class WorkerMessageHandler {
       }
       }
 
 
       testMessageProcessed = true;
       testMessageProcessed = true;
-
-      if (!(data instanceof Uint8Array)) {
-        handler.send("test", null);
-        return;
-      }
-
-      const supportTransfers = data[0] === 255;
-      handler.postMessageTransfers = supportTransfers;
-      handler.send("test", {
-        supportTransfers
-      });
+      handler.send("test", data instanceof Uint8Array && data[0] === 255);
     });
     });
     handler.on("configure", function wphConfigure(data) {
     handler.on("configure", function wphConfigure(data) {
       (0, _util.setVerbosityLevel)(data.verbosity);
       (0, _util.setVerbosityLevel)(data.verbosity);
@@ -107,7 +97,7 @@ class WorkerMessageHandler {
     const WorkerTasks = [];
     const WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     const verbosity = (0, _util.getVerbosityLevel)();
     const apiVersion = docParams.apiVersion;
     const apiVersion = docParams.apiVersion;
-    const workerVersion = '2.11.338';
+    const workerVersion = '2.12.313';
 
 
     if (apiVersion !== workerVersion) {
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
@@ -131,7 +121,6 @@ class WorkerMessageHandler {
     const docBaseUrl = docParams.docBaseUrl;
     const docBaseUrl = docParams.docBaseUrl;
     const workerHandlerName = docParams.docId + "_worker";
     const workerHandlerName = docParams.docId + "_worker";
     let handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     let handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
-    handler.postMessageTransfers = docParams.postMessageTransfers;
 
 
     function ensureNotTerminated() {
     function ensureNotTerminated() {
       if (terminated) {
       if (terminated) {
@@ -153,11 +142,8 @@ class WorkerMessageHandler {
       await pdfManager.ensureDoc("checkHeader");
       await pdfManager.ensureDoc("checkHeader");
       await pdfManager.ensureDoc("parseStartXRef");
       await pdfManager.ensureDoc("parseStartXRef");
       await pdfManager.ensureDoc("parse", [recoveryMode]);
       await pdfManager.ensureDoc("parse", [recoveryMode]);
-
-      if (!recoveryMode) {
-        await pdfManager.ensureDoc("checkFirstPage");
-      }
-
+      await pdfManager.ensureDoc("checkFirstPage", [recoveryMode]);
+      await pdfManager.ensureDoc("checkLastPage", [recoveryMode]);
       const isPureXfa = await pdfManager.ensureDoc("isPureXfa");
       const isPureXfa = await pdfManager.ensureDoc("isPureXfa");
 
 
       if (isPureXfa) {
       if (isPureXfa) {
@@ -182,7 +168,7 @@ class WorkerMessageHandler {
 
 
       if (source.data) {
       if (source.data) {
         try {
         try {
-          newPdfManager = new _pdf_manager.LocalPdfManager(docId, source.data, source.password, evaluatorOptions, enableXfa, docBaseUrl);
+          newPdfManager = new _pdf_manager.LocalPdfManager(docId, source.data, source.password, handler, evaluatorOptions, enableXfa, docBaseUrl);
           pdfManagerCapability.resolve(newPdfManager);
           pdfManagerCapability.resolve(newPdfManager);
         } catch (ex) {
         } catch (ex) {
           pdfManagerCapability.reject(ex);
           pdfManagerCapability.reject(ex);
@@ -237,7 +223,7 @@ class WorkerMessageHandler {
         }
         }
 
 
         try {
         try {
-          newPdfManager = new _pdf_manager.LocalPdfManager(docId, pdfFile, source.password, evaluatorOptions, enableXfa, docBaseUrl);
+          newPdfManager = new _pdf_manager.LocalPdfManager(docId, pdfFile, source.password, handler, evaluatorOptions, enableXfa, docBaseUrl);
           pdfManagerCapability.resolve(newPdfManager);
           pdfManagerCapability.resolve(newPdfManager);
         } catch (ex) {
         } catch (ex) {
           pdfManagerCapability.reject(ex);
           pdfManagerCapability.reject(ex);
@@ -450,9 +436,6 @@ class WorkerMessageHandler {
         return stream.bytes;
         return stream.bytes;
       });
       });
     });
     });
-    handler.on("GetStats", function wphSetupGetStats(data) {
-      return pdfManager.ensureXRef("stats");
-    });
     handler.on("GetAnnotations", function ({
     handler.on("GetAnnotations", function ({
       pageIndex,
       pageIndex,
       intent
       intent

+ 5 - 0
lib/core/xfa/bind.js

@@ -571,6 +571,11 @@ class Binder {
           match = dataNode[_xfa_object.$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value;
           match = dataNode[_xfa_object.$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value;
 
 
           if (!match) {
           if (!match) {
+            if (min === 0) {
+              uselessNodes.push(child);
+              continue;
+            }
+
             const nsId = dataNode[_xfa_object.$namespaceId] === NS_DATASETS ? -1 : dataNode[_xfa_object.$namespaceId];
             const nsId = dataNode[_xfa_object.$namespaceId] === NS_DATASETS ? -1 : dataNode[_xfa_object.$namespaceId];
             match = child[_xfa_object.$data] = new _xfa_object.XmlObject(nsId, child.name);
             match = child[_xfa_object.$data] = new _xfa_object.XmlObject(nsId, child.name);
 
 

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

@@ -76,7 +76,7 @@ class Empty extends _xfa_object.XFAObject {
 }
 }
 
 
 class Builder {
 class Builder {
-  constructor() {
+  constructor(rootNameSpace = null) {
     this._namespaceStack = [];
     this._namespaceStack = [];
     this._nsAgnosticLevel = 0;
     this._nsAgnosticLevel = 0;
     this._namespacePrefixes = new Map();
     this._namespacePrefixes = new Map();
@@ -84,7 +84,7 @@ class Builder {
     this._nextNsId = Math.max(...Object.values(_namespaces.NamespaceIds).map(({
     this._nextNsId = Math.max(...Object.values(_namespaces.NamespaceIds).map(({
       id
       id
     }) => id));
     }) => id));
-    this._currentNamespace = new _unknown.UnknownNamespace(++this._nextNsId);
+    this._currentNamespace = rootNameSpace || new _unknown.UnknownNamespace(++this._nextNsId);
   }
   }
 
 
   buildRoot(ids) {
   buildRoot(ids) {

+ 78 - 6
lib/core/xfa/factory.js

@@ -40,6 +40,8 @@ var _util = require("../../shared/util.js");
 
 
 var _parser = require("./parser.js");
 var _parser = require("./parser.js");
 
 
+var _xhtml = require("./xhtml.js");
+
 class XFAFactory {
 class XFAFactory {
   constructor(data) {
   constructor(data) {
     try {
     try {
@@ -57,9 +59,31 @@ class XFAFactory {
     return this.root && this.form;
     return this.root && this.form;
   }
   }
 
 
-  _createPages() {
+  _createPagesHelper() {
+    const iterator = this.form[_xfa_object.$toPages]();
+
+    return new Promise((resolve, reject) => {
+      const nextIteration = () => {
+        try {
+          const value = iterator.next();
+
+          if (value.done) {
+            resolve(value.value);
+          } else {
+            setTimeout(nextIteration, 0);
+          }
+        } catch (e) {
+          reject(e);
+        }
+      };
+
+      setTimeout(nextIteration, 0);
+    });
+  }
+
+  async _createPages() {
     try {
     try {
-      this.pages = this.form[_xfa_object.$toHTML]();
+      this.pages = await this._createPagesHelper();
       this.dims = this.pages.children.map(c => {
       this.dims = this.pages.children.map(c => {
         const {
         const {
           width,
           width,
@@ -76,9 +100,9 @@ class XFAFactory {
     return this.dims[pageIndex];
     return this.dims[pageIndex];
   }
   }
 
 
-  get numberPages() {
+  async getNumPages() {
     if (!this.pages) {
     if (!this.pages) {
-      this._createPages();
+      await this._createPages();
     }
     }
 
 
     return this.dims.length;
     return this.dims.length;
@@ -113,9 +137,9 @@ class XFAFactory {
     this.form[_xfa_object.$globalData].fontFinder.add(fonts, reallyMissingFonts);
     this.form[_xfa_object.$globalData].fontFinder.add(fonts, reallyMissingFonts);
   }
   }
 
 
-  getPages() {
+  async getPages() {
     if (!this.pages) {
     if (!this.pages) {
-      this._createPages();
+      await this._createPages();
     }
     }
 
 
     const pages = this.pages;
     const pages = this.pages;
@@ -135,6 +159,54 @@ class XFAFactory {
     return Object.values(data).join("");
     return Object.values(data).join("");
   }
   }
 
 
+  static getRichTextAsHtml(rc) {
+    if (!rc || typeof rc !== "string") {
+      return null;
+    }
+
+    try {
+      let root = new _parser.XFAParser(_xhtml.XhtmlNamespace, true).parse(rc);
+
+      if (!["body", "xhtml"].includes(root[_xfa_object.$nodeName])) {
+        const newRoot = _xhtml.XhtmlNamespace.body({});
+
+        newRoot[_xfa_object.$appendChild](root);
+
+        root = newRoot;
+      }
+
+      const result = root[_xfa_object.$toHTML]();
+
+      if (!result.success) {
+        return null;
+      }
+
+      const {
+        html
+      } = result;
+      const {
+        attributes
+      } = html;
+
+      if (attributes) {
+        if (attributes.class) {
+          attributes.class = attributes.class.filter(attr => !attr.startsWith("xfa"));
+        }
+
+        attributes.dir = "auto";
+      }
+
+      return {
+        html,
+        str: root[_xfa_object.$text]()
+      };
+    } catch (e) {
+      (0, _util.warn)(`XFA - an error occurred during parsing of rich text: ${e}`);
+    }
+
+    return null;
+  }
+
 }
 }
 
 
 exports.XFAFactory = XFAFactory;
 exports.XFAFactory = XFAFactory;

+ 1 - 1
lib/core/xfa/fonts.js

@@ -24,9 +24,9 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.FontFinder = void 0;
 exports.getMetrics = getMetrics;
 exports.getMetrics = getMetrics;
 exports.selectFont = selectFont;
 exports.selectFont = selectFont;
-exports.FontFinder = void 0;
 
 
 var _xfa_object = require("./xfa_object.js");
 var _xfa_object = require("./xfa_object.js");
 
 

+ 1 - 1
lib/core/xfa/formcalc_lexer.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.TOKEN = exports.Token = exports.Lexer = void 0;
+exports.Token = exports.TOKEN = exports.Lexer = void 0;
 const KEYWORDS = new Set(["and", "break", "continue", "do", "downto", "else", "elseif", "end", "endfor", "endfunc", "endif", "endwhile", "eq", "exit", "for", "foreach", "func", "ge", "gt", "if", "in", "infinity", "le", "lt", "nan", "ne", "not", "null", "or", "return", "step", "then", "this", "throw", "upto", "var", "while"]);
 const KEYWORDS = new Set(["and", "break", "continue", "do", "downto", "else", "elseif", "end", "endfor", "endfunc", "endif", "endwhile", "eq", "exit", "for", "foreach", "func", "ge", "gt", "if", "in", "infinity", "le", "lt", "nan", "ne", "not", "null", "or", "return", "step", "then", "this", "throw", "upto", "var", "while"]);
 const TOKEN = {
 const TOKEN = {
   and: 0,
   and: 0,

+ 7 - 2
lib/core/xfa/html_utils.js

@@ -283,7 +283,7 @@ function layoutNode(node, availableSpace) {
 
 
       let parent = node[_xfa_object.$getParent]();
       let parent = node[_xfa_object.$getParent]();
 
 
-      while (parent !== root) {
+      while (parent && parent !== root) {
         if (parent.font) {
         if (parent.font) {
           font = parent.font;
           font = parent.font;
           break;
           break;
@@ -655,9 +655,14 @@ function setPara(node, nodeStyle, value) {
 }
 }
 
 
 function setFontFamily(xfaFont, node, fontFinder, style) {
 function setFontFamily(xfaFont, node, fontFinder, style) {
+  if (!fontFinder) {
+    delete style.fontFamily;
+    return;
+  }
+
   const name = (0, _utils.stripQuotes)(xfaFont.typeface);
   const name = (0, _utils.stripQuotes)(xfaFont.typeface);
-  const typeface = fontFinder.find(name);
   style.fontFamily = `"${name}"`;
   style.fontFamily = `"${name}"`;
+  const typeface = fontFinder.find(name);
 
 
   if (typeface) {
   if (typeface) {
     const {
     const {

+ 1 - 1
lib/core/xfa/layout.js

@@ -137,7 +137,7 @@ function addHTML(node, html, bbox) {
 
 
     case "tb":
     case "tb":
       {
       {
-        extra.width = availableSpace.width;
+        extra.width = Math.min(availableSpace.width, Math.max(extra.width, w));
         extra.height += h;
         extra.height += h;
         extra.children.push(html);
         extra.children.push(html);
         break;
         break;

+ 5 - 4
lib/core/xfa/parser.js

@@ -35,9 +35,9 @@ var _builder = require("./builder.js");
 var _util = require("../../shared/util.js");
 var _util = require("../../shared/util.js");
 
 
 class XFAParser extends _xml_parser.XMLParserBase {
 class XFAParser extends _xml_parser.XMLParserBase {
-  constructor() {
+  constructor(rootNameSpace = null, richText = false) {
     super();
     super();
-    this._builder = new _builder.Builder();
+    this._builder = new _builder.Builder(rootNameSpace);
     this._stack = [];
     this._stack = [];
     this._globalData = {
     this._globalData = {
       usedTypefaces: new Set()
       usedTypefaces: new Set()
@@ -47,6 +47,7 @@ class XFAParser extends _xml_parser.XMLParserBase {
     this._errorCode = _xml_parser.XMLParserErrorCode.NoError;
     this._errorCode = _xml_parser.XMLParserErrorCode.NoError;
     this._whiteRegex = /^\s+$/;
     this._whiteRegex = /^\s+$/;
     this._nbsps = /\xa0+/g;
     this._nbsps = /\xa0+/g;
+    this._richText = richText;
   }
   }
 
 
   parse(data) {
   parse(data) {
@@ -64,8 +65,8 @@ class XFAParser extends _xml_parser.XMLParserBase {
   onText(text) {
   onText(text) {
     text = text.replace(this._nbsps, match => match.slice(1) + " ");
     text = text.replace(this._nbsps, match => match.slice(1) + " ");
 
 
-    if (this._current[_xfa_object.$acceptWhitespace]()) {
-      this._current[_xfa_object.$onText](text);
+    if (this._richText || this._current[_xfa_object.$acceptWhitespace]()) {
+      this._current[_xfa_object.$onText](text, this._richText);
 
 
       return;
       return;
     }
     }

+ 24 - 10
lib/core/xfa/template.js

@@ -50,7 +50,7 @@ const MAX_ATTEMPTS_FOR_LRTB_LAYOUT = 2;
 const MAX_EMPTY_PAGES = 3;
 const MAX_EMPTY_PAGES = 3;
 const DEFAULT_TAB_INDEX = 5000;
 const DEFAULT_TAB_INDEX = 5000;
 const HEADING_PATTERN = /^H(\d+)$/;
 const HEADING_PATTERN = /^H(\d+)$/;
-const MIMES = new Set(["image/gif", "image/jpeg", "image/jpg", "image/pjpeg", "image/png", "image/apng", "image/x-png", "image/bmp", "image/x-ms-bmp", "image/tiff", "image/tif"]);
+const MIMES = new Set(["image/gif", "image/jpeg", "image/jpg", "image/pjpeg", "image/png", "image/apng", "image/x-png", "image/bmp", "image/x-ms-bmp", "image/tiff", "image/tif", "application/octet-stream"]);
 const IMAGES_HEADERS = [[[0x42, 0x4d], "image/bmp"], [[0xff, 0xd8, 0xff], "image/jpeg"], [[0x49, 0x49, 0x2a, 0x00], "image/tiff"], [[0x4d, 0x4d, 0x00, 0x2a], "image/tiff"], [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], "image/gif"], [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], "image/png"]];
 const IMAGES_HEADERS = [[[0x42, 0x4d], "image/bmp"], [[0xff, 0xd8, 0xff], "image/jpeg"], [[0x49, 0x49, 0x2a, 0x00], "image/tiff"], [[0x4d, 0x4d, 0x00, 0x2a], "image/tiff"], [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], "image/gif"], [[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], "image/png"]];
 
 
 function getBorderDims(node) {
 function getBorderDims(node) {
@@ -285,22 +285,31 @@ function handleBreak(node) {
   const pageArea = target && target[_xfa_object.$getParent]();
   const pageArea = target && target[_xfa_object.$getParent]();
 
 
   let index;
   let index;
+  let nextPageArea = pageArea;
 
 
   if (node.startNew) {
   if (node.startNew) {
     if (target) {
     if (target) {
       const contentAreas = pageArea.contentArea.children;
       const contentAreas = pageArea.contentArea.children;
-      index = contentAreas.findIndex(e => e === target) - 1;
+      const indexForCurrent = contentAreas.indexOf(currentContentArea);
+      const indexForTarget = contentAreas.indexOf(target);
+
+      if (indexForCurrent !== -1 && indexForCurrent < indexForTarget) {
+        nextPageArea = null;
+      }
+
+      index = indexForTarget - 1;
     } else {
     } else {
-      index = currentPageArea.contentArea.children.findIndex(e => e === currentContentArea);
+      index = currentPageArea.contentArea.children.indexOf(currentContentArea);
     }
     }
   } else if (target && target !== currentContentArea) {
   } else if (target && target !== currentContentArea) {
     const contentAreas = pageArea.contentArea.children;
     const contentAreas = pageArea.contentArea.children;
-    index = contentAreas.findIndex(e => e === target) - 1;
+    index = contentAreas.indexOf(target) - 1;
+    nextPageArea = pageArea === currentPageArea ? null : pageArea;
   } else {
   } else {
     return false;
     return false;
   }
   }
 
 
-  node[_xfa_object.$extra].target = pageArea === currentPageArea ? null : pageArea;
+  node[_xfa_object.$extra].target = nextPageArea;
   node[_xfa_object.$extra].index = index;
   node[_xfa_object.$extra].index = index;
   return true;
   return true;
 }
 }
@@ -385,7 +394,7 @@ class Arc extends _xfa_object.XFAObject {
       }
       }
     };
     };
 
 
-    if (this.startAngle === 0 && this.sweepAngle === 360) {
+    if (this.sweepAngle === 360) {
       arc = {
       arc = {
         name: "ellipse",
         name: "ellipse",
         attributes: {
         attributes: {
@@ -400,8 +409,8 @@ class Arc extends _xfa_object.XFAObject {
     } else {
     } else {
       const startAngle = this.startAngle * Math.PI / 180;
       const startAngle = this.startAngle * Math.PI / 180;
       const sweepAngle = this.sweepAngle * Math.PI / 180;
       const sweepAngle = this.sweepAngle * Math.PI / 180;
-      const largeArc = this.sweepAngle - this.startAngle > 180 ? 1 : 0;
-      const [x1, y1, x2, y2] = [50 * (1 + Math.cos(startAngle)), 50 * (1 - Math.sin(startAngle)), 50 * (1 + Math.cos(sweepAngle)), 50 * (1 - Math.sin(sweepAngle))];
+      const largeArc = this.sweepAngle > 180 ? 1 : 0;
+      const [x1, y1, x2, y2] = [50 * (1 + Math.cos(startAngle)), 50 * (1 - Math.sin(startAngle)), 50 * (1 + Math.cos(startAngle + sweepAngle)), 50 * (1 - Math.sin(startAngle + sweepAngle))];
       arc = {
       arc = {
         name: "path",
         name: "path",
         attributes: {
         attributes: {
@@ -4601,6 +4610,10 @@ class Subform extends _xfa_object.XFAObject {
       style.height = (0, _html_utils.measureToString)(height);
       style.height = (0, _html_utils.measureToString)(height);
     }
     }
 
 
+    if ((style.width === "0px" || style.height === "0px") && children.length === 0) {
+      return _utils.HTMLResult.EMPTY;
+    }
+
     const html = {
     const html = {
       name: "div",
       name: "div",
       attributes,
       attributes,
@@ -4756,7 +4769,7 @@ class Template extends _xfa_object.XFAObject {
     return (0, _som.searchNode)(this, container, expr, true, true);
     return (0, _som.searchNode)(this, container, expr, true, true);
   }
   }
 
 
-  [_xfa_object.$toHTML]() {
+  *[_xfa_object.$toPages]() {
     if (!this.subform.children.length) {
     if (!this.subform.children.length) {
       return _utils.HTMLResult.success({
       return _utils.HTMLResult.success({
         name: "div",
         name: "div",
@@ -4904,7 +4917,7 @@ class Template extends _xfa_object.XFAObject {
           if (html.html) {
           if (html.html) {
             hasSomething = hasSomething || html.html.children && html.html.children.length !== 0;
             hasSomething = hasSomething || html.html.children && html.html.children.length !== 0;
             htmlContentAreas[i].children.push(html.html);
             htmlContentAreas[i].children.push(html.html);
-          } else if (!hasSomething) {
+          } else if (!hasSomething && mainHtml.children.length > 1) {
             mainHtml.children.pop();
             mainHtml.children.pop();
           }
           }
 
 
@@ -4990,6 +5003,7 @@ class Template extends _xfa_object.XFAObject {
       }
       }
 
 
       pageArea = targetPageArea || pageArea[_xfa_object.$getNextPage]();
       pageArea = targetPageArea || pageArea[_xfa_object.$getNextPage]();
+      yield null;
     }
     }
   }
   }
 
 

+ 1 - 1
lib/core/xfa/utils.js

@@ -24,6 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.HTMLResult = void 0;
 exports.getBBox = getBBox;
 exports.getBBox = getBBox;
 exports.getColor = getColor;
 exports.getColor = getColor;
 exports.getFloat = getFloat;
 exports.getFloat = getFloat;
@@ -34,7 +35,6 @@ exports.getRatio = getRatio;
 exports.getRelevant = getRelevant;
 exports.getRelevant = getRelevant;
 exports.getStringOption = getStringOption;
 exports.getStringOption = getStringOption;
 exports.stripQuotes = stripQuotes;
 exports.stripQuotes = stripQuotes;
-exports.HTMLResult = void 0;
 
 
 var _util = require("../../shared/util.js");
 var _util = require("../../shared/util.js");
 
 

+ 8 - 4
lib/core/xfa/xfa_object.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   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.$toString = exports.$toHTML = exports.$text = exports.$tabIndex = exports.$setValue = exports.$setSetAttributes = exports.$setId = exports.$searchNode = exports.$root = exports.$resolvePrototypes = exports.$removeChild = exports.$pushPara = exports.$pushGlyphs = exports.$popPara = exports.$onText = exports.$onChildCheck = exports.$onChild = exports.$nsAttributes = exports.$nodeName = exports.$namespaceId = exports.$isUsable = exports.$isTransparent = exports.$isThereMoreWidth = exports.$isSplittable = exports.$isNsAgnostic = exports.$isDescendent = exports.$isDataValue = exports.$isCDATAXml = exports.$isBindable = exports.$insertAt = exports.$indexOf = exports.$ids = exports.$hasSettableValue = exports.$globalData = exports.$getTemplateRoot = exports.$getSubformParent = exports.$getRealChildrenByNameIt = exports.$getParent = exports.$getNextPage = exports.$getExtra = exports.$getDataValue = exports.$getContainedChildren = exports.$getChildrenByNameIt = exports.$getChildrenByName = exports.$getChildrenByClass = exports.$getChildren = exports.$getAvailableSpace = exports.$getAttributes = exports.$getAttributeIt = exports.$flushHTML = exports.$finalize = exports.$extra = exports.$dump = exports.$data = exports.$content = exports.$consumed = exports.$clone = exports.$cleanup = exports.$cleanPage = exports.$clean = exports.$childrenToHTML = exports.$appendChild = exports.$addHTML = exports.$acceptWhitespace = 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.$toString = exports.$toPages = exports.$toHTML = exports.$text = exports.$tabIndex = exports.$setValue = exports.$setSetAttributes = exports.$setId = exports.$searchNode = exports.$root = exports.$resolvePrototypes = exports.$removeChild = exports.$pushPara = exports.$pushGlyphs = exports.$popPara = exports.$onText = exports.$onChildCheck = exports.$onChild = exports.$nsAttributes = exports.$nodeName = exports.$namespaceId = exports.$isUsable = exports.$isTransparent = exports.$isThereMoreWidth = exports.$isSplittable = exports.$isNsAgnostic = exports.$isDescendent = exports.$isDataValue = exports.$isCDATAXml = exports.$isBindable = exports.$insertAt = exports.$indexOf = exports.$ids = exports.$hasSettableValue = exports.$globalData = exports.$getTemplateRoot = exports.$getSubformParent = exports.$getRealChildrenByNameIt = exports.$getParent = exports.$getNextPage = exports.$getExtra = exports.$getDataValue = exports.$getContainedChildren = exports.$getChildrenByNameIt = exports.$getChildrenByName = exports.$getChildrenByClass = exports.$getChildren = exports.$getAvailableSpace = exports.$getAttributes = exports.$getAttributeIt = exports.$flushHTML = exports.$finalize = exports.$extra = exports.$dump = exports.$data = exports.$content = exports.$consumed = exports.$clone = exports.$cleanup = exports.$cleanPage = exports.$clean = exports.$childrenToHTML = exports.$appendChild = exports.$addHTML = exports.$acceptWhitespace = void 0;
 
 
 var _utils = require("./utils.js");
 var _utils = require("./utils.js");
 
 
@@ -161,6 +161,8 @@ const $tabIndex = Symbol();
 exports.$tabIndex = $tabIndex;
 exports.$tabIndex = $tabIndex;
 const $text = Symbol();
 const $text = Symbol();
 exports.$text = $text;
 exports.$text = $text;
+const $toPages = Symbol();
+exports.$toPages = $toPages;
 const $toHTML = Symbol();
 const $toHTML = Symbol();
 exports.$toHTML = $toHTML;
 exports.$toHTML = $toHTML;
 const $toString = Symbol();
 const $toString = Symbol();
@@ -921,11 +923,13 @@ class XmlObject extends XFAObject {
       return;
       return;
     }
     }
 
 
+    const utf8TagName = (0, _util.utf8StringToString)(tagName);
     const prefix = this[$namespaceId] === NS_DATASETS ? "xfa:" : "";
     const prefix = this[$namespaceId] === NS_DATASETS ? "xfa:" : "";
-    buf.push(`<${prefix}${tagName}`);
+    buf.push(`<${prefix}${utf8TagName}`);
 
 
     for (const [name, value] of this[_attributes].entries()) {
     for (const [name, value] of this[_attributes].entries()) {
-      buf.push(` ${name}="${(0, _core_utils.encodeToXmlString)(value[$content])}"`);
+      const utf8Name = (0, _util.utf8StringToString)(name);
+      buf.push(` ${utf8Name}="${(0, _core_utils.encodeToXmlString)(value[$content])}"`);
     }
     }
 
 
     if (this[_dataValue] !== null) {
     if (this[_dataValue] !== null) {
@@ -955,7 +959,7 @@ class XmlObject extends XFAObject {
       }
       }
     }
     }
 
 
-    buf.push(`</${prefix}${tagName}>`);
+    buf.push(`</${prefix}${utf8TagName}>`);
   }
   }
 
 
   [$onChild](child) {
   [$onChild](child) {

+ 37 - 8
lib/core/xfa/xhtml.js

@@ -35,15 +35,17 @@ var _html_utils = require("./html_utils.js");
 var _utils = require("./utils.js");
 var _utils = require("./utils.js");
 
 
 const XHTML_NS_ID = _namespaces.NamespaceIds.xhtml.id;
 const XHTML_NS_ID = _namespaces.NamespaceIds.xhtml.id;
+const $richText = Symbol();
 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 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, original) => {
 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, original) => {
   value = original.fontSize = (0, _utils.getMeasurement)(value);
   value = original.fontSize = (0, _utils.getMeasurement)(value);
   return (0, _html_utils.measureToString)(0.99 * value);
   return (0, _html_utils.measureToString)(0.99 * value);
-}], ["letter-spacing", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["line-height", value => (0, _html_utils.measureToString)((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 => value]]);
+}], ["letter-spacing", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["line-height", value => (0, _html_utils.measureToString)((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 => value], ["vertical-align", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))]]);
 const spacesRegExp = /\s+/g;
 const spacesRegExp = /\s+/g;
 const crlfRegExp = /[\r\n]+/g;
 const crlfRegExp = /[\r\n]+/g;
+const crlfForRichTextRegExp = /\r\n?/g;
 
 
-function mapStyle(styleStr, node) {
+function mapStyle(styleStr, node, richText) {
   const style = Object.create(null);
   const style = Object.create(null);
 
 
   if (!styleStr) {
   if (!styleStr) {
@@ -89,6 +91,14 @@ function mapStyle(styleStr, node) {
     }, node, node[_xfa_object.$globalData].fontFinder, style);
     }, node, node[_xfa_object.$globalData].fontFinder, style);
   }
   }
 
 
+  if (richText && style.verticalAlign && style.verticalAlign !== "0px" && style.fontSize) {
+    const SUB_SUPER_SCRIPT_FACTOR = 0.583;
+    const VERTICAL_FACTOR = 0.333;
+    const fontSize = (0, _utils.getMeasurement)(style.fontSize);
+    style.fontSize = (0, _html_utils.measureToString)(fontSize * SUB_SUPER_SCRIPT_FACTOR);
+    style.verticalAlign = (0, _html_utils.measureToString)(Math.sign((0, _utils.getMeasurement)(style.verticalAlign)) * fontSize * VERTICAL_FACTOR);
+  }
+
   (0, _html_utils.fixTextIndent)(style);
   (0, _html_utils.fixTextIndent)(style);
   return style;
   return style;
 }
 }
@@ -112,6 +122,7 @@ const NoWhites = new Set(["body", "html"]);
 class XhtmlObject extends _xfa_object.XmlObject {
 class XhtmlObject extends _xfa_object.XmlObject {
   constructor(attributes, name) {
   constructor(attributes, name) {
     super(XHTML_NS_ID, name);
     super(XHTML_NS_ID, name);
+    this[$richText] = false;
     this.style = attributes.style || "";
     this.style = attributes.style || "";
   }
   }
 
 
@@ -125,11 +136,15 @@ class XhtmlObject extends _xfa_object.XmlObject {
     return !NoWhites.has(this[_xfa_object.$nodeName]);
     return !NoWhites.has(this[_xfa_object.$nodeName]);
   }
   }
 
 
-  [_xfa_object.$onText](str) {
-    str = str.replace(crlfRegExp, "");
+  [_xfa_object.$onText](str, richText = false) {
+    if (!richText) {
+      str = str.replace(crlfRegExp, "");
 
 
-    if (!this.style.includes("xfa-spacerun:yes")) {
-      str = str.replace(spacesRegExp, " ");
+      if (!this.style.includes("xfa-spacerun:yes")) {
+        str = str.replace(spacesRegExp, " ");
+      }
+    } else {
+      this[$richText] = true;
     }
     }
 
 
     if (str) {
     if (str) {
@@ -252,14 +267,22 @@ class XhtmlObject extends _xfa_object.XmlObject {
       return _utils.HTMLResult.EMPTY;
       return _utils.HTMLResult.EMPTY;
     }
     }
 
 
+    let value;
+
+    if (this[$richText]) {
+      value = this[_xfa_object.$content] ? this[_xfa_object.$content].replace(crlfForRichTextRegExp, "\n") : undefined;
+    } else {
+      value = this[_xfa_object.$content] || undefined;
+    }
+
     return _utils.HTMLResult.success({
     return _utils.HTMLResult.success({
       name: this[_xfa_object.$nodeName],
       name: this[_xfa_object.$nodeName],
       attributes: {
       attributes: {
         href: this.href,
         href: this.href,
-        style: mapStyle(this.style, this)
+        style: mapStyle(this.style, this, this[$richText])
       },
       },
       children,
       children,
-      value: this[_xfa_object.$content] || ""
+      value
     });
     });
   }
   }
 
 
@@ -423,6 +446,12 @@ class P extends XhtmlObject {
   }
   }
 
 
   [_xfa_object.$text]() {
   [_xfa_object.$text]() {
+    const siblings = this[_xfa_object.$getParent]()[_xfa_object.$getChildren]();
+
+    if (siblings[siblings.length - 1] === this) {
+      return super[_xfa_object.$text]();
+    }
+
     return super[_xfa_object.$text]() + "\n";
     return super[_xfa_object.$text]() + "\n";
   }
   }
 
 

+ 91 - 40
lib/core/xref.js

@@ -30,9 +30,11 @@ var _util = require("../shared/util.js");
 
 
 var _primitives = require("./primitives.js");
 var _primitives = require("./primitives.js");
 
 
+var _core_utils = require("./core_utils.js");
+
 var _parser = require("./parser.js");
 var _parser = require("./parser.js");
 
 
-var _core_utils = require("./core_utils.js");
+var _base_stream = require("./base_stream.js");
 
 
 var _crypto = require("./crypto.js");
 var _crypto = require("./crypto.js");
 
 
@@ -43,10 +45,8 @@ class XRef {
     this.entries = [];
     this.entries = [];
     this.xrefstms = Object.create(null);
     this.xrefstms = Object.create(null);
     this._cacheMap = new Map();
     this._cacheMap = new Map();
-    this.stats = {
-      streamTypes: Object.create(null),
-      fontTypes: Object.create(null)
-    };
+    this._pendingRefs = new _primitives.RefSet();
+    this.stats = new _core_utils.DocStats(pdfManager.msgHandler);
     this._newRefNum = null;
     this._newRefNum = null;
   }
   }
 
 
@@ -90,7 +90,7 @@ class XRef {
       (0, _util.warn)(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
       (0, _util.warn)(`XRef.parse - Invalid "Encrypt" reference: "${ex}".`);
     }
     }
 
 
-    if ((0, _primitives.isDict)(encrypt)) {
+    if (encrypt instanceof _primitives.Dict) {
       const ids = trailerDict.get("ID");
       const ids = trailerDict.get("ID");
       const fileId = ids && ids.length ? ids[0] : "";
       const fileId = ids && ids.length ? ids[0] : "";
       encrypt.suppressEncryption = true;
       encrypt.suppressEncryption = true;
@@ -109,15 +109,28 @@ class XRef {
       (0, _util.warn)(`XRef.parse - Invalid "Root" reference: "${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();
+    if (root instanceof _primitives.Dict) {
+      try {
+        const pages = root.get("Pages");
+
+        if (pages instanceof _primitives.Dict) {
+          this.root = root;
+          return;
+        }
+      } catch (ex) {
+        if (ex instanceof _core_utils.MissingDataException) {
+          throw ex;
+        }
+
+        (0, _util.warn)(`XRef.parse - Invalid "Pages" reference: "${ex}".`);
       }
       }
+    }
 
 
-      throw new _util.FormatError("Invalid root reference");
+    if (!recoveryMode) {
+      throw new _core_utils.XRefParseException();
     }
     }
+
+    throw new _util.InvalidPDFException("Invalid Root reference.");
   }
   }
 
 
   processXRefTable(parser) {
   processXRefTable(parser) {
@@ -138,11 +151,11 @@ class XRef {
 
 
     let dict = parser.getObj();
     let dict = parser.getObj();
 
 
-    if (!(0, _primitives.isDict)(dict) && dict.dict) {
+    if (!(dict instanceof _primitives.Dict) && dict.dict) {
       dict = dict.dict;
       dict = dict.dict;
     }
     }
 
 
-    if (!(0, _primitives.isDict)(dict)) {
+    if (!(dict instanceof _primitives.Dict)) {
       throw new _util.FormatError("Invalid XRef table: could not parse trailer dictionary");
       throw new _util.FormatError("Invalid XRef table: could not parse trailer dictionary");
     }
     }
 
 
@@ -249,18 +262,13 @@ class XRef {
   }
   }
 
 
   readXRefStream(stream) {
   readXRefStream(stream) {
-    let i, j;
     const streamState = this.streamState;
     const streamState = this.streamState;
     stream.pos = streamState.streamPos;
     stream.pos = streamState.streamPos;
-    const byteWidths = streamState.byteWidths;
-    const typeFieldWidth = byteWidths[0];
-    const offsetFieldWidth = byteWidths[1];
-    const generationFieldWidth = byteWidths[2];
+    const [typeFieldWidth, offsetFieldWidth, generationFieldWidth] = streamState.byteWidths;
     const entryRanges = streamState.entryRanges;
     const entryRanges = streamState.entryRanges;
 
 
     while (entryRanges.length > 0) {
     while (entryRanges.length > 0) {
-      const first = entryRanges[0];
-      const n = entryRanges[1];
+      const [first, n] = entryRanges;
 
 
       if (!Number.isInteger(first) || !Number.isInteger(n)) {
       if (!Number.isInteger(first) || !Number.isInteger(n)) {
         throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
         throw new _util.FormatError(`Invalid XRef range fields: ${first}, ${n}`);
@@ -270,27 +278,45 @@ class XRef {
         throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
         throw new _util.FormatError(`Invalid XRef entry fields length: ${first}, ${n}`);
       }
       }
 
 
-      for (i = streamState.entryNum; i < n; ++i) {
+      for (let i = streamState.entryNum; i < n; ++i) {
         streamState.entryNum = i;
         streamState.entryNum = i;
         streamState.streamPos = stream.pos;
         streamState.streamPos = stream.pos;
         let type = 0,
         let type = 0,
             offset = 0,
             offset = 0,
             generation = 0;
             generation = 0;
 
 
-        for (j = 0; j < typeFieldWidth; ++j) {
-          type = type << 8 | stream.getByte();
+        for (let j = 0; j < typeFieldWidth; ++j) {
+          const typeByte = stream.getByte();
+
+          if (typeByte === -1) {
+            throw new _util.FormatError("Invalid XRef byteWidths 'type'.");
+          }
+
+          type = type << 8 | typeByte;
         }
         }
 
 
         if (typeFieldWidth === 0) {
         if (typeFieldWidth === 0) {
           type = 1;
           type = 1;
         }
         }
 
 
-        for (j = 0; j < offsetFieldWidth; ++j) {
-          offset = offset << 8 | stream.getByte();
+        for (let j = 0; j < offsetFieldWidth; ++j) {
+          const offsetByte = stream.getByte();
+
+          if (offsetByte === -1) {
+            throw new _util.FormatError("Invalid XRef byteWidths 'offset'.");
+          }
+
+          offset = offset << 8 | offsetByte;
         }
         }
 
 
-        for (j = 0; j < generationFieldWidth; ++j) {
-          generation = generation << 8 | stream.getByte();
+        for (let j = 0; j < generationFieldWidth; ++j) {
+          const generationByte = stream.getByte();
+
+          if (generationByte === -1) {
+            throw new _util.FormatError("Invalid XRef byteWidths 'generation'.");
+          }
+
+          generation = generation << 8 | generationByte;
         }
         }
 
 
         const entry = {};
         const entry = {};
@@ -380,6 +406,9 @@ class XRef {
     const objBytes = new Uint8Array([111, 98, 106]);
     const objBytes = new Uint8Array([111, 98, 106]);
     const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
     const xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
     this.entries.length = 0;
     this.entries.length = 0;
+
+    this._cacheMap.clear();
+
     const stream = this.stream;
     const stream = this.stream;
     stream.pos = 0;
     stream.pos = 0;
     const buffer = stream.getBytes(),
     const buffer = stream.getBytes(),
@@ -511,7 +540,7 @@ class XRef {
 
 
       const dict = parser.getObj();
       const dict = parser.getObj();
 
 
-      if (!(0, _primitives.isDict)(dict)) {
+      if (!(dict instanceof _primitives.Dict)) {
         continue;
         continue;
       }
       }
 
 
@@ -548,6 +577,10 @@ class XRef {
       return trailerDict;
       return trailerDict;
     }
     }
 
 
+    if (this.topDict) {
+      return this.topDict;
+    }
+
     throw new _util.InvalidPDFException("Invalid PDF structure.");
     throw new _util.InvalidPDFException("Invalid PDF structure.");
   }
   }
 
 
@@ -593,7 +626,7 @@ class XRef {
             }
             }
           }
           }
         } else if (Number.isInteger(obj)) {
         } else if (Number.isInteger(obj)) {
-          if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), "obj") || !(0, _primitives.isStream)(obj = parser.getObj())) {
+          if (!Number.isInteger(parser.getObj()) || !(0, _primitives.isCmd)(parser.getObj(), "obj") || !((obj = parser.getObj()) instanceof _base_stream.BaseStream)) {
             throw new _util.FormatError("Invalid XRef stream");
             throw new _util.FormatError("Invalid XRef stream");
           }
           }
 
 
@@ -614,7 +647,7 @@ class XRef {
 
 
         if (Number.isInteger(obj)) {
         if (Number.isInteger(obj)) {
           this.startXRefQueue.push(obj);
           this.startXRefQueue.push(obj);
-        } else if ((0, _primitives.isRef)(obj)) {
+        } else if (obj instanceof _primitives.Ref) {
           this.startXRefQueue.push(obj.num);
           this.startXRefQueue.push(obj.num);
         }
         }
 
 
@@ -628,6 +661,7 @@ class XRef {
       }
       }
 
 
       (0, _util.info)("(while reading XRef): " + e);
       (0, _util.info)("(while reading XRef): " + e);
+      this.startXRefQueue.shift();
     }
     }
 
 
     if (recoveryMode) {
     if (recoveryMode) {
@@ -680,15 +714,32 @@ class XRef {
       return xrefEntry;
       return xrefEntry;
     }
     }
 
 
-    if (xrefEntry.uncompressed) {
-      xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
-    } else {
-      xrefEntry = this.fetchCompressed(ref, xrefEntry, suppressEncryption);
+    if (this._pendingRefs.has(ref)) {
+      this._pendingRefs.remove(ref);
+
+      (0, _util.warn)(`Ignoring circular reference: ${ref}.`);
+      return _primitives.CIRCULAR_REF;
+    }
+
+    this._pendingRefs.put(ref);
+
+    try {
+      if (xrefEntry.uncompressed) {
+        xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
+      } else {
+        xrefEntry = this.fetchCompressed(ref, xrefEntry, suppressEncryption);
+      }
+
+      this._pendingRefs.remove(ref);
+    } catch (ex) {
+      this._pendingRefs.remove(ref);
+
+      throw ex;
     }
     }
 
 
-    if ((0, _primitives.isDict)(xrefEntry)) {
+    if (xrefEntry instanceof _primitives.Dict) {
       xrefEntry.objId = ref.toString();
       xrefEntry.objId = ref.toString();
-    } else if ((0, _primitives.isStream)(xrefEntry)) {
+    } else if (xrefEntry instanceof _base_stream.BaseStream) {
       xrefEntry.dict.objId = ref.toString();
       xrefEntry.dict.objId = ref.toString();
     }
     }
 
 
@@ -735,7 +786,7 @@ class XRef {
       xrefEntry = parser.getObj();
       xrefEntry = parser.getObj();
     }
     }
 
 
-    if (!(0, _primitives.isStream)(xrefEntry)) {
+    if (!(xrefEntry instanceof _base_stream.BaseStream)) {
       this._cacheMap.set(num, xrefEntry);
       this._cacheMap.set(num, xrefEntry);
     }
     }
 
 
@@ -746,7 +797,7 @@ class XRef {
     const tableOffset = xrefEntry.offset;
     const tableOffset = xrefEntry.offset;
     const stream = this.fetch(_primitives.Ref.get(tableOffset, 0));
     const stream = this.fetch(_primitives.Ref.get(tableOffset, 0));
 
 
-    if (!(0, _primitives.isStream)(stream)) {
+    if (!(stream instanceof _base_stream.BaseStream)) {
       throw new _util.FormatError("bad ObjStm stream");
       throw new _util.FormatError("bad ObjStm stream");
     }
     }
 
 
@@ -800,7 +851,7 @@ class XRef {
       const obj = parser.getObj();
       const obj = parser.getObj();
       entries[i] = obj;
       entries[i] = obj;
 
 
-      if ((0, _primitives.isStream)(obj)) {
+      if (obj instanceof _base_stream.BaseStream) {
         continue;
         continue;
       }
       }
 
 

+ 147 - 36
lib/display/annotation_layer.js

@@ -34,6 +34,8 @@ var _annotation_storage = require("./annotation_storage.js");
 
 
 var _scripting_utils = require("../shared/scripting_utils.js");
 var _scripting_utils = require("../shared/scripting_utils.js");
 
 
+var _xfa_layer = require("./xfa_layer.js");
+
 const DEFAULT_TAB_INDEX = 1000;
 const DEFAULT_TAB_INDEX = 1000;
 const GetElementsByNameSet = new WeakSet();
 const GetElementsByNameSet = new WeakSet();
 
 
@@ -164,7 +166,25 @@ class AnnotationElement {
 
 
     const rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
     const rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
 
 
-    container.style.transform = `matrix(${viewport.transform.join(",")})`;
+    if (data.hasOwnCanvas) {
+      const transform = viewport.transform.slice();
+
+      const [scaleX, scaleY] = _util.Util.singularValueDecompose2dScale(transform);
+
+      width = Math.ceil(width * scaleX);
+      height = Math.ceil(height * scaleY);
+      rect[0] *= scaleX;
+      rect[1] *= scaleY;
+
+      for (let i = 0; i < 4; i++) {
+        transform[i] = Math.sign(transform[i]);
+      }
+
+      container.style.transform = `matrix(${transform.join(",")})`;
+    } else {
+      container.style.transform = `matrix(${viewport.transform.join(",")})`;
+    }
+
     container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`;
     container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`;
 
 
     if (!ignoreBorder && data.borderStyle.width > 0) {
     if (!ignoreBorder && data.borderStyle.width > 0) {
@@ -219,8 +239,14 @@ class AnnotationElement {
 
 
     container.style.left = `${rect[0]}px`;
     container.style.left = `${rect[0]}px`;
     container.style.top = `${rect[1]}px`;
     container.style.top = `${rect[1]}px`;
-    container.style.width = `${width}px`;
-    container.style.height = `${height}px`;
+
+    if (data.hasOwnCanvas) {
+      container.style.width = container.style.height = "auto";
+    } else {
+      container.style.width = `${width}px`;
+      container.style.height = `${height}px`;
+    }
+
     return container;
     return container;
   }
   }
 
 
@@ -263,6 +289,7 @@ class AnnotationElement {
       titleObj: data.titleObj,
       titleObj: data.titleObj,
       modificationDate: data.modificationDate,
       modificationDate: data.modificationDate,
       contentsObj: data.contentsObj,
       contentsObj: data.contentsObj,
+      richText: data.richText,
       hideWrapper: true
       hideWrapper: true
     });
     });
     const popup = popupElement.render();
     const popup = popupElement.render();
@@ -356,10 +383,11 @@ class AnnotationElement {
 }
 }
 
 
 class LinkAnnotationElement extends AnnotationElement {
 class LinkAnnotationElement extends AnnotationElement {
-  constructor(parameters) {
+  constructor(parameters, options = null) {
     const isRenderable = !!(parameters.data.url || parameters.data.dest || parameters.data.action || parameters.data.isTooltipOnly || parameters.data.resetForm || parameters.data.actions && (parameters.data.actions.Action || parameters.data.actions["Mouse Up"] || parameters.data.actions["Mouse Down"]));
     const isRenderable = !!(parameters.data.url || parameters.data.dest || parameters.data.action || parameters.data.isTooltipOnly || parameters.data.resetForm || parameters.data.actions && (parameters.data.actions.Action || parameters.data.actions["Mouse Up"] || parameters.data.actions["Mouse Down"]));
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
+      ignoreBorder: !!options?.ignoreBorder,
       createQuadrilaterals: true
       createQuadrilaterals: true
     });
     });
   }
   }
@@ -596,7 +624,7 @@ class LinkAnnotationElement extends AnnotationElement {
 
 
 class TextAnnotationElement extends AnnotationElement {
 class TextAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable
       isRenderable
     });
     });
@@ -1203,6 +1231,12 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
 }
 }
 
 
 class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
 class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
+  constructor(parameters) {
+    super(parameters, {
+      ignoreBorder: parameters.data.hasAppearance
+    });
+  }
+
   render() {
   render() {
     const container = super.render();
     const container = super.render();
     container.className = "buttonWidgetAnnotation pushButton";
     container.className = "buttonWidgetAnnotation pushButton";
@@ -1456,7 +1490,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
 
 
 class PopupAnnotationElement extends AnnotationElement {
 class PopupAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable
       isRenderable
     });
     });
@@ -1483,7 +1517,8 @@ class PopupAnnotationElement extends AnnotationElement {
       color: this.data.color,
       color: this.data.color,
       titleObj: this.data.titleObj,
       titleObj: this.data.titleObj,
       modificationDate: this.data.modificationDate,
       modificationDate: this.data.modificationDate,
-      contentsObj: this.data.contentsObj
+      contentsObj: this.data.contentsObj,
+      richText: this.data.richText
     });
     });
     const page = this.page;
     const page = this.page;
 
 
@@ -1508,6 +1543,7 @@ class PopupElement {
     this.titleObj = parameters.titleObj;
     this.titleObj = parameters.titleObj;
     this.modificationDate = parameters.modificationDate;
     this.modificationDate = parameters.modificationDate;
     this.contentsObj = parameters.contentsObj;
     this.contentsObj = parameters.contentsObj;
+    this.richText = parameters.richText;
     this.hideWrapper = parameters.hideWrapper || false;
     this.hideWrapper = parameters.hideWrapper || false;
     this.pinned = false;
     this.pinned = false;
   }
   }
@@ -1538,6 +1574,7 @@ class PopupElement {
 
 
     if (dateObject) {
     if (dateObject) {
       const modificationDate = document.createElement("span");
       const modificationDate = document.createElement("span");
+      modificationDate.className = "popupDate";
       modificationDate.textContent = "{{date}}, {{time}}";
       modificationDate.textContent = "{{date}}, {{time}}";
       modificationDate.dataset.l10nId = "annotation_date_string";
       modificationDate.dataset.l10nId = "annotation_date_string";
       modificationDate.dataset.l10nArgs = JSON.stringify({
       modificationDate.dataset.l10nArgs = JSON.stringify({
@@ -1547,9 +1584,19 @@ class PopupElement {
       popup.appendChild(modificationDate);
       popup.appendChild(modificationDate);
     }
     }
 
 
-    const contents = this._formatContents(this.contentsObj);
+    if (this.richText?.str && (!this.contentsObj?.str || this.contentsObj.str === this.richText.str)) {
+      _xfa_layer.XfaLayer.render({
+        xfaHtml: this.richText.html,
+        intent: "richText",
+        div: popup
+      });
 
 
-    popup.appendChild(contents);
+      popup.lastChild.className = "richText popupContent";
+    } else {
+      const contents = this._formatContents(this.contentsObj);
+
+      popup.appendChild(contents);
+    }
 
 
     if (!Array.isArray(this.trigger)) {
     if (!Array.isArray(this.trigger)) {
       this.trigger = [this.trigger];
       this.trigger = [this.trigger];
@@ -1571,6 +1618,7 @@ class PopupElement {
     dir
     dir
   }) {
   }) {
     const p = document.createElement("p");
     const p = document.createElement("p");
+    p.className = "popupContent";
     p.dir = dir;
     p.dir = dir;
     const lines = str.split(/(?:\r\n?|\n)/);
     const lines = str.split(/(?:\r\n?|\n)/);
 
 
@@ -1620,7 +1668,7 @@ class PopupElement {
 
 
 class FreeTextAnnotationElement extends AnnotationElement {
 class FreeTextAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1641,7 +1689,7 @@ class FreeTextAnnotationElement extends AnnotationElement {
 
 
 class LineAnnotationElement extends AnnotationElement {
 class LineAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1661,6 +1709,7 @@ class LineAnnotationElement extends AnnotationElement {
     line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
     line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
     line.setAttribute("stroke-width", data.borderStyle.width || 1);
     line.setAttribute("stroke-width", data.borderStyle.width || 1);
     line.setAttribute("stroke", "transparent");
     line.setAttribute("stroke", "transparent");
+    line.setAttribute("fill", "transparent");
     svg.appendChild(line);
     svg.appendChild(line);
     this.container.append(svg);
     this.container.append(svg);
 
 
@@ -1673,7 +1722,7 @@ class LineAnnotationElement extends AnnotationElement {
 
 
 class SquareAnnotationElement extends AnnotationElement {
 class SquareAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1694,7 +1743,7 @@ class SquareAnnotationElement extends AnnotationElement {
     square.setAttribute("height", height - borderWidth);
     square.setAttribute("height", height - borderWidth);
     square.setAttribute("stroke-width", borderWidth || 1);
     square.setAttribute("stroke-width", borderWidth || 1);
     square.setAttribute("stroke", "transparent");
     square.setAttribute("stroke", "transparent");
-    square.setAttribute("fill", "none");
+    square.setAttribute("fill", "transparent");
     svg.appendChild(square);
     svg.appendChild(square);
     this.container.append(svg);
     this.container.append(svg);
 
 
@@ -1707,7 +1756,7 @@ class SquareAnnotationElement extends AnnotationElement {
 
 
 class CircleAnnotationElement extends AnnotationElement {
 class CircleAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1728,7 +1777,7 @@ class CircleAnnotationElement extends AnnotationElement {
     circle.setAttribute("ry", height / 2 - borderWidth / 2);
     circle.setAttribute("ry", height / 2 - borderWidth / 2);
     circle.setAttribute("stroke-width", borderWidth || 1);
     circle.setAttribute("stroke-width", borderWidth || 1);
     circle.setAttribute("stroke", "transparent");
     circle.setAttribute("stroke", "transparent");
-    circle.setAttribute("fill", "none");
+    circle.setAttribute("fill", "transparent");
     svg.appendChild(circle);
     svg.appendChild(circle);
     this.container.append(svg);
     this.container.append(svg);
 
 
@@ -1741,7 +1790,7 @@ class CircleAnnotationElement extends AnnotationElement {
 
 
 class PolylineAnnotationElement extends AnnotationElement {
 class PolylineAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1769,7 +1818,7 @@ class PolylineAnnotationElement extends AnnotationElement {
     polyline.setAttribute("points", points);
     polyline.setAttribute("points", points);
     polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
     polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
     polyline.setAttribute("stroke", "transparent");
     polyline.setAttribute("stroke", "transparent");
-    polyline.setAttribute("fill", "none");
+    polyline.setAttribute("fill", "transparent");
     svg.appendChild(polyline);
     svg.appendChild(polyline);
     this.container.append(svg);
     this.container.append(svg);
 
 
@@ -1791,7 +1840,7 @@ class PolygonAnnotationElement extends PolylineAnnotationElement {
 
 
 class CaretAnnotationElement extends AnnotationElement {
 class CaretAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1812,7 +1861,7 @@ class CaretAnnotationElement extends AnnotationElement {
 
 
 class InkAnnotationElement extends AnnotationElement {
 class InkAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -1842,7 +1891,7 @@ class InkAnnotationElement extends AnnotationElement {
       polyline.setAttribute("points", points);
       polyline.setAttribute("points", points);
       polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
       polyline.setAttribute("stroke-width", data.borderStyle.width || 1);
       polyline.setAttribute("stroke", "transparent");
       polyline.setAttribute("stroke", "transparent");
-      polyline.setAttribute("fill", "none");
+      polyline.setAttribute("fill", "transparent");
 
 
       this._createPopup(polyline, data);
       this._createPopup(polyline, data);
 
 
@@ -1857,7 +1906,7 @@ class InkAnnotationElement extends AnnotationElement {
 
 
 class HighlightAnnotationElement extends AnnotationElement {
 class HighlightAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true,
       ignoreBorder: true,
@@ -1882,7 +1931,7 @@ class HighlightAnnotationElement extends AnnotationElement {
 
 
 class UnderlineAnnotationElement extends AnnotationElement {
 class UnderlineAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true,
       ignoreBorder: true,
@@ -1907,7 +1956,7 @@ class UnderlineAnnotationElement extends AnnotationElement {
 
 
 class SquigglyAnnotationElement extends AnnotationElement {
 class SquigglyAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true,
       ignoreBorder: true,
@@ -1932,7 +1981,7 @@ class SquigglyAnnotationElement extends AnnotationElement {
 
 
 class StrikeOutAnnotationElement extends AnnotationElement {
 class StrikeOutAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true,
       ignoreBorder: true,
@@ -1957,7 +2006,7 @@ class StrikeOutAnnotationElement extends AnnotationElement {
 
 
 class StampAnnotationElement extends AnnotationElement {
 class StampAnnotationElement extends AnnotationElement {
   constructor(parameters) {
   constructor(parameters) {
-    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str);
+    const isRenderable = !!(parameters.data.hasPopup || parameters.data.titleObj?.str || parameters.data.contentsObj?.str || parameters.data.richText?.str);
     super(parameters, {
     super(parameters, {
       isRenderable,
       isRenderable,
       ignoreBorder: true
       ignoreBorder: true
@@ -2002,7 +2051,7 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
     trigger.style.width = this.container.style.width;
     trigger.style.width = this.container.style.width;
     trigger.addEventListener("dblclick", this._download.bind(this));
     trigger.addEventListener("dblclick", this._download.bind(this));
 
 
-    if (!this.data.hasPopup && (this.data.titleObj?.str || this.data.contentsObj?.str)) {
+    if (!this.data.hasPopup && (this.data.titleObj?.str || this.data.contentsObj?.str || this.data.richText)) {
       this._createPopup(trigger, this.data);
       this._createPopup(trigger, this.data);
     }
     }
 
 
@@ -2038,10 +2087,12 @@ class AnnotationLayer {
       sortedAnnotations.push(...popupAnnotations);
       sortedAnnotations.push(...popupAnnotations);
     }
     }
 
 
+    const div = parameters.div;
+
     for (const data of sortedAnnotations) {
     for (const data of sortedAnnotations) {
       const element = AnnotationElementFactory.create({
       const element = AnnotationElementFactory.create({
         data,
         data,
-        layer: parameters.div,
+        layer: div,
         page: parameters.page,
         page: parameters.page,
         viewport: parameters.viewport,
         viewport: parameters.viewport,
         linkService: parameters.linkService,
         linkService: parameters.linkService,
@@ -2067,33 +2118,93 @@ class AnnotationLayer {
 
 
         if (Array.isArray(rendered)) {
         if (Array.isArray(rendered)) {
           for (const renderedElement of rendered) {
           for (const renderedElement of rendered) {
-            parameters.div.appendChild(renderedElement);
+            div.appendChild(renderedElement);
           }
           }
         } else {
         } else {
           if (element instanceof PopupAnnotationElement) {
           if (element instanceof PopupAnnotationElement) {
-            parameters.div.prepend(rendered);
+            div.prepend(rendered);
           } else {
           } else {
-            parameters.div.appendChild(rendered);
+            div.appendChild(rendered);
           }
           }
         }
         }
       }
       }
     }
     }
+
+    this.#setAnnotationCanvasMap(div, parameters.annotationCanvasMap);
   }
   }
 
 
   static update(parameters) {
   static update(parameters) {
-    const transform = `matrix(${parameters.viewport.transform.join(",")})`;
-
-    for (const data of parameters.annotations) {
-      const elements = parameters.div.querySelectorAll(`[data-annotation-id="${data.id}"]`);
+    const {
+      page,
+      viewport,
+      annotations,
+      annotationCanvasMap,
+      div
+    } = parameters;
+    const transform = viewport.transform;
+    const matrix = `matrix(${transform.join(",")})`;
+    let scale, ownMatrix;
+
+    for (const data of annotations) {
+      const elements = div.querySelectorAll(`[data-annotation-id="${data.id}"]`);
 
 
       if (elements) {
       if (elements) {
         for (const element of elements) {
         for (const element of elements) {
-          element.style.transform = transform;
+          if (data.hasOwnCanvas) {
+            const rect = _util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
+
+            if (!ownMatrix) {
+              scale = Math.abs(transform[0] || transform[1]);
+              const ownTransform = transform.slice();
+
+              for (let i = 0; i < 4; i++) {
+                ownTransform[i] = Math.sign(ownTransform[i]);
+              }
+
+              ownMatrix = `matrix(${ownTransform.join(",")})`;
+            }
+
+            const left = rect[0] * scale;
+            const top = rect[1] * scale;
+            element.style.left = `${left}px`;
+            element.style.top = `${top}px`;
+            element.style.transformOrigin = `${-left}px ${-top}px`;
+            element.style.transform = ownMatrix;
+          } else {
+            element.style.transform = matrix;
+          }
         }
         }
       }
       }
     }
     }
 
 
-    parameters.div.hidden = false;
+    this.#setAnnotationCanvasMap(div, annotationCanvasMap);
+    div.hidden = false;
+  }
+
+  static #setAnnotationCanvasMap(div, annotationCanvasMap) {
+    if (!annotationCanvasMap) {
+      return;
+    }
+
+    for (const [id, canvas] of annotationCanvasMap) {
+      const element = div.querySelector(`[data-annotation-id="${id}"]`);
+
+      if (!element) {
+        continue;
+      }
+
+      const {
+        firstChild
+      } = element;
+
+      if (firstChild.nodeName === "CANVAS") {
+        element.replaceChild(canvas, firstChild);
+      } else {
+        element.insertBefore(canvas, firstChild);
+      }
+    }
+
+    annotationCanvasMap.clear();
   }
   }
 
 
 }
 }

+ 136 - 128
lib/display/api.js

@@ -24,9 +24,10 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.build = exports.RenderTask = exports.PDFWorker = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFDocumentLoadingTask = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.DefaultStandardFontDataFactory = exports.DefaultCanvasFactory = exports.DefaultCMapReaderFactory = void 0;
 exports.getDocument = getDocument;
 exports.getDocument = getDocument;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
-exports.version = exports.RenderTask = exports.PDFWorker = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFDocumentLoadingTask = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.DefaultStandardFontDataFactory = exports.DefaultCMapReaderFactory = exports.DefaultCanvasFactory = exports.build = void 0;
+exports.version = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
@@ -247,7 +248,6 @@ function getDocument(src) {
       }
       }
 
 
       const messageHandler = new _message_handler.MessageHandler(docId, workerId, worker.port);
       const messageHandler = new _message_handler.MessageHandler(docId, workerId, worker.port);
-      messageHandler.postMessageTransfers = worker.postMessageTransfers;
       const transport = new WorkerTransport(messageHandler, task, networkStream, params);
       const transport = new WorkerTransport(messageHandler, task, networkStream, params);
       task._transport = transport;
       task._transport = transport;
       messageHandler.send("Ready", null);
       messageHandler.send("Ready", null);
@@ -270,7 +270,7 @@ async function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
 
 
   const workerId = await worker.messageHandler.sendWithPromise("GetDocRequest", {
   const workerId = await worker.messageHandler.sendWithPromise("GetDocRequest", {
     docId,
     docId,
-    apiVersion: '2.11.338',
+    apiVersion: '2.12.313',
     source: {
     source: {
       data: source.data,
       data: source.data,
       url: source.url,
       url: source.url,
@@ -281,7 +281,6 @@ async function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
     },
     },
     maxImageSize: source.maxImageSize,
     maxImageSize: source.maxImageSize,
     disableFontFace: source.disableFontFace,
     disableFontFace: source.disableFontFace,
-    postMessageTransfers: worker.postMessageTransfers,
     docBaseUrl: source.docBaseUrl,
     docBaseUrl: source.docBaseUrl,
     ignoreErrors: source.ignoreErrors,
     ignoreErrors: source.ignoreErrors,
     isEvalSupported: source.isEvalSupported,
     isEvalSupported: source.isEvalSupported,
@@ -421,6 +420,15 @@ class PDFDocumentProxy {
       }
       }
 
 
     });
     });
+    Object.defineProperty(this, "getStats", {
+      value: async () => {
+        (0, _display_utils.deprecated)("`PDFDocumentProxy.getStats`, " + "please use the `PDFDocumentProxy.stats`-getter instead.");
+        return this.stats || {
+          streamTypes: {},
+          fontTypes: {}
+        };
+      }
+    });
   }
   }
 
 
   get annotationStorage() {
   get annotationStorage() {
@@ -435,6 +443,10 @@ class PDFDocumentProxy {
     return this._pdfInfo.fingerprints;
     return this._pdfInfo.fingerprints;
   }
   }
 
 
+  get stats() {
+    return this._transport.stats;
+  }
+
   get isPureXfa() {
   get isPureXfa() {
     return !!this._transport._htmlForXfa;
     return !!this._transport._htmlForXfa;
   }
   }
@@ -519,10 +531,6 @@ class PDFDocumentProxy {
     return this._transport.downloadInfoCapability.promise;
     return this._transport.downloadInfoCapability.promise;
   }
   }
 
 
-  getStats() {
-    return this._transport.getStats();
-  }
-
   cleanup(keepLoadedFonts = false) {
   cleanup(keepLoadedFonts = false) {
     return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
     return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
   }
   }
@@ -676,7 +684,8 @@ class PDFPageProxy {
     imageLayer = null,
     imageLayer = null,
     canvasFactory = null,
     canvasFactory = null,
     background = null,
     background = null,
-    optionalContentConfigPromise = null
+    optionalContentConfigPromise = null,
+    annotationCanvasMap = null
   }) {
   }) {
     if (arguments[0]?.renderInteractiveForms !== undefined) {
     if (arguments[0]?.renderInteractiveForms !== undefined) {
       (0, _display_utils.deprecated)("render no longer accepts the `renderInteractiveForms`-option, " + "please use the `annotationMode`-option instead.");
       (0, _display_utils.deprecated)("render no longer accepts the `renderInteractiveForms`-option, " + "please use the `annotationMode`-option instead.");
@@ -777,6 +786,7 @@ class PDFPageProxy {
       },
       },
       objs: this.objs,
       objs: this.objs,
       commonObjs: this.commonObjs,
       commonObjs: this.commonObjs,
+      annotationCanvasMap,
       operatorList: intentState.operatorList,
       operatorList: intentState.operatorList,
       pageIndex: this._pageIndex,
       pageIndex: this._pageIndex,
       canvasFactory: canvasFactoryInstance,
       canvasFactory: canvasFactoryInstance,
@@ -909,7 +919,6 @@ class PDFPageProxy {
 
 
   _destroy() {
   _destroy() {
     this.destroyed = true;
     this.destroyed = true;
-    this._transport.pageCache[this._pageIndex] = null;
     const waitOn = [];
     const waitOn = [];
 
 
     for (const intentState of this._intentStates.values()) {
     for (const intentState of this._intentStates.values()) {
@@ -1129,84 +1138,92 @@ exports.PDFPageProxy = PDFPageProxy;
 class LoopbackPort {
 class LoopbackPort {
   constructor() {
   constructor() {
     this._listeners = [];
     this._listeners = [];
-    this._deferred = Promise.resolve(undefined);
+    this._deferred = Promise.resolve();
   }
   }
 
 
   postMessage(obj, transfers) {
   postMessage(obj, transfers) {
-    function cloneValue(value) {
-      if (typeof value === "function" || typeof value === "symbol" || value instanceof URL) {
-        throw new Error(`LoopbackPort.postMessage - cannot clone: ${value?.toString()}`);
+    function cloneValue(object) {
+      if (globalThis.structuredClone) {
+        return globalThis.structuredClone(object, transfers);
       }
       }
 
 
-      if (typeof value !== "object" || value === null) {
-        return value;
-      }
+      function fallbackCloneValue(value) {
+        if (typeof value === "function" || typeof value === "symbol" || value instanceof URL) {
+          throw new Error(`LoopbackPort.postMessage - cannot clone: ${value?.toString()}`);
+        }
 
 
-      if (cloned.has(value)) {
-        return cloned.get(value);
-      }
+        if (typeof value !== "object" || value === null) {
+          return value;
+        }
 
 
-      let buffer, result;
+        if (cloned.has(value)) {
+          return cloned.get(value);
+        }
 
 
-      if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
-        if (transfers?.includes(buffer)) {
-          result = new value.constructor(buffer, value.byteOffset, value.byteLength);
-        } else {
-          result = new value.constructor(value);
+        let buffer, result;
+
+        if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) {
+          if (transfers?.includes(buffer)) {
+            result = new value.constructor(buffer, value.byteOffset, value.byteLength);
+          } else {
+            result = new value.constructor(value);
+          }
+
+          cloned.set(value, result);
+          return result;
         }
         }
 
 
-        cloned.set(value, result);
-        return result;
-      }
+        if (value instanceof Map) {
+          result = new Map();
+          cloned.set(value, result);
 
 
-      if (value instanceof Map) {
-        result = new Map();
-        cloned.set(value, result);
+          for (const [key, val] of value) {
+            result.set(key, fallbackCloneValue(val));
+          }
 
 
-        for (const [key, val] of value) {
-          result.set(key, cloneValue(val));
+          return result;
         }
         }
 
 
-        return result;
-      }
+        if (value instanceof Set) {
+          result = new Set();
+          cloned.set(value, result);
 
 
-      if (value instanceof Set) {
-        result = new Set();
-        cloned.set(value, result);
+          for (const val of value) {
+            result.add(fallbackCloneValue(val));
+          }
 
 
-        for (const val of value) {
-          result.add(cloneValue(val));
+          return result;
         }
         }
 
 
-        return result;
-      }
+        result = Array.isArray(value) ? [] : Object.create(null);
+        cloned.set(value, result);
 
 
-      result = Array.isArray(value) ? [] : Object.create(null);
-      cloned.set(value, result);
+        for (const i in value) {
+          let desc,
+              p = value;
 
 
-      for (const i in value) {
-        let desc,
-            p = value;
+          while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
+            p = Object.getPrototypeOf(p);
+          }
 
 
-        while (!(desc = Object.getOwnPropertyDescriptor(p, i))) {
-          p = Object.getPrototypeOf(p);
-        }
+          if (typeof desc.value === "undefined") {
+            continue;
+          }
 
 
-        if (typeof desc.value === "undefined") {
-          continue;
-        }
+          if (typeof desc.value === "function" && !value.hasOwnProperty?.(i)) {
+            continue;
+          }
 
 
-        if (typeof desc.value === "function" && !value.hasOwnProperty?.(i)) {
-          continue;
+          result[i] = fallbackCloneValue(desc.value);
         }
         }
 
 
-        result[i] = cloneValue(desc.value);
+        return result;
       }
       }
 
 
-      return result;
+      const cloned = new WeakMap();
+      return fallbackCloneValue(object);
     }
     }
 
 
-    const cloned = new WeakMap();
     const event = {
     const event = {
       data: cloneValue(obj)
       data: cloneValue(obj)
     };
     };
@@ -1274,7 +1291,6 @@ class PDFWorker {
 
 
     this.name = name;
     this.name = name;
     this.destroyed = false;
     this.destroyed = false;
-    this.postMessageTransfers = true;
     this.verbosity = verbosity;
     this.verbosity = verbosity;
     this._readyCapability = (0, _util.createPromiseCapability)();
     this._readyCapability = (0, _util.createPromiseCapability)();
     this._port = null;
     this._port = null;
@@ -1357,10 +1373,6 @@ class PDFWorker {
             this._port = worker;
             this._port = worker;
             this._webWorker = worker;
             this._webWorker = worker;
 
 
-            if (!data.supportTransfers) {
-              this.postMessageTransfers = false;
-            }
-
             this._readyCapability.resolve();
             this._readyCapability.resolve();
 
 
             messageHandler.send("configure", {
             messageHandler.send("configure", {
@@ -1389,7 +1401,7 @@ class PDFWorker {
         });
         });
 
 
         const sendTest = () => {
         const sendTest = () => {
-          const testObj = new Uint8Array([this.postMessageTransfers ? 255 : 0]);
+          const testObj = new Uint8Array([255]);
 
 
           try {
           try {
             messageHandler.send("test", testObj, [testObj.buffer]);
             messageHandler.send("test", testObj, [testObj.buffer]);
@@ -1528,6 +1540,11 @@ exports.PDFWorker = PDFWorker;
 }
 }
 
 
 class WorkerTransport {
 class WorkerTransport {
+  #docStats = null;
+  #pageCache = new Map();
+  #pagePromises = new Map();
+  #metadataPromise = null;
+
   constructor(messageHandler, loadingTask, networkStream, params) {
   constructor(messageHandler, loadingTask, networkStream, params) {
     this.messageHandler = messageHandler;
     this.messageHandler = messageHandler;
     this.loadingTask = loadingTask;
     this.loadingTask = loadingTask;
@@ -1556,8 +1573,6 @@ class WorkerTransport {
     this._networkStream = networkStream;
     this._networkStream = networkStream;
     this._fullReader = null;
     this._fullReader = null;
     this._lastProgress = null;
     this._lastProgress = null;
-    this.pageCache = [];
-    this.pagePromises = [];
     this.downloadInfoCapability = (0, _util.createPromiseCapability)();
     this.downloadInfoCapability = (0, _util.createPromiseCapability)();
     this.setupMessageHandler();
     this.setupMessageHandler();
   }
   }
@@ -1566,6 +1581,10 @@ class WorkerTransport {
     return (0, _util.shadow)(this, "annotationStorage", new _annotation_storage.AnnotationStorage());
     return (0, _util.shadow)(this, "annotationStorage", new _annotation_storage.AnnotationStorage());
   }
   }
 
 
+  get stats() {
+    return this.#docStats;
+  }
+
   getRenderingIntent(intent, annotationMode = _util.AnnotationMode.ENABLE, isOpList = false) {
   getRenderingIntent(intent, annotationMode = _util.AnnotationMode.ENABLE, isOpList = false) {
     let renderingIntent = _util.RenderingIntentFlag.DISPLAY;
     let renderingIntent = _util.RenderingIntentFlag.DISPLAY;
     let lastModified = "";
     let lastModified = "";
@@ -1631,14 +1650,12 @@ class WorkerTransport {
 
 
     const waitOn = [];
     const waitOn = [];
 
 
-    for (const page of this.pageCache) {
-      if (page) {
-        waitOn.push(page._destroy());
-      }
+    for (const page of this.#pageCache.values()) {
+      waitOn.push(page._destroy());
     }
     }
 
 
-    this.pageCache.length = 0;
-    this.pagePromises.length = 0;
+    this.#pageCache.clear();
+    this.#pagePromises.clear();
 
 
     if (this.hasOwnProperty("annotationStorage")) {
     if (this.hasOwnProperty("annotationStorage")) {
       this.annotationStorage.resetModified();
       this.annotationStorage.resetModified();
@@ -1649,6 +1666,7 @@ class WorkerTransport {
     Promise.all(waitOn).then(() => {
     Promise.all(waitOn).then(() => {
       this.commonObjs.clear();
       this.commonObjs.clear();
       this.fontLoader.clear();
       this.fontLoader.clear();
+      this.#metadataPromise = null;
       this._getFieldObjectsPromise = null;
       this._getFieldObjectsPromise = null;
       this._hasJSActionsPromise = null;
       this._hasJSActionsPromise = null;
 
 
@@ -1716,17 +1734,15 @@ class WorkerTransport {
       const fullReader = this._fullReader;
       const fullReader = this._fullReader;
       fullReader.headersReady.then(() => {
       fullReader.headersReady.then(() => {
         if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {
         if (!fullReader.isStreamingSupported || !fullReader.isRangeSupported) {
-          if (this._lastProgress && loadingTask.onProgress) {
-            loadingTask.onProgress(this._lastProgress);
+          if (this._lastProgress) {
+            loadingTask.onProgress?.(this._lastProgress);
           }
           }
 
 
           fullReader.onProgress = evt => {
           fullReader.onProgress = evt => {
-            if (loadingTask.onProgress) {
-              loadingTask.onProgress({
-                loaded: evt.loaded,
-                total: evt.total
-              });
-            }
+            loadingTask.onProgress?.({
+              loaded: evt.loaded,
+              total: evt.total
+            });
           };
           };
         }
         }
 
 
@@ -1837,13 +1853,10 @@ class WorkerTransport {
       return this._passwordCapability.promise;
       return this._passwordCapability.promise;
     });
     });
     messageHandler.on("DataLoaded", data => {
     messageHandler.on("DataLoaded", data => {
-      if (loadingTask.onProgress) {
-        loadingTask.onProgress({
-          loaded: data.length,
-          total: data.length
-        });
-      }
-
+      loadingTask.onProgress?.({
+        loaded: data.length,
+        total: data.length
+      });
       this.downloadInfoCapability.resolve(data);
       this.downloadInfoCapability.resolve(data);
     });
     });
     messageHandler.on("StartRenderPage", data => {
     messageHandler.on("StartRenderPage", data => {
@@ -1851,17 +1864,15 @@ class WorkerTransport {
         return;
         return;
       }
       }
 
 
-      const page = this.pageCache[data.pageIndex];
+      const page = this.#pageCache.get(data.pageIndex);
 
 
       page._startRenderPage(data.transparency, data.cacheKey);
       page._startRenderPage(data.transparency, data.cacheKey);
     });
     });
-    messageHandler.on("commonobj", data => {
+    messageHandler.on("commonobj", ([id, type, exportedData]) => {
       if (this.destroyed) {
       if (this.destroyed) {
         return;
         return;
       }
       }
 
 
-      const [id, type, exportedData] = data;
-
       if (this.commonObjs.has(id)) {
       if (this.commonObjs.has(id)) {
         return;
         return;
       }
       }
@@ -1917,16 +1928,15 @@ class WorkerTransport {
           throw new Error(`Got unknown common object type ${type}`);
           throw new Error(`Got unknown common object type ${type}`);
       }
       }
     });
     });
-    messageHandler.on("obj", data => {
+    messageHandler.on("obj", ([id, pageIndex, type, imageData]) => {
       if (this.destroyed) {
       if (this.destroyed) {
-        return undefined;
+        return;
       }
       }
 
 
-      const [id, pageIndex, type, imageData] = data;
-      const pageProxy = this.pageCache[pageIndex];
+      const pageProxy = this.#pageCache.get(pageIndex);
 
 
       if (pageProxy.objs.has(id)) {
       if (pageProxy.objs.has(id)) {
-        return undefined;
+        return;
       }
       }
 
 
       switch (type) {
       switch (type) {
@@ -1947,20 +1957,26 @@ class WorkerTransport {
         default:
         default:
           throw new Error(`Got unknown object type ${type}`);
           throw new Error(`Got unknown object type ${type}`);
       }
       }
-
-      return undefined;
     });
     });
     messageHandler.on("DocProgress", data => {
     messageHandler.on("DocProgress", data => {
       if (this.destroyed) {
       if (this.destroyed) {
         return;
         return;
       }
       }
 
 
-      if (loadingTask.onProgress) {
-        loadingTask.onProgress({
-          loaded: data.loaded,
-          total: data.total
-        });
+      loadingTask.onProgress?.({
+        loaded: data.loaded,
+        total: data.total
+      });
+    });
+    messageHandler.on("DocStats", data => {
+      if (this.destroyed) {
+        return;
       }
       }
+
+      this.#docStats = Object.freeze({
+        streamTypes: Object.freeze(data.streamTypes),
+        fontTypes: Object.freeze(data.fontTypes)
+      });
     });
     });
     messageHandler.on("UnsupportedFeature", this._onUnsupportedFeature.bind(this));
     messageHandler.on("UnsupportedFeature", this._onUnsupportedFeature.bind(this));
     messageHandler.on("FetchBuiltInCMap", data => {
     messageHandler.on("FetchBuiltInCMap", data => {
@@ -1994,9 +2010,7 @@ class WorkerTransport {
       return;
       return;
     }
     }
 
 
-    if (this.loadingTask.onUnsupportedFeature) {
-      this.loadingTask.onUnsupportedFeature(featureId);
-    }
+    this.loadingTask.onUnsupportedFeature?.(featureId);
   }
   }
 
 
   getData() {
   getData() {
@@ -2008,10 +2022,11 @@ class WorkerTransport {
       return Promise.reject(new Error("Invalid page request"));
       return Promise.reject(new Error("Invalid page request"));
     }
     }
 
 
-    const pageIndex = pageNumber - 1;
+    const pageIndex = pageNumber - 1,
+          cachedPromise = this.#pagePromises.get(pageIndex);
 
 
-    if (pageIndex in this.pagePromises) {
-      return this.pagePromises[pageIndex];
+    if (cachedPromise) {
+      return cachedPromise;
     }
     }
 
 
     const promise = this.messageHandler.sendWithPromise("GetPage", {
     const promise = this.messageHandler.sendWithPromise("GetPage", {
@@ -2022,10 +2037,10 @@ class WorkerTransport {
       }
       }
 
 
       const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.ownerDocument, this._params.pdfBug);
       const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.ownerDocument, this._params.pdfBug);
-      this.pageCache[pageIndex] = page;
+      this.#pageCache.set(pageIndex, page);
       return page;
       return page;
     });
     });
-    this.pagePromises[pageIndex] = promise;
+    this.#pagePromises.set(pageIndex, promise);
     return promise;
     return promise;
   }
   }
 
 
@@ -2138,7 +2153,7 @@ class WorkerTransport {
   }
   }
 
 
   getMetadata() {
   getMetadata() {
-    return this.messageHandler.sendWithPromise("GetMetadata", null).then(results => {
+    return this.#metadataPromise ||= this.messageHandler.sendWithPromise("GetMetadata", null).then(results => {
       return {
       return {
         info: results[0],
         info: results[0],
         metadata: results[1] ? new _metadata.Metadata(results[1]) : null,
         metadata: results[1] ? new _metadata.Metadata(results[1]) : null,
@@ -2152,10 +2167,6 @@ class WorkerTransport {
     return this.messageHandler.sendWithPromise("GetMarkInfo", null);
     return this.messageHandler.sendWithPromise("GetMarkInfo", null);
   }
   }
 
 
-  getStats() {
-    return this.messageHandler.sendWithPromise("GetStats", null);
-  }
-
   async startCleanup(keepLoadedFonts = false) {
   async startCleanup(keepLoadedFonts = false) {
     await this.messageHandler.sendWithPromise("Cleanup", null);
     await this.messageHandler.sendWithPromise("Cleanup", null);
 
 
@@ -2163,17 +2174,11 @@ class WorkerTransport {
       return;
       return;
     }
     }
 
 
-    for (let i = 0, ii = this.pageCache.length; i < ii; i++) {
-      const page = this.pageCache[i];
-
-      if (!page) {
-        continue;
-      }
-
+    for (const page of this.#pageCache.values()) {
       const cleanupSuccessful = page.cleanup();
       const cleanupSuccessful = page.cleanup();
 
 
       if (!cleanupSuccessful) {
       if (!cleanupSuccessful) {
-        throw new Error(`startCleanup: Page ${i + 1} is currently rendering.`);
+        throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`);
       }
       }
     }
     }
 
 
@@ -2183,6 +2188,7 @@ class WorkerTransport {
       this.fontLoader.clear();
       this.fontLoader.clear();
     }
     }
 
 
+    this.#metadataPromise = null;
     this._getFieldObjectsPromise = null;
     this._getFieldObjectsPromise = null;
     this._hasJSActionsPromise = null;
     this._hasJSActionsPromise = null;
   }
   }
@@ -2277,6 +2283,7 @@ class InternalRenderTask {
     params,
     params,
     objs,
     objs,
     commonObjs,
     commonObjs,
+    annotationCanvasMap,
     operatorList,
     operatorList,
     pageIndex,
     pageIndex,
     canvasFactory,
     canvasFactory,
@@ -2287,6 +2294,7 @@ class InternalRenderTask {
     this.params = params;
     this.params = params;
     this.objs = objs;
     this.objs = objs;
     this.commonObjs = commonObjs;
     this.commonObjs = commonObjs;
+    this.annotationCanvasMap = annotationCanvasMap;
     this.operatorListIdx = null;
     this.operatorListIdx = null;
     this.operatorList = operatorList;
     this.operatorList = operatorList;
     this._pageIndex = pageIndex;
     this._pageIndex = pageIndex;
@@ -2339,7 +2347,7 @@ class InternalRenderTask {
       imageLayer,
       imageLayer,
       background
       background
     } = this.params;
     } = this.params;
-    this.gfx = new _canvas.CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, imageLayer, optionalContentConfig);
+    this.gfx = new _canvas.CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, imageLayer, optionalContentConfig, this.annotationCanvasMap);
     this.gfx.beginDrawing({
     this.gfx.beginDrawing({
       transform,
       transform,
       viewport,
       viewport,
@@ -2437,7 +2445,7 @@ class InternalRenderTask {
 
 
 }
 }
 
 
-const version = '2.11.338';
+const version = '2.12.313';
 exports.version = version;
 exports.version = version;
-const build = 'dedff3c98';
+const build = 'a2ae56f39';
 exports.build = build;
 exports.build = build;

+ 1 - 1
lib/display/base_factory.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.BaseSVGFactory = exports.BaseStandardFontDataFactory = exports.BaseCMapReaderFactory = exports.BaseCanvasFactory = void 0;
+exports.BaseStandardFontDataFactory = exports.BaseSVGFactory = exports.BaseCanvasFactory = exports.BaseCMapReaderFactory = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 

+ 410 - 159
lib/display/canvas.js

@@ -35,7 +35,6 @@ var _display_utils = require("./display_utils.js");
 const MIN_FONT_SIZE = 16;
 const MIN_FONT_SIZE = 16;
 const MAX_FONT_SIZE = 100;
 const MAX_FONT_SIZE = 100;
 const MAX_GROUP_SIZE = 4096;
 const MAX_GROUP_SIZE = 4096;
-const MAX_CACHED_CANVAS_PATTERNS = 2;
 const EXECUTION_TIME = 15;
 const EXECUTION_TIME = 15;
 const EXECUTION_STEPS = 10;
 const EXECUTION_STEPS = 10;
 const COMPILE_TYPE3_GLYPHS = true;
 const COMPILE_TYPE3_GLYPHS = true;
@@ -43,6 +42,137 @@ const MAX_SIZE_TO_COMPILE = 1000;
 const FULL_CHUNK_HEIGHT = 16;
 const FULL_CHUNK_HEIGHT = 16;
 const LINEWIDTH_SCALE_FACTOR = 1.000001;
 const LINEWIDTH_SCALE_FACTOR = 1.000001;
 
 
+function mirrorContextOperations(ctx, destCtx) {
+  if (ctx._removeMirroring) {
+    throw new Error("Context is already forwarding operations.");
+  }
+
+  ctx.__originalSave = ctx.save;
+  ctx.__originalRestore = ctx.restore;
+  ctx.__originalRotate = ctx.rotate;
+  ctx.__originalScale = ctx.scale;
+  ctx.__originalTranslate = ctx.translate;
+  ctx.__originalTransform = ctx.transform;
+  ctx.__originalSetTransform = ctx.setTransform;
+  ctx.__originalResetTransform = ctx.resetTransform;
+  ctx.__originalClip = ctx.clip;
+  ctx.__originalMoveTo = ctx.moveTo;
+  ctx.__originalLineTo = ctx.lineTo;
+  ctx.__originalBezierCurveTo = ctx.bezierCurveTo;
+  ctx.__originalRect = ctx.rect;
+  ctx.__originalClosePath = ctx.closePath;
+  ctx.__originalBeginPath = ctx.beginPath;
+
+  ctx._removeMirroring = () => {
+    ctx.save = ctx.__originalSave;
+    ctx.restore = ctx.__originalRestore;
+    ctx.rotate = ctx.__originalRotate;
+    ctx.scale = ctx.__originalScale;
+    ctx.translate = ctx.__originalTranslate;
+    ctx.transform = ctx.__originalTransform;
+    ctx.setTransform = ctx.__originalSetTransform;
+    ctx.resetTransform = ctx.__originalResetTransform;
+    ctx.clip = ctx.__originalClip;
+    ctx.moveTo = ctx.__originalMoveTo;
+    ctx.lineTo = ctx.__originalLineTo;
+    ctx.bezierCurveTo = ctx.__originalBezierCurveTo;
+    ctx.rect = ctx.__originalRect;
+    ctx.closePath = ctx.__originalClosePath;
+    ctx.beginPath = ctx.__originalBeginPath;
+    delete ctx._removeMirroring;
+  };
+
+  ctx.save = function ctxSave() {
+    destCtx.save();
+
+    this.__originalSave();
+  };
+
+  ctx.restore = function ctxRestore() {
+    destCtx.restore();
+
+    this.__originalRestore();
+  };
+
+  ctx.translate = function ctxTranslate(x, y) {
+    destCtx.translate(x, y);
+
+    this.__originalTranslate(x, y);
+  };
+
+  ctx.scale = function ctxScale(x, y) {
+    destCtx.scale(x, y);
+
+    this.__originalScale(x, y);
+  };
+
+  ctx.transform = function ctxTransform(a, b, c, d, e, f) {
+    destCtx.transform(a, b, c, d, e, f);
+
+    this.__originalTransform(a, b, c, d, e, f);
+  };
+
+  ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
+    destCtx.setTransform(a, b, c, d, e, f);
+
+    this.__originalSetTransform(a, b, c, d, e, f);
+  };
+
+  ctx.resetTransform = function ctxResetTransform() {
+    destCtx.resetTransform();
+
+    this.__originalResetTransform();
+  };
+
+  ctx.rotate = function ctxRotate(angle) {
+    destCtx.rotate(angle);
+
+    this.__originalRotate(angle);
+  };
+
+  ctx.clip = function ctxRotate(rule) {
+    destCtx.clip(rule);
+
+    this.__originalClip(rule);
+  };
+
+  ctx.moveTo = function (x, y) {
+    destCtx.moveTo(x, y);
+
+    this.__originalMoveTo(x, y);
+  };
+
+  ctx.lineTo = function (x, y) {
+    destCtx.lineTo(x, y);
+
+    this.__originalLineTo(x, y);
+  };
+
+  ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
+    destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+
+    this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+  };
+
+  ctx.rect = function (x, y, width, height) {
+    destCtx.rect(x, y, width, height);
+
+    this.__originalRect(x, y, width, height);
+  };
+
+  ctx.closePath = function () {
+    destCtx.closePath();
+
+    this.__originalClosePath();
+  };
+
+  ctx.beginPath = function () {
+    destCtx.beginPath();
+
+    this.__originalBeginPath();
+  };
+}
+
 function addContextCurrentTransform(ctx) {
 function addContextCurrentTransform(ctx) {
   if (ctx.mozCurrentTransform) {
   if (ctx.mozCurrentTransform) {
     return;
     return;
@@ -189,46 +319,6 @@ class CachedCanvases {
 
 
 }
 }
 
 
-class LRUCache {
-  constructor(maxSize = 0) {
-    this._cache = new Map();
-    this._maxSize = maxSize;
-  }
-
-  has(key) {
-    return this._cache.has(key);
-  }
-
-  get(key) {
-    if (this._cache.has(key)) {
-      const value = this._cache.get(key);
-
-      this._cache.delete(key);
-
-      this._cache.set(key, value);
-    }
-
-    return this._cache.get(key);
-  }
-
-  set(key, value) {
-    if (this._maxSize <= 0) {
-      return;
-    }
-
-    if (this._cache.size + 1 > this._maxSize) {
-      this._cache.delete(this._cache.keys().next().value);
-    }
-
-    this._cache.set(key, value);
-  }
-
-  clear() {
-    this._cache.clear();
-  }
-
-}
-
 function compileType3Glyph(imgData) {
 function compileType3Glyph(imgData) {
   const POINT_TO_PROCESS_LIMIT = 1000;
   const POINT_TO_PROCESS_LIMIT = 1000;
   const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
   const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
@@ -403,7 +493,7 @@ function compileType3Glyph(imgData) {
 }
 }
 
 
 class CanvasExtraState {
 class CanvasExtraState {
-  constructor() {
+  constructor(width, height) {
     this.alphaIsShape = false;
     this.alphaIsShape = false;
     this.fontSize = 0;
     this.fontSize = 0;
     this.fontSizeScale = 1;
     this.fontSizeScale = 1;
@@ -427,12 +517,14 @@ class CanvasExtraState {
     this.strokeAlpha = 1;
     this.strokeAlpha = 1;
     this.lineWidth = 1;
     this.lineWidth = 1;
     this.activeSMask = null;
     this.activeSMask = null;
-    this.resumeSMaskCtx = null;
     this.transferMaps = null;
     this.transferMaps = null;
+    this.startNewPathAndClipBox([0, 0, width, height]);
   }
   }
 
 
   clone() {
   clone() {
-    return Object.create(this);
+    const clone = Object.create(this);
+    clone.clipBox = this.clipBox.slice();
+    return clone;
   }
   }
 
 
   setCurrentPoint(x, y) {
   setCurrentPoint(x, y) {
@@ -440,6 +532,60 @@ class CanvasExtraState {
     this.y = y;
     this.y = y;
   }
   }
 
 
+  updatePathMinMax(transform, x, y) {
+    [x, y] = _util.Util.applyTransform([x, y], transform);
+    this.minX = Math.min(this.minX, x);
+    this.minY = Math.min(this.minY, y);
+    this.maxX = Math.max(this.maxX, x);
+    this.maxY = Math.max(this.maxY, y);
+  }
+
+  updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3) {
+    const box = _util.Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3);
+
+    this.updatePathMinMax(transform, box[0], box[1]);
+    this.updatePathMinMax(transform, box[2], box[3]);
+  }
+
+  getPathBoundingBox(pathType = _pattern_helper.PathType.FILL, transform = null) {
+    const box = [this.minX, this.minY, this.maxX, this.maxY];
+
+    if (pathType === _pattern_helper.PathType.STROKE) {
+      if (!transform) {
+        (0, _util.unreachable)("Stroke bounding box must include transform.");
+      }
+
+      const scale = _util.Util.singularValueDecompose2dScale(transform);
+
+      const xStrokePad = scale[0] * this.lineWidth / 2;
+      const yStrokePad = scale[1] * this.lineWidth / 2;
+      box[0] -= xStrokePad;
+      box[1] -= yStrokePad;
+      box[2] += xStrokePad;
+      box[3] += yStrokePad;
+    }
+
+    return box;
+  }
+
+  updateClipFromPath() {
+    const intersect = _util.Util.intersect(this.clipBox, this.getPathBoundingBox());
+
+    this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);
+  }
+
+  startNewPathAndClipBox(box) {
+    this.clipBox = box;
+    this.minX = Infinity;
+    this.minY = Infinity;
+    this.maxX = 0;
+    this.maxY = 0;
+  }
+
+  getClippedPathBoundingBox(pathType = _pattern_helper.PathType.FILL, transform = null) {
+    return _util.Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));
+  }
+
 }
 }
 
 
 function putBinaryImageData(ctx, imgData, transferMaps = null) {
 function putBinaryImageData(ctx, imgData, transferMaps = null) {
@@ -736,7 +882,7 @@ function composeSMaskLuminosity(maskData, layerData, transferMap) {
   }
   }
 }
 }
 
 
-function genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap) {
+function genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {
   const hasBackdrop = !!backdrop;
   const hasBackdrop = !!backdrop;
   const r0 = hasBackdrop ? backdrop[0] : 0;
   const r0 = hasBackdrop ? backdrop[0] : 0;
   const g0 = hasBackdrop ? backdrop[1] : 0;
   const g0 = hasBackdrop ? backdrop[1] : 0;
@@ -754,24 +900,35 @@ function genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop
 
 
   for (let row = 0; row < height; row += chunkSize) {
   for (let row = 0; row < height; row += chunkSize) {
     const chunkHeight = Math.min(chunkSize, height - row);
     const chunkHeight = Math.min(chunkSize, height - row);
-    const maskData = maskCtx.getImageData(0, row, width, chunkHeight);
-    const layerData = layerCtx.getImageData(0, row, width, chunkHeight);
+    const maskData = maskCtx.getImageData(layerOffsetX - maskOffsetX, row + (layerOffsetY - maskOffsetY), width, chunkHeight);
+    const layerData = layerCtx.getImageData(layerOffsetX, row + layerOffsetY, width, chunkHeight);
 
 
     if (hasBackdrop) {
     if (hasBackdrop) {
       composeSMaskBackdrop(maskData.data, r0, g0, b0);
       composeSMaskBackdrop(maskData.data, r0, g0, b0);
     }
     }
 
 
     composeFn(maskData.data, layerData.data, transferMap);
     composeFn(maskData.data, layerData.data, transferMap);
-    maskCtx.putImageData(layerData, 0, row);
+    layerCtx.putImageData(layerData, layerOffsetX, row + layerOffsetY);
   }
   }
 }
 }
 
 
-function composeSMask(ctx, smask, layerCtx) {
-  const mask = smask.canvas;
-  const maskCtx = smask.context;
-  ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY, smask.offsetX, smask.offsetY);
-  genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height, smask.subtype, smask.backdrop, smask.transferMap);
-  ctx.drawImage(mask, 0, 0);
+function composeSMask(ctx, smask, layerCtx, layerBox) {
+  const layerOffsetX = layerBox[0];
+  const layerOffsetY = layerBox[1];
+  const layerWidth = layerBox[2] - layerOffsetX;
+  const layerHeight = layerBox[3] - layerOffsetY;
+
+  if (layerWidth === 0 || layerHeight === 0) {
+    return;
+  }
+
+  genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);
+  ctx.save();
+  ctx.globalAlpha = 1;
+  ctx.globalCompositeOperation = "source-over";
+  ctx.setTransform(1, 0, 0, 1, 0, 0);
+  ctx.drawImage(layerCtx.canvas, 0, 0);
+  ctx.restore();
 }
 }
 
 
 function getImageSmoothingEnabled(transform, interpolate) {
 function getImageSmoothingEnabled(transform, interpolate) {
@@ -796,9 +953,9 @@ const NORMAL_CLIP = {};
 const EO_CLIP = {};
 const EO_CLIP = {};
 
 
 class CanvasGraphics {
 class CanvasGraphics {
-  constructor(canvasCtx, commonObjs, objs, canvasFactory, imageLayer, optionalContentConfig) {
+  constructor(canvasCtx, commonObjs, objs, canvasFactory, imageLayer, optionalContentConfig, annotationCanvasMap) {
     this.ctx = canvasCtx;
     this.ctx = canvasCtx;
-    this.current = new CanvasExtraState();
+    this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
     this.stateStack = [];
     this.stateStack = [];
     this.pendingClip = null;
     this.pendingClip = null;
     this.pendingEOFill = false;
     this.pendingEOFill = false;
@@ -816,12 +973,16 @@ class CanvasGraphics {
     this.smaskStack = [];
     this.smaskStack = [];
     this.smaskCounter = 0;
     this.smaskCounter = 0;
     this.tempSMask = null;
     this.tempSMask = null;
+    this.suspendedCtx = null;
     this.contentVisible = true;
     this.contentVisible = true;
     this.markedContentStack = [];
     this.markedContentStack = [];
     this.optionalContentConfig = optionalContentConfig;
     this.optionalContentConfig = optionalContentConfig;
     this.cachedCanvases = new CachedCanvases(this.canvasFactory);
     this.cachedCanvases = new CachedCanvases(this.canvasFactory);
-    this.cachedCanvasPatterns = new LRUCache(MAX_CACHED_CANVAS_PATTERNS);
     this.cachedPatterns = new Map();
     this.cachedPatterns = new Map();
+    this.annotationCanvasMap = annotationCanvasMap;
+    this.viewportScale = 1;
+    this.outputScaleX = 1;
+    this.outputScaleY = 1;
 
 
     if (canvasCtx) {
     if (canvasCtx) {
       addContextCurrentTransform(canvasCtx);
       addContextCurrentTransform(canvasCtx);
@@ -857,9 +1018,12 @@ class CanvasGraphics {
 
 
     if (transform) {
     if (transform) {
       this.ctx.transform.apply(this.ctx, transform);
       this.ctx.transform.apply(this.ctx, transform);
+      this.outputScaleX = transform[0];
+      this.outputScaleY = transform[0];
     }
     }
 
 
     this.ctx.transform.apply(this.ctx, viewport.transform);
     this.ctx.transform.apply(this.ctx, viewport.transform);
+    this.viewportScale = viewport.scale;
     this.baseTransform = this.ctx.mozCurrentTransform.slice();
     this.baseTransform = this.ctx.mozCurrentTransform.slice();
     this._combinedScaleFactor = Math.hypot(this.baseTransform[0], this.baseTransform[2]);
     this._combinedScaleFactor = Math.hypot(this.baseTransform[0], this.baseTransform[2]);
 
 
@@ -940,7 +1104,6 @@ class CanvasGraphics {
     }
     }
 
 
     this.cachedCanvases.clear();
     this.cachedCanvases.clear();
-    this.cachedCanvasPatterns.clear();
     this.cachedPatterns.clear();
     this.cachedPatterns.clear();
 
 
     if (this.imageLayer) {
     if (this.imageLayer) {
@@ -1027,7 +1190,7 @@ class CanvasGraphics {
 
 
     const inverse = _util.Util.transform(fillCtx.mozCurrentTransformInverse, [1, 0, 0, 1, -offsetX, -offsetY]);
     const inverse = _util.Util.transform(fillCtx.mozCurrentTransformInverse, [1, 0, 0, 1, -offsetX, -offsetY]);
 
 
-    fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, false) : fillColor;
+    fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, _pattern_helper.PathType.FILL) : fillColor;
     fillCtx.fillRect(0, 0, width, height);
     fillCtx.fillRect(0, 0, width, height);
     return {
     return {
       canvas: fillCanvas.canvas,
       canvas: fillCanvas.canvas,
@@ -1119,21 +1282,9 @@ class CanvasGraphics {
           break;
           break;
 
 
         case "SMask":
         case "SMask":
-          if (this.current.activeSMask) {
-            if (this.stateStack.length > 0 && this.stateStack[this.stateStack.length - 1].activeSMask === this.current.activeSMask) {
-              this.suspendSMaskGroup();
-            } else {
-              this.endSMaskGroup();
-            }
-          }
-
           this.current.activeSMask = value ? this.tempSMask : null;
           this.current.activeSMask = value ? this.tempSMask : null;
-
-          if (this.current.activeSMask) {
-            this.beginSMaskGroup();
-          }
-
           this.tempSMask = null;
           this.tempSMask = null;
+          this.checkSMaskState();
           break;
           break;
 
 
         case "TR":
         case "TR":
@@ -1142,65 +1293,68 @@ class CanvasGraphics {
     }
     }
   }
   }
 
 
-  beginSMaskGroup() {
-    const activeSMask = this.current.activeSMask;
-    const drawnWidth = activeSMask.canvas.width;
-    const drawnHeight = activeSMask.canvas.height;
+  checkSMaskState() {
+    const inSMaskMode = !!this.suspendedCtx;
+
+    if (this.current.activeSMask && !inSMaskMode) {
+      this.beginSMaskMode();
+    } else if (!this.current.activeSMask && inSMaskMode) {
+      this.endSMaskMode();
+    }
+  }
+
+  beginSMaskMode() {
+    if (this.suspendedCtx) {
+      throw new Error("beginSMaskMode called while already in smask mode");
+    }
+
+    const drawnWidth = this.ctx.canvas.width;
+    const drawnHeight = this.ctx.canvas.height;
     const cacheId = "smaskGroupAt" + this.groupLevel;
     const cacheId = "smaskGroupAt" + this.groupLevel;
     const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight, true);
     const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight, true);
-    const currentCtx = this.ctx;
-    const currentTransform = currentCtx.mozCurrentTransform;
-    this.ctx.save();
-    const groupCtx = scratchCanvas.context;
-    groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY);
-    groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY);
-    groupCtx.transform.apply(groupCtx, currentTransform);
-    activeSMask.startTransformInverse = groupCtx.mozCurrentTransformInverse;
-    copyCtxState(currentCtx, groupCtx);
-    this.ctx = groupCtx;
+    this.suspendedCtx = this.ctx;
+    this.ctx = scratchCanvas.context;
+    const ctx = this.ctx;
+    ctx.setTransform.apply(ctx, this.suspendedCtx.mozCurrentTransform);
+    copyCtxState(this.suspendedCtx, ctx);
+    mirrorContextOperations(ctx, this.suspendedCtx);
     this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
     this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
-    this.groupStack.push(currentCtx);
-    this.groupLevel++;
   }
   }
 
 
-  suspendSMaskGroup() {
-    const groupCtx = this.ctx;
-    this.groupLevel--;
-    this.ctx = this.groupStack.pop();
-    composeSMask(this.ctx, this.current.activeSMask, groupCtx);
-    this.ctx.restore();
-    this.ctx.save();
-    copyCtxState(groupCtx, this.ctx);
-    this.current.resumeSMaskCtx = groupCtx;
-
-    const deltaTransform = _util.Util.transform(this.current.activeSMask.startTransformInverse, groupCtx.mozCurrentTransform);
+  endSMaskMode() {
+    if (!this.suspendedCtx) {
+      throw new Error("endSMaskMode called while not in smask mode");
+    }
 
 
-    this.ctx.transform.apply(this.ctx, deltaTransform);
-    groupCtx.save();
-    groupCtx.setTransform(1, 0, 0, 1, 0, 0);
-    groupCtx.clearRect(0, 0, groupCtx.canvas.width, groupCtx.canvas.height);
-    groupCtx.restore();
-  }
+    this.ctx._removeMirroring();
 
 
-  resumeSMaskGroup() {
-    const groupCtx = this.current.resumeSMaskCtx;
-    const currentCtx = this.ctx;
-    this.ctx = groupCtx;
-    this.groupStack.push(currentCtx);
-    this.groupLevel++;
+    copyCtxState(this.ctx, this.suspendedCtx);
+    this.ctx = this.suspendedCtx;
+    this.current.activeSMask = null;
+    this.suspendedCtx = null;
   }
   }
 
 
-  endSMaskGroup() {
-    const groupCtx = this.ctx;
-    this.groupLevel--;
-    this.ctx = this.groupStack.pop();
-    composeSMask(this.ctx, this.current.activeSMask, groupCtx);
-    this.ctx.restore();
-    copyCtxState(groupCtx, this.ctx);
+  compose(dirtyBox) {
+    if (!this.current.activeSMask) {
+      return;
+    }
 
 
-    const deltaTransform = _util.Util.transform(this.current.activeSMask.startTransformInverse, groupCtx.mozCurrentTransform);
+    if (!dirtyBox) {
+      dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
+    } else {
+      dirtyBox[0] = Math.floor(dirtyBox[0]);
+      dirtyBox[1] = Math.floor(dirtyBox[1]);
+      dirtyBox[2] = Math.ceil(dirtyBox[2]);
+      dirtyBox[3] = Math.ceil(dirtyBox[3]);
+    }
 
 
-    this.ctx.transform.apply(this.ctx, deltaTransform);
+    const smask = this.current.activeSMask;
+    const suspendedCtx = this.suspendedCtx;
+    composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);
+    this.ctx.save();
+    this.ctx.setTransform(1, 0, 0, 1, 0, 0);
+    this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+    this.ctx.restore();
   }
   }
 
 
   save() {
   save() {
@@ -1208,25 +1362,19 @@ class CanvasGraphics {
     const old = this.current;
     const old = this.current;
     this.stateStack.push(old);
     this.stateStack.push(old);
     this.current = old.clone();
     this.current = old.clone();
-    this.current.resumeSMaskCtx = null;
   }
   }
 
 
   restore() {
   restore() {
-    if (this.current.resumeSMaskCtx) {
-      this.resumeSMaskGroup();
-    }
-
-    if (this.current.activeSMask !== null && (this.stateStack.length === 0 || this.stateStack[this.stateStack.length - 1].activeSMask !== this.current.activeSMask)) {
-      this.endSMaskGroup();
+    if (this.stateStack.length === 0 && this.current.activeSMask) {
+      this.endSMaskMode();
     }
     }
 
 
     if (this.stateStack.length !== 0) {
     if (this.stateStack.length !== 0) {
       this.current = this.stateStack.pop();
       this.current = this.stateStack.pop();
       this.ctx.restore();
       this.ctx.restore();
+      this.checkSMaskState();
       this.pendingClip = null;
       this.pendingClip = null;
       this._cachedGetSinglePixelWidth = null;
       this._cachedGetSinglePixelWidth = null;
-    } else {
-      this.current.activeSMask = null;
     }
     }
   }
   }
 
 
@@ -1240,6 +1388,7 @@ class CanvasGraphics {
     const current = this.current;
     const current = this.current;
     let x = current.x,
     let x = current.x,
         y = current.y;
         y = current.y;
+    let startX, startY;
 
 
     for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
     for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
       switch (ops[i] | 0) {
       switch (ops[i] | 0) {
@@ -1260,6 +1409,8 @@ class CanvasGraphics {
             ctx.lineTo(x, yh);
             ctx.lineTo(x, yh);
           }
           }
 
 
+          current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
+          current.updatePathMinMax(ctx.mozCurrentTransform, xw, yh);
           ctx.closePath();
           ctx.closePath();
           break;
           break;
 
 
@@ -1267,32 +1418,43 @@ class CanvasGraphics {
           x = args[j++];
           x = args[j++];
           y = args[j++];
           y = args[j++];
           ctx.moveTo(x, y);
           ctx.moveTo(x, y);
+          current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
           break;
           break;
 
 
         case _util.OPS.lineTo:
         case _util.OPS.lineTo:
           x = args[j++];
           x = args[j++];
           y = args[j++];
           y = args[j++];
           ctx.lineTo(x, y);
           ctx.lineTo(x, y);
+          current.updatePathMinMax(ctx.mozCurrentTransform, x, y);
           break;
           break;
 
 
         case _util.OPS.curveTo:
         case _util.OPS.curveTo:
+          startX = x;
+          startY = y;
           x = args[j + 4];
           x = args[j + 4];
           y = args[j + 5];
           y = args[j + 5];
           ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
           ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
+          current.updateCurvePathMinMax(ctx.mozCurrentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
           j += 6;
           j += 6;
           break;
           break;
 
 
         case _util.OPS.curveTo2:
         case _util.OPS.curveTo2:
+          startX = x;
+          startY = y;
           ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
           ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
+          current.updateCurvePathMinMax(ctx.mozCurrentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
           x = args[j + 2];
           x = args[j + 2];
           y = args[j + 3];
           y = args[j + 3];
           j += 4;
           j += 4;
           break;
           break;
 
 
         case _util.OPS.curveTo3:
         case _util.OPS.curveTo3:
+          startX = x;
+          startY = y;
           x = args[j + 2];
           x = args[j + 2];
           y = args[j + 3];
           y = args[j + 3];
           ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
           ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
+          current.updateCurvePathMinMax(ctx.mozCurrentTransform, startX, startY, args[j], args[j + 1], x, y, x, y);
           j += 4;
           j += 4;
           break;
           break;
 
 
@@ -1319,7 +1481,7 @@ class CanvasGraphics {
       if (typeof strokeColor === "object" && strokeColor?.getPattern) {
       if (typeof strokeColor === "object" && strokeColor?.getPattern) {
         const lineWidth = this.getSinglePixelWidth();
         const lineWidth = this.getSinglePixelWidth();
         ctx.save();
         ctx.save();
-        ctx.strokeStyle = strokeColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
+        ctx.strokeStyle = strokeColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse, _pattern_helper.PathType.STROKE);
         ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
         ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
         ctx.stroke();
         ctx.stroke();
         ctx.restore();
         ctx.restore();
@@ -1340,7 +1502,7 @@ class CanvasGraphics {
     }
     }
 
 
     if (consumePath) {
     if (consumePath) {
-      this.consumePath();
+      this.consumePath(this.current.getClippedPathBoundingBox());
     }
     }
 
 
     ctx.globalAlpha = this.current.fillAlpha;
     ctx.globalAlpha = this.current.fillAlpha;
@@ -1360,11 +1522,13 @@ class CanvasGraphics {
 
 
     if (isPatternFill) {
     if (isPatternFill) {
       ctx.save();
       ctx.save();
-      ctx.fillStyle = fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
+      ctx.fillStyle = fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse, _pattern_helper.PathType.FILL);
       needRestore = true;
       needRestore = true;
     }
     }
 
 
-    if (this.contentVisible) {
+    const intersect = this.current.getClippedPathBoundingBox();
+
+    if (this.contentVisible && intersect !== null) {
       if (this.pendingEOFill) {
       if (this.pendingEOFill) {
         ctx.fill("evenodd");
         ctx.fill("evenodd");
         this.pendingEOFill = false;
         this.pendingEOFill = false;
@@ -1378,7 +1542,7 @@ class CanvasGraphics {
     }
     }
 
 
     if (consumePath) {
     if (consumePath) {
-      this.consumePath();
+      this.consumePath(intersect);
     }
     }
   }
   }
 
 
@@ -1665,16 +1829,6 @@ class CanvasGraphics {
     const widthAdvanceScale = fontSize * current.fontMatrix[0];
     const widthAdvanceScale = fontSize * current.fontMatrix[0];
     const simpleFillText = current.textRenderingMode === _util.TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
     const simpleFillText = current.textRenderingMode === _util.TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
     ctx.save();
     ctx.save();
-    let patternTransform;
-
-    if (current.patternFill) {
-      ctx.save();
-      const pattern = current.fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
-      patternTransform = ctx.mozCurrentTransform;
-      ctx.restore();
-      ctx.fillStyle = pattern;
-    }
-
     ctx.transform.apply(ctx, current.textMatrix);
     ctx.transform.apply(ctx, current.textMatrix);
     ctx.translate(current.x, current.y + current.textRise);
     ctx.translate(current.x, current.y + current.textRise);
 
 
@@ -1684,6 +1838,16 @@ class CanvasGraphics {
       ctx.scale(textHScale, 1);
       ctx.scale(textHScale, 1);
     }
     }
 
 
+    let patternTransform;
+
+    if (current.patternFill) {
+      ctx.save();
+      const pattern = current.fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse, _pattern_helper.PathType.FILL);
+      patternTransform = ctx.mozCurrentTransform;
+      ctx.restore();
+      ctx.fillStyle = pattern;
+    }
+
     let lineWidth = current.lineWidth;
     let lineWidth = current.lineWidth;
     let resetLineWidthToOne = false;
     let resetLineWidthToOne = false;
     const scale = current.textMatrixScale;
     const scale = current.textMatrixScale;
@@ -1786,6 +1950,7 @@ class CanvasGraphics {
     }
     }
 
 
     ctx.restore();
     ctx.restore();
+    this.compose();
     return undefined;
     return undefined;
   }
   }
 
 
@@ -1909,7 +2074,7 @@ class CanvasGraphics {
     if (this.cachedPatterns.has(objId)) {
     if (this.cachedPatterns.has(objId)) {
       pattern = this.cachedPatterns.get(objId);
       pattern = this.cachedPatterns.get(objId);
     } else {
     } else {
-      pattern = (0, _pattern_helper.getShadingPattern)(this.objs.get(objId), this.cachedCanvasPatterns);
+      pattern = (0, _pattern_helper.getShadingPattern)(this.objs.get(objId));
       this.cachedPatterns.set(objId, pattern);
       this.cachedPatterns.set(objId, pattern);
     }
     }
 
 
@@ -1930,7 +2095,7 @@ class CanvasGraphics {
 
 
     const pattern = this._getPattern(objId);
     const pattern = this._getPattern(objId);
 
 
-    ctx.fillStyle = pattern.getPattern(ctx, this, ctx.mozCurrentTransformInverse, true);
+    ctx.fillStyle = pattern.getPattern(ctx, this, ctx.mozCurrentTransformInverse, _pattern_helper.PathType.SHADING);
     const inv = ctx.mozCurrentTransformInverse;
     const inv = ctx.mozCurrentTransformInverse;
 
 
     if (inv) {
     if (inv) {
@@ -1955,6 +2120,7 @@ class CanvasGraphics {
       this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
       this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
     }
     }
 
 
+    this.compose(this.current.getClippedPathBoundingBox());
     this.restore();
     this.restore();
   }
   }
 
 
@@ -1984,6 +2150,8 @@ class CanvasGraphics {
       const width = bbox[2] - bbox[0];
       const width = bbox[2] - bbox[0];
       const height = bbox[3] - bbox[1];
       const height = bbox[3] - bbox[1];
       this.ctx.rect(bbox[0], bbox[1], width, height);
       this.ctx.rect(bbox[0], bbox[1], width, height);
+      this.current.updatePathMinMax(this.ctx.mozCurrentTransform, bbox[0], bbox[1]);
+      this.current.updatePathMinMax(this.ctx.mozCurrentTransform, bbox[2], bbox[3]);
       this.clip();
       this.clip();
       this.endPath();
       this.endPath();
     }
     }
@@ -2004,6 +2172,13 @@ class CanvasGraphics {
     }
     }
 
 
     this.save();
     this.save();
+    const suspendedCtx = this.suspendedCtx;
+
+    if (this.current.activeSMask) {
+      this.suspendedCtx = null;
+      this.current.activeSMask = null;
+    }
+
     const currentCtx = this.ctx;
     const currentCtx = this.ctx;
 
 
     if (!group.isolated) {
     if (!group.isolated) {
@@ -2045,6 +2220,7 @@ class CanvasGraphics {
       drawnHeight = MAX_GROUP_SIZE;
       drawnHeight = MAX_GROUP_SIZE;
     }
     }
 
 
+    this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);
     let cacheId = "groupAt" + this.groupLevel;
     let cacheId = "groupAt" + this.groupLevel;
 
 
     if (group.smask) {
     if (group.smask) {
@@ -2074,14 +2250,17 @@ class CanvasGraphics {
       currentCtx.setTransform(1, 0, 0, 1, 0, 0);
       currentCtx.setTransform(1, 0, 0, 1, 0, 0);
       currentCtx.translate(offsetX, offsetY);
       currentCtx.translate(offsetX, offsetY);
       currentCtx.scale(scaleX, scaleY);
       currentCtx.scale(scaleX, scaleY);
+      currentCtx.save();
     }
     }
 
 
     copyCtxState(currentCtx, groupCtx);
     copyCtxState(currentCtx, groupCtx);
     this.ctx = groupCtx;
     this.ctx = groupCtx;
     this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
     this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
-    this.groupStack.push(currentCtx);
+    this.groupStack.push({
+      ctx: currentCtx,
+      suspendedCtx
+    });
     this.groupLevel++;
     this.groupLevel++;
-    this.current.activeSMask = null;
   }
   }
 
 
   endGroup(group) {
   endGroup(group) {
@@ -2091,16 +2270,33 @@ class CanvasGraphics {
 
 
     this.groupLevel--;
     this.groupLevel--;
     const groupCtx = this.ctx;
     const groupCtx = this.ctx;
-    this.ctx = this.groupStack.pop();
+    const {
+      ctx,
+      suspendedCtx
+    } = this.groupStack.pop();
+    this.ctx = ctx;
     this.ctx.imageSmoothingEnabled = false;
     this.ctx.imageSmoothingEnabled = false;
 
 
+    if (suspendedCtx) {
+      this.suspendedCtx = suspendedCtx;
+    }
+
     if (group.smask) {
     if (group.smask) {
       this.tempSMask = this.smaskStack.pop();
       this.tempSMask = this.smaskStack.pop();
+      this.restore();
     } else {
     } else {
+      this.ctx.restore();
+      const currentMtx = this.ctx.mozCurrentTransform;
+      this.restore();
+      this.ctx.save();
+      this.ctx.setTransform.apply(this.ctx, currentMtx);
+
+      const dirtyBox = _util.Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx);
+
       this.ctx.drawImage(groupCtx.canvas, 0, 0);
       this.ctx.drawImage(groupCtx.canvas, 0, 0);
+      this.ctx.restore();
+      this.compose(dirtyBox);
     }
     }
-
-    this.restore();
   }
   }
 
 
   beginAnnotations() {
   beginAnnotations() {
@@ -2115,24 +2311,62 @@ class CanvasGraphics {
     this.restore();
     this.restore();
   }
   }
 
 
-  beginAnnotation(id, rect, transform, matrix) {
+  beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) {
     this.save();
     this.save();
-    resetCtxToDefault(this.ctx);
-    this.current = new CanvasExtraState();
 
 
     if (Array.isArray(rect) && rect.length === 4) {
     if (Array.isArray(rect) && rect.length === 4) {
       const width = rect[2] - rect[0];
       const width = rect[2] - rect[0];
       const height = rect[3] - rect[1];
       const height = rect[3] - rect[1];
-      this.ctx.rect(rect[0], rect[1], width, height);
-      this.clip();
-      this.endPath();
+
+      if (hasOwnCanvas && this.annotationCanvasMap) {
+        transform = transform.slice();
+        transform[4] -= rect[0];
+        transform[5] -= rect[1];
+        rect = rect.slice();
+        rect[0] = rect[1] = 0;
+        rect[2] = width;
+        rect[3] = height;
+
+        const [scaleX, scaleY] = _util.Util.singularValueDecompose2dScale(this.ctx.mozCurrentTransform);
+
+        const {
+          viewportScale
+        } = this;
+        const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale);
+        const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale);
+        this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight);
+        const {
+          canvas,
+          context
+        } = this.annotationCanvas;
+        canvas.style.width = `calc(${width}px * var(--viewport-scale-factor))`;
+        canvas.style.height = `calc(${height}px * var(--viewport-scale-factor))`;
+        this.annotationCanvasMap.set(id, canvas);
+        this.annotationCanvas.savedCtx = this.ctx;
+        this.ctx = context;
+        this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY);
+        addContextCurrentTransform(this.ctx);
+        resetCtxToDefault(this.ctx);
+      } else {
+        resetCtxToDefault(this.ctx);
+        this.ctx.rect(rect[0], rect[1], width, height);
+        this.clip();
+        this.endPath();
+      }
     }
     }
 
 
+    this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
     this.transform.apply(this, transform);
     this.transform.apply(this, transform);
     this.transform.apply(this, matrix);
     this.transform.apply(this, matrix);
   }
   }
 
 
   endAnnotation() {
   endAnnotation() {
+    if (this.annotationCanvas) {
+      this.ctx = this.annotationCanvas.savedCtx;
+      delete this.annotationCanvas.savedCtx;
+      delete this.annotationCanvas;
+    }
+
     this.restore();
     this.restore();
   }
   }
 
 
@@ -2170,6 +2404,7 @@ class CanvasGraphics {
     ctx.setTransform(1, 0, 0, 1, 0, 0);
     ctx.setTransform(1, 0, 0, 1, 0, 0);
     ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
     ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
     ctx.restore();
     ctx.restore();
+    this.compose();
   }
   }
 
 
   paintImageMaskXObjectRepeat(imgData, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
   paintImageMaskXObjectRepeat(imgData, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
@@ -2195,6 +2430,7 @@ class CanvasGraphics {
     }
     }
 
 
     ctx.restore();
     ctx.restore();
+    this.compose();
   }
   }
 
 
   paintImageMaskXObjectGroup(images) {
   paintImageMaskXObjectGroup(images) {
@@ -2215,7 +2451,7 @@ class CanvasGraphics {
       maskCtx.save();
       maskCtx.save();
       putBinaryImageMask(maskCtx, image);
       putBinaryImageMask(maskCtx, image);
       maskCtx.globalCompositeOperation = "source-in";
       maskCtx.globalCompositeOperation = "source-in";
-      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, ctx.mozCurrentTransformInverse, false) : fillColor;
+      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, ctx.mozCurrentTransformInverse, _pattern_helper.PathType.FILL) : fillColor;
       maskCtx.fillRect(0, 0, width, height);
       maskCtx.fillRect(0, 0, width, height);
       maskCtx.restore();
       maskCtx.restore();
       ctx.save();
       ctx.save();
@@ -2224,6 +2460,8 @@ class CanvasGraphics {
       ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
       ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
       ctx.restore();
       ctx.restore();
     }
     }
+
+    this.compose();
   }
   }
 
 
   paintImageXObject(objId) {
   paintImageXObject(objId) {
@@ -2307,6 +2545,7 @@ class CanvasGraphics {
       });
       });
     }
     }
 
 
+    this.compose();
     this.restore();
     this.restore();
   }
   }
 
 
@@ -2342,6 +2581,8 @@ class CanvasGraphics {
 
 
       ctx.restore();
       ctx.restore();
     }
     }
+
+    this.compose();
   }
   }
 
 
   paintSolidColorImageMask() {
   paintSolidColorImageMask() {
@@ -2350,6 +2591,7 @@ class CanvasGraphics {
     }
     }
 
 
     this.ctx.fillRect(0, 0, 1, 1);
     this.ctx.fillRect(0, 0, 1, 1);
+    this.compose();
   }
   }
 
 
   markPoint(tag) {}
   markPoint(tag) {}
@@ -2385,7 +2627,15 @@ class CanvasGraphics {
 
 
   endCompat() {}
   endCompat() {}
 
 
-  consumePath() {
+  consumePath(clipBox) {
+    if (this.pendingClip) {
+      this.current.updateClipFromPath();
+    }
+
+    if (!this.pendingClip) {
+      this.compose(clipBox);
+    }
+
     const ctx = this.ctx;
     const ctx = this.ctx;
 
 
     if (this.pendingClip) {
     if (this.pendingClip) {
@@ -2398,6 +2648,7 @@ class CanvasGraphics {
       this.pendingClip = null;
       this.pendingClip = null;
     }
     }
 
 
+    this.current.startNewPathAndClipBox(this.current.clipBox);
     ctx.beginPath();
     ctx.beginPath();
   }
   }
 
 

+ 1 - 1
lib/display/display_utils.js

@@ -24,6 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.StatTimer = exports.RenderingCancelledException = exports.PixelsPerInch = exports.PageViewport = exports.PDFDateString = exports.LinkTarget = exports.DOMStandardFontDataFactory = exports.DOMSVGFactory = exports.DOMCanvasFactory = exports.DOMCMapReaderFactory = void 0;
 exports.addLinkAttributes = addLinkAttributes;
 exports.addLinkAttributes = addLinkAttributes;
 exports.deprecated = deprecated;
 exports.deprecated = deprecated;
 exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.getFilenameFromUrl = getFilenameFromUrl;
@@ -33,7 +34,6 @@ exports.isDataScheme = isDataScheme;
 exports.isPdfFile = isPdfFile;
 exports.isPdfFile = isPdfFile;
 exports.isValidFetchUrl = isValidFetchUrl;
 exports.isValidFetchUrl = isValidFetchUrl;
 exports.loadScript = loadScript;
 exports.loadScript = loadScript;
-exports.StatTimer = exports.RenderingCancelledException = exports.PixelsPerInch = exports.PDFDateString = exports.PageViewport = exports.LinkTarget = exports.DOMSVGFactory = exports.DOMStandardFontDataFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 

+ 1 - 2
lib/display/font_loader.js

@@ -294,8 +294,7 @@ exports.FontLoader = FontLoader;
       this._document.body.appendChild(div);
       this._document.body.appendChild(div);
 
 
       isFontReady(loadTestFontId, () => {
       isFontReady(loadTestFontId, () => {
-        this._document.body.removeChild(div);
-
+        div.remove();
         request.complete();
         request.complete();
       });
       });
     }
     }

+ 9 - 6
lib/display/metadata.js

@@ -29,28 +29,31 @@ exports.Metadata = void 0;
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
 class Metadata {
 class Metadata {
+  #metadataMap;
+  #data;
+
   constructor({
   constructor({
     parsedData,
     parsedData,
     rawData
     rawData
   }) {
   }) {
-    this._metadataMap = parsedData;
-    this._data = rawData;
+    this.#metadataMap = parsedData;
+    this.#data = rawData;
   }
   }
 
 
   getRaw() {
   getRaw() {
-    return this._data;
+    return this.#data;
   }
   }
 
 
   get(name) {
   get(name) {
-    return this._metadataMap.get(name) ?? null;
+    return this.#metadataMap.get(name) ?? null;
   }
   }
 
 
   getAll() {
   getAll() {
-    return (0, _util.objectFromMap)(this._metadataMap);
+    return (0, _util.objectFromMap)(this.#metadataMap);
   }
   }
 
 
   has(name) {
   has(name) {
-    return this._metadataMap.has(name);
+    return this.#metadataMap.has(name);
   }
   }
 
 
 }
 }

+ 1 - 1
lib/display/node_utils.js

@@ -24,7 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-exports.NodeStandardFontDataFactory = exports.NodeCMapReaderFactory = exports.NodeCanvasFactory = void 0;
+exports.NodeStandardFontDataFactory = exports.NodeCanvasFactory = exports.NodeCMapReaderFactory = void 0;
 
 
 var _base_factory = require("./base_factory.js");
 var _base_factory = require("./base_factory.js");
 
 

+ 39 - 36
lib/display/pattern_helper.js

@@ -24,11 +24,18 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.TilingPattern = exports.PathType = void 0;
 exports.getShadingPattern = getShadingPattern;
 exports.getShadingPattern = getShadingPattern;
-exports.TilingPattern = void 0;
 
 
 var _util = require("../shared/util.js");
 var _util = require("../shared/util.js");
 
 
+const PathType = {
+  FILL: "Fill",
+  STROKE: "Stroke",
+  SHADING: "Shading"
+};
+exports.PathType = PathType;
+
 function applyBoundingBox(ctx, bbox) {
 function applyBoundingBox(ctx, bbox) {
   if (!bbox || typeof Path2D === "undefined") {
   if (!bbox || typeof Path2D === "undefined") {
     return;
     return;
@@ -55,7 +62,7 @@ class BaseShadingPattern {
 }
 }
 
 
 class RadialAxialShadingPattern extends BaseShadingPattern {
 class RadialAxialShadingPattern extends BaseShadingPattern {
-  constructor(IR, cachedCanvasPatterns) {
+  constructor(IR) {
     super();
     super();
     this._type = IR[1];
     this._type = IR[1];
     this._bbox = IR[2];
     this._bbox = IR[2];
@@ -65,7 +72,6 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
     this._r0 = IR[6];
     this._r0 = IR[6];
     this._r1 = IR[7];
     this._r1 = IR[7];
     this.matrix = null;
     this.matrix = null;
-    this.cachedCanvasPatterns = cachedCanvasPatterns;
   }
   }
 
 
   _createGradient(ctx) {
   _createGradient(ctx) {
@@ -84,36 +90,30 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
     return grad;
     return grad;
   }
   }
 
 
-  getPattern(ctx, owner, inverse, shadingFill = false) {
+  getPattern(ctx, owner, inverse, pathType) {
     let pattern;
     let pattern;
 
 
-    if (!shadingFill) {
-      if (this.cachedCanvasPatterns.has(this)) {
-        pattern = this.cachedCanvasPatterns.get(this);
-      } else {
-        const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", owner.ctx.canvas.width, owner.ctx.canvas.height, true);
-        const tmpCtx = tmpCanvas.context;
-        tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
-        tmpCtx.beginPath();
-        tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
-        tmpCtx.setTransform.apply(tmpCtx, owner.baseTransform);
-
-        if (this.matrix) {
-          tmpCtx.transform.apply(tmpCtx, this.matrix);
-        }
+    if (pathType === PathType.STROKE || pathType === PathType.FILL) {
+      const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, ctx.mozCurrentTransform) || [0, 0, 0, 0];
+      const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
+      const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;
+      const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height, true);
+      const tmpCtx = tmpCanvas.context;
+      tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
+      tmpCtx.beginPath();
+      tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
+      tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]);
+      inverse = _util.Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]);
+      tmpCtx.transform.apply(tmpCtx, owner.baseTransform);
 
 
-        applyBoundingBox(tmpCtx, this._bbox);
-        tmpCtx.fillStyle = this._createGradient(tmpCtx);
-        tmpCtx.fill();
-        pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
-        this.cachedCanvasPatterns.set(this, pattern);
+      if (this.matrix) {
+        tmpCtx.transform.apply(tmpCtx, this.matrix);
       }
       }
-    } else {
-      applyBoundingBox(ctx, this._bbox);
-      pattern = this._createGradient(ctx);
-    }
 
 
-    if (!shadingFill) {
+      applyBoundingBox(tmpCtx, this._bbox);
+      tmpCtx.fillStyle = this._createGradient(tmpCtx);
+      tmpCtx.fill();
+      pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat");
       const domMatrix = new DOMMatrix(inverse);
       const domMatrix = new DOMMatrix(inverse);
 
 
       try {
       try {
@@ -121,6 +121,9 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
       } catch (ex) {
       } catch (ex) {
         (0, _util.warn)(`RadialAxialShadingPattern.getPattern: "${ex?.message}".`);
         (0, _util.warn)(`RadialAxialShadingPattern.getPattern: "${ex?.message}".`);
       }
       }
+    } else {
+      applyBoundingBox(ctx, this._bbox);
+      pattern = this._createGradient(ctx);
     }
     }
 
 
     return pattern;
     return pattern;
@@ -351,11 +354,11 @@ class MeshShadingPattern extends BaseShadingPattern {
     };
     };
   }
   }
 
 
-  getPattern(ctx, owner, inverse, shadingFill = false) {
+  getPattern(ctx, owner, inverse, pathType) {
     applyBoundingBox(ctx, this._bbox);
     applyBoundingBox(ctx, this._bbox);
     let scale;
     let scale;
 
 
-    if (shadingFill) {
+    if (pathType === PathType.SHADING) {
       scale = _util.Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
       scale = _util.Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
     } else {
     } else {
       scale = _util.Util.singularValueDecompose2dScale(owner.baseTransform);
       scale = _util.Util.singularValueDecompose2dScale(owner.baseTransform);
@@ -367,9 +370,9 @@ class MeshShadingPattern extends BaseShadingPattern {
       }
       }
     }
     }
 
 
-    const temporaryPatternCanvas = this._createMeshCanvas(scale, shadingFill ? null : this._background, owner.cachedCanvases);
+    const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases);
 
 
-    if (!shadingFill) {
+    if (pathType !== PathType.SHADING) {
       ctx.setTransform.apply(ctx, owner.baseTransform);
       ctx.setTransform.apply(ctx, owner.baseTransform);
 
 
       if (this.matrix) {
       if (this.matrix) {
@@ -391,10 +394,10 @@ class DummyShadingPattern extends BaseShadingPattern {
 
 
 }
 }
 
 
-function getShadingPattern(IR, cachedCanvasPatterns) {
+function getShadingPattern(IR) {
   switch (IR[0]) {
   switch (IR[0]) {
     case "RadialAxial":
     case "RadialAxial":
-      return new RadialAxialShadingPattern(IR, cachedCanvasPatterns);
+      return new RadialAxialShadingPattern(IR);
 
 
     case "Mesh":
     case "Mesh":
       return new MeshShadingPattern(IR);
       return new MeshShadingPattern(IR);
@@ -539,10 +542,10 @@ class TilingPattern {
     }
     }
   }
   }
 
 
-  getPattern(ctx, owner, inverse, shadingFill = false) {
+  getPattern(ctx, owner, inverse, pathType) {
     let matrix = inverse;
     let matrix = inverse;
 
 
-    if (!shadingFill) {
+    if (pathType !== PathType.SHADING) {
       matrix = _util.Util.transform(matrix, owner.baseTransform);
       matrix = _util.Util.transform(matrix, owner.baseTransform);
 
 
       if (this.matrix) {
       if (this.matrix) {

+ 4 - 0
lib/display/svg.js

@@ -1067,6 +1067,10 @@ exports.SVGGraphics = SVGGraphics;
     }
     }
 
 
     _makeShadingPattern(args) {
     _makeShadingPattern(args) {
+      if (typeof args === "string") {
+        args = this.objs.get(args);
+      }
+
       switch (args[0]) {
       switch (args[0]) {
         case "RadialAxial":
         case "RadialAxial":
           const shadingId = `shading${shadingCount++}`;
           const shadingId = `shading${shadingCount++}`;

+ 1 - 1
lib/display/text_layer.js

@@ -154,7 +154,7 @@ function appendText(task, geom, styles, ctx) {
 
 
   if (geom.str.length > 1 || task._enhanceTextSelection && AllWhitespaceRegexp.test(geom.str)) {
   if (geom.str.length > 1 || task._enhanceTextSelection && AllWhitespaceRegexp.test(geom.str)) {
     shouldScaleText = true;
     shouldScaleText = true;
-  } else if (geom.transform[0] !== geom.transform[3]) {
+  } else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) {
     const absScaleX = Math.abs(geom.transform[0]),
     const absScaleX = Math.abs(geom.transform[0]),
           absScaleY = Math.abs(geom.transform[3]);
           absScaleY = Math.abs(geom.transform[3]);
 
 

+ 14 - 5
lib/display/xfa_layer.js

@@ -133,7 +133,9 @@ class XfaLayer {
         if (key === "textContent") {
         if (key === "textContent") {
           html.textContent = value;
           html.textContent = value;
         } else if (key === "class") {
         } else if (key === "class") {
-          html.setAttribute(key, value.join(" "));
+          if (value.length) {
+            html.setAttribute(key, value.join(" "));
+          }
         } else {
         } else {
           if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
           if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
             continue;
             continue;
@@ -162,7 +164,7 @@ class XfaLayer {
   static render(parameters) {
   static render(parameters) {
     const storage = parameters.annotationStorage;
     const storage = parameters.annotationStorage;
     const linkService = parameters.linkService;
     const linkService = parameters.linkService;
-    const root = parameters.xfa;
+    const root = parameters.xfaHtml;
     const intent = parameters.intent || "display";
     const intent = parameters.intent || "display";
     const rootHtml = document.createElement(root.name);
     const rootHtml = document.createElement(root.name);
 
 
@@ -178,9 +180,16 @@ class XfaLayer {
     const stack = [[root, -1, rootHtml]];
     const stack = [[root, -1, rootHtml]];
     const rootDiv = parameters.div;
     const rootDiv = parameters.div;
     rootDiv.appendChild(rootHtml);
     rootDiv.appendChild(rootHtml);
-    const transform = `matrix(${parameters.viewport.transform.join(",")})`;
-    rootDiv.style.transform = transform;
-    rootDiv.setAttribute("class", "xfaLayer xfaFont");
+
+    if (parameters.viewport) {
+      const transform = `matrix(${parameters.viewport.transform.join(",")})`;
+      rootDiv.style.transform = transform;
+    }
+
+    if (intent !== "richText") {
+      rootDiv.setAttribute("class", "xfaLayer xfaFont");
+    }
+
     const textDivs = [];
     const textDivs = [];
 
 
     while (stack.length > 0) {
     while (stack.length > 0) {

+ 70 - 70
lib/pdf.js

@@ -24,34 +24,34 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
-Object.defineProperty(exports, "addLinkAttributes", {
+Object.defineProperty(exports, "AnnotationLayer", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.addLinkAttributes;
+    return _annotation_layer.AnnotationLayer;
   }
   }
 });
 });
-Object.defineProperty(exports, "getFilenameFromUrl", {
+Object.defineProperty(exports, "AnnotationMode", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.getFilenameFromUrl;
+    return _util.AnnotationMode;
   }
   }
 });
 });
-Object.defineProperty(exports, "getPdfFilenameFromUrl", {
+Object.defineProperty(exports, "CMapCompressionType", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.getPdfFilenameFromUrl;
+    return _util.CMapCompressionType;
   }
   }
 });
 });
-Object.defineProperty(exports, "getXfaPageViewport", {
+Object.defineProperty(exports, "GlobalWorkerOptions", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.getXfaPageViewport;
+    return _worker_options.GlobalWorkerOptions;
   }
   }
 });
 });
-Object.defineProperty(exports, "isPdfFile", {
+Object.defineProperty(exports, "InvalidPDFException", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.isPdfFile;
+    return _util.InvalidPDFException;
   }
   }
 });
 });
 Object.defineProperty(exports, "LinkTarget", {
 Object.defineProperty(exports, "LinkTarget", {
@@ -60,130 +60,130 @@ Object.defineProperty(exports, "LinkTarget", {
     return _display_utils.LinkTarget;
     return _display_utils.LinkTarget;
   }
   }
 });
 });
-Object.defineProperty(exports, "loadScript", {
+Object.defineProperty(exports, "LoopbackPort", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.loadScript;
+    return _api.LoopbackPort;
   }
   }
 });
 });
-Object.defineProperty(exports, "PDFDateString", {
+Object.defineProperty(exports, "MissingPDFException", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.PDFDateString;
+    return _util.MissingPDFException;
   }
   }
 });
 });
-Object.defineProperty(exports, "PixelsPerInch", {
+Object.defineProperty(exports, "OPS", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.PixelsPerInch;
+    return _util.OPS;
   }
   }
 });
 });
-Object.defineProperty(exports, "RenderingCancelledException", {
+Object.defineProperty(exports, "PDFDataRangeTransport", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _display_utils.RenderingCancelledException;
+    return _api.PDFDataRangeTransport;
   }
   }
 });
 });
-Object.defineProperty(exports, "AnnotationMode", {
+Object.defineProperty(exports, "PDFDateString", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.AnnotationMode;
+    return _display_utils.PDFDateString;
   }
   }
 });
 });
-Object.defineProperty(exports, "CMapCompressionType", {
+Object.defineProperty(exports, "PDFWorker", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.CMapCompressionType;
+    return _api.PDFWorker;
   }
   }
 });
 });
-Object.defineProperty(exports, "createObjectURL", {
+Object.defineProperty(exports, "PasswordResponses", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.createObjectURL;
+    return _util.PasswordResponses;
   }
   }
 });
 });
-Object.defineProperty(exports, "createPromiseCapability", {
+Object.defineProperty(exports, "PermissionFlag", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.createPromiseCapability;
+    return _util.PermissionFlag;
   }
   }
 });
 });
-Object.defineProperty(exports, "createValidAbsoluteUrl", {
+Object.defineProperty(exports, "PixelsPerInch", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.createValidAbsoluteUrl;
+    return _display_utils.PixelsPerInch;
   }
   }
 });
 });
-Object.defineProperty(exports, "InvalidPDFException", {
+Object.defineProperty(exports, "RenderingCancelledException", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.InvalidPDFException;
+    return _display_utils.RenderingCancelledException;
   }
   }
 });
 });
-Object.defineProperty(exports, "MissingPDFException", {
+Object.defineProperty(exports, "SVGGraphics", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.MissingPDFException;
+    return _svg.SVGGraphics;
   }
   }
 });
 });
-Object.defineProperty(exports, "OPS", {
+Object.defineProperty(exports, "UNSUPPORTED_FEATURES", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.OPS;
+    return _util.UNSUPPORTED_FEATURES;
   }
   }
 });
 });
-Object.defineProperty(exports, "PasswordResponses", {
+Object.defineProperty(exports, "UnexpectedResponseException", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.PasswordResponses;
+    return _util.UnexpectedResponseException;
   }
   }
 });
 });
-Object.defineProperty(exports, "PermissionFlag", {
+Object.defineProperty(exports, "Util", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.PermissionFlag;
+    return _util.Util;
   }
   }
 });
 });
-Object.defineProperty(exports, "removeNullCharacters", {
+Object.defineProperty(exports, "VerbosityLevel", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.removeNullCharacters;
+    return _util.VerbosityLevel;
   }
   }
 });
 });
-Object.defineProperty(exports, "shadow", {
+Object.defineProperty(exports, "XfaLayer", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.shadow;
+    return _xfa_layer.XfaLayer;
   }
   }
 });
 });
-Object.defineProperty(exports, "UnexpectedResponseException", {
+Object.defineProperty(exports, "addLinkAttributes", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.UnexpectedResponseException;
+    return _display_utils.addLinkAttributes;
   }
   }
 });
 });
-Object.defineProperty(exports, "UNSUPPORTED_FEATURES", {
+Object.defineProperty(exports, "build", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.UNSUPPORTED_FEATURES;
+    return _api.build;
   }
   }
 });
 });
-Object.defineProperty(exports, "Util", {
+Object.defineProperty(exports, "createObjectURL", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.Util;
+    return _util.createObjectURL;
   }
   }
 });
 });
-Object.defineProperty(exports, "VerbosityLevel", {
+Object.defineProperty(exports, "createPromiseCapability", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _util.VerbosityLevel;
+    return _util.createPromiseCapability;
   }
   }
 });
 });
-Object.defineProperty(exports, "build", {
+Object.defineProperty(exports, "createValidAbsoluteUrl", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _api.build;
+    return _util.createValidAbsoluteUrl;
   }
   }
 });
 });
 Object.defineProperty(exports, "getDocument", {
 Object.defineProperty(exports, "getDocument", {
@@ -192,40 +192,40 @@ Object.defineProperty(exports, "getDocument", {
     return _api.getDocument;
     return _api.getDocument;
   }
   }
 });
 });
-Object.defineProperty(exports, "LoopbackPort", {
+Object.defineProperty(exports, "getFilenameFromUrl", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _api.LoopbackPort;
+    return _display_utils.getFilenameFromUrl;
   }
   }
 });
 });
-Object.defineProperty(exports, "PDFDataRangeTransport", {
+Object.defineProperty(exports, "getPdfFilenameFromUrl", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _api.PDFDataRangeTransport;
+    return _display_utils.getPdfFilenameFromUrl;
   }
   }
 });
 });
-Object.defineProperty(exports, "PDFWorker", {
+Object.defineProperty(exports, "getXfaPageViewport", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _api.PDFWorker;
+    return _display_utils.getXfaPageViewport;
   }
   }
 });
 });
-Object.defineProperty(exports, "version", {
+Object.defineProperty(exports, "isPdfFile", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _api.version;
+    return _display_utils.isPdfFile;
   }
   }
 });
 });
-Object.defineProperty(exports, "AnnotationLayer", {
+Object.defineProperty(exports, "loadScript", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _annotation_layer.AnnotationLayer;
+    return _display_utils.loadScript;
   }
   }
 });
 });
-Object.defineProperty(exports, "GlobalWorkerOptions", {
+Object.defineProperty(exports, "removeNullCharacters", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _worker_options.GlobalWorkerOptions;
+    return _util.removeNullCharacters;
   }
   }
 });
 });
 Object.defineProperty(exports, "renderTextLayer", {
 Object.defineProperty(exports, "renderTextLayer", {
@@ -234,16 +234,16 @@ Object.defineProperty(exports, "renderTextLayer", {
     return _text_layer.renderTextLayer;
     return _text_layer.renderTextLayer;
   }
   }
 });
 });
-Object.defineProperty(exports, "SVGGraphics", {
+Object.defineProperty(exports, "shadow", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _svg.SVGGraphics;
+    return _util.shadow;
   }
   }
 });
 });
-Object.defineProperty(exports, "XfaLayer", {
+Object.defineProperty(exports, "version", {
   enumerable: true,
   enumerable: true,
   get: function () {
   get: function () {
-    return _xfa_layer.XfaLayer;
+    return _api.version;
   }
   }
 });
 });
 
 
@@ -265,8 +265,8 @@ var _svg = require("./display/svg.js");
 
 
 var _xfa_layer = require("./display/xfa_layer.js");
 var _xfa_layer = require("./display/xfa_layer.js");
 
 
-const pdfjsVersion = '2.11.338';
-const pdfjsBuild = 'dedff3c98';
+const pdfjsVersion = '2.12.313';
+const pdfjsBuild = 'a2ae56f39';
 {
 {
   if (_is_node.isNodeJS) {
   if (_is_node.isNodeJS) {
     const {
     const {

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 2 - 2
lib/pdf.sandbox.js


+ 2 - 2
lib/pdf.worker.js

@@ -33,5 +33,5 @@ Object.defineProperty(exports, "WorkerMessageHandler", {
 
 
 var _worker = require("./core/worker.js");
 var _worker = require("./core/worker.js");
 
 
-const pdfjsVersion = '2.11.338';
-const pdfjsBuild = 'dedff3c98';
+const pdfjsVersion = '2.12.313';
+const pdfjsBuild = 'a2ae56f39';

+ 4 - 15
lib/shared/message_handler.js

@@ -79,7 +79,6 @@ class MessageHandler {
     this.comObj = comObj;
     this.comObj = comObj;
     this.callbackId = 1;
     this.callbackId = 1;
     this.streamId = 1;
     this.streamId = 1;
-    this.postMessageTransfers = true;
     this.streamSinks = Object.create(null);
     this.streamSinks = Object.create(null);
     this.streamControllers = Object.create(null);
     this.streamControllers = Object.create(null);
     this.callbackCapabilities = Object.create(null);
     this.callbackCapabilities = Object.create(null);
@@ -173,7 +172,7 @@ class MessageHandler {
   }
   }
 
 
   send(actionName, data, transfers) {
   send(actionName, data, transfers) {
-    this._postMessage({
+    this.comObj.postMessage({
       sourceName: this.sourceName,
       sourceName: this.sourceName,
       targetName: this.targetName,
       targetName: this.targetName,
       action: actionName,
       action: actionName,
@@ -187,7 +186,7 @@ class MessageHandler {
     this.callbackCapabilities[callbackId] = capability;
     this.callbackCapabilities[callbackId] = capability;
 
 
     try {
     try {
-      this._postMessage({
+      this.comObj.postMessage({
         sourceName: this.sourceName,
         sourceName: this.sourceName,
         targetName: this.targetName,
         targetName: this.targetName,
         action: actionName,
         action: actionName,
@@ -216,8 +215,7 @@ class MessageHandler {
           cancelCall: null,
           cancelCall: null,
           isClosed: false
           isClosed: false
         };
         };
-
-        this._postMessage({
+        comObj.postMessage({
           sourceName,
           sourceName,
           targetName,
           targetName,
           action: actionName,
           action: actionName,
@@ -225,7 +223,6 @@ class MessageHandler {
           data,
           data,
           desiredSize: controller.desiredSize
           desiredSize: controller.desiredSize
         }, transfers);
         }, transfers);
-
         return startCapability.promise;
         return startCapability.promise;
       },
       },
       pull: controller => {
       pull: controller => {
@@ -278,7 +275,7 @@ class MessageHandler {
           this.ready = this.sinkCapability.promise;
           this.ready = this.sinkCapability.promise;
         }
         }
 
 
-        self._postMessage({
+        comObj.postMessage({
           sourceName,
           sourceName,
           targetName,
           targetName,
           stream: StreamKind.ENQUEUE,
           stream: StreamKind.ENQUEUE,
@@ -497,14 +494,6 @@ class MessageHandler {
     delete this.streamControllers[streamId];
     delete this.streamControllers[streamId];
   }
   }
 
 
-  _postMessage(message, transfers) {
-    if (transfers && this.postMessageTransfers) {
-      this.comObj.postMessage(message, transfers);
-    } else {
-      this.comObj.postMessage(message);
-    }
-  }
-
   destroy() {
   destroy() {
     this.comObj.removeEventListener("message", this._onComObjOnMessage);
     this.comObj.removeEventListener("message", this._onComObjOnMessage);
   }
   }

+ 78 - 4
lib/shared/util.js

@@ -24,6 +24,7 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.VerbosityLevel = exports.Util = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.UNSUPPORTED_FEATURES = exports.TextRenderingMode = exports.StreamType = exports.RenderingIntentFlag = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.PageActionEventType = exports.OPS = exports.MissingPDFException = exports.IsLittleEndianCached = exports.IsEvalSupportedCached = exports.InvalidPDFException = exports.ImageKind = exports.IDENTITY_MATRIX = exports.FormatError = exports.FontType = exports.FONT_IDENTITY_MATRIX = exports.DocumentActionEventType = exports.CMapCompressionType = exports.BaseException = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMode = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.AnnotationActionEventType = exports.AbortException = void 0;
 exports.arrayByteLength = arrayByteLength;
 exports.arrayByteLength = arrayByteLength;
 exports.arraysToBytes = arraysToBytes;
 exports.arraysToBytes = arraysToBytes;
 exports.assert = assert;
 exports.assert = assert;
@@ -55,7 +56,6 @@ exports.stringToUTF8String = stringToUTF8String;
 exports.unreachable = unreachable;
 exports.unreachable = unreachable;
 exports.utf8StringToString = utf8StringToString;
 exports.utf8StringToString = utf8StringToString;
 exports.warn = warn;
 exports.warn = warn;
-exports.VerbosityLevel = exports.Util = exports.UNSUPPORTED_FEATURES = exports.UnknownErrorException = exports.UnexpectedResponseException = exports.TextRenderingMode = exports.StreamType = exports.RenderingIntentFlag = exports.PermissionFlag = exports.PasswordResponses = exports.PasswordException = exports.PageActionEventType = exports.OPS = exports.MissingPDFException = exports.IsLittleEndianCached = exports.IsEvalSupportedCached = exports.InvalidPDFException = exports.ImageKind = exports.IDENTITY_MATRIX = exports.FormatError = exports.FontType = exports.FONT_IDENTITY_MATRIX = exports.DocumentActionEventType = exports.CMapCompressionType = exports.BaseException = exports.AnnotationType = exports.AnnotationStateModelType = exports.AnnotationReviewState = exports.AnnotationReplyType = exports.AnnotationMode = exports.AnnotationMarkedState = exports.AnnotationFlag = exports.AnnotationFieldFlag = exports.AnnotationBorderStyleType = exports.AnnotationActionEventType = exports.AbortException = void 0;
 
 
 require("./compatibility.js");
 require("./compatibility.js");
 
 
@@ -592,14 +592,19 @@ class AbortException extends BaseException {
 }
 }
 
 
 exports.AbortException = AbortException;
 exports.AbortException = AbortException;
-const NullCharactersRegExp = /\x00/g;
+const NullCharactersRegExp = /\x00+/g;
+const InvisibleCharactersRegExp = /[\x01-\x1F]/g;
 
 
-function removeNullCharacters(str) {
+function removeNullCharacters(str, replaceInvisible = false) {
   if (typeof str !== "string") {
   if (typeof str !== "string") {
     warn("The argument for removeNullCharacters must be a string.");
     warn("The argument for removeNullCharacters must be a string.");
     return str;
     return str;
   }
   }
 
 
+  if (replaceInvisible) {
+    str = str.replace(InvisibleCharactersRegExp, " ");
+  }
+
   return str.replace(NullCharactersRegExp, "");
   return str.replace(NullCharactersRegExp, "");
 }
 }
 
 
@@ -826,6 +831,75 @@ class Util {
     return result;
     return result;
   }
   }
 
 
+  static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3) {
+    const tvalues = [],
+          bounds = [[], []];
+    let a, b, c, t, t1, t2, b2ac, sqrtb2ac;
+
+    for (let i = 0; i < 2; ++i) {
+      if (i === 0) {
+        b = 6 * x0 - 12 * x1 + 6 * x2;
+        a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
+        c = 3 * x1 - 3 * x0;
+      } else {
+        b = 6 * y0 - 12 * y1 + 6 * y2;
+        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
+        c = 3 * y1 - 3 * y0;
+      }
+
+      if (Math.abs(a) < 1e-12) {
+        if (Math.abs(b) < 1e-12) {
+          continue;
+        }
+
+        t = -c / b;
+
+        if (0 < t && t < 1) {
+          tvalues.push(t);
+        }
+
+        continue;
+      }
+
+      b2ac = b * b - 4 * c * a;
+      sqrtb2ac = Math.sqrt(b2ac);
+
+      if (b2ac < 0) {
+        continue;
+      }
+
+      t1 = (-b + sqrtb2ac) / (2 * a);
+
+      if (0 < t1 && t1 < 1) {
+        tvalues.push(t1);
+      }
+
+      t2 = (-b - sqrtb2ac) / (2 * a);
+
+      if (0 < t2 && t2 < 1) {
+        tvalues.push(t2);
+      }
+    }
+
+    let j = tvalues.length,
+        mt;
+    const jlen = j;
+
+    while (j--) {
+      t = tvalues[j];
+      mt = 1 - t;
+      bounds[0][j] = mt * mt * mt * x0 + 3 * mt * mt * t * x1 + 3 * mt * t * t * x2 + t * t * t * x3;
+      bounds[1][j] = mt * mt * mt * y0 + 3 * mt * mt * t * y1 + 3 * mt * t * t * y2 + t * t * t * y3;
+    }
+
+    bounds[0][jlen] = x0;
+    bounds[1][jlen] = y0;
+    bounds[0][jlen + 1] = x3;
+    bounds[1][jlen + 1] = y3;
+    bounds[0].length = bounds[1].length = jlen + 2;
+    return [Math.min(...bounds[0]), Math.min(...bounds[1]), Math.max(...bounds[0]), Math.max(...bounds[1])];
+  }
+
 }
 }
 
 
 exports.Util = Util;
 exports.Util = Util;
@@ -947,7 +1021,7 @@ function createPromiseCapability() {
 }
 }
 
 
 function createObjectURL(data, contentType = "", forceDataSchema = false) {
 function createObjectURL(data, contentType = "", forceDataSchema = false) {
-  if (URL.createObjectURL && !forceDataSchema) {
+  if (URL.createObjectURL && typeof Blob !== "undefined" && !forceDataSchema) {
     return URL.createObjectURL(new Blob([data], {
     return URL.createObjectURL(new Blob([data], {
       type: contentType
       type: contentType
     }));
     }));

+ 18 - 18
lib/test/unit/annotation_spec.js

@@ -1460,10 +1460,10 @@ describe("annotation", function () {
       partialEvaluator.xref = xref;
       partialEvaluator.xref = xref;
       const annotation = await _annotation.AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock);
       const annotation = await _annotation.AnnotationFactory.create(xref, textWidgetRef, pdfManagerMock, idFactoryMock);
       const annotationStorage = new Map();
       const annotationStorage = new Map();
-      const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      const operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["271R", [0, 0, 32, 10], [32, 0, 0, 10, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["271R", [0, 0, 32, 10], [32, 0, 0, 10, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
     });
     });
     it("should render auto-sized text for printing", async function () {
     it("should render auto-sized text for printing", async function () {
@@ -1872,10 +1872,10 @@ describe("annotation", function () {
       annotationStorage.set(annotation.data.id, {
       annotationStorage.set(annotation.data.id, {
         value: true
         value: true
       });
       });
-      const operatorList = await annotation.getOperatorList(checkboxEvaluator, task, false, annotationStorage);
+      const operatorList = await annotation.getOperatorList(checkboxEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(5);
       expect(operatorList.argsArray.length).toEqual(5);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.dependency, _util.OPS.setFont, _util.OPS.showText, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.dependency, _util.OPS.setFont, _util.OPS.showText, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[3][0][0].unicode).toEqual("4");
       expect(operatorList.argsArray[3][0][0].unicode).toEqual("4");
     });
     });
     it("should render checkboxes for printing", async function () {
     it("should render checkboxes for printing", async function () {
@@ -1907,18 +1907,18 @@ describe("annotation", function () {
       annotationStorage.set(annotation.data.id, {
       annotationStorage.set(annotation.data.id, {
         value: true
         value: true
       });
       });
-      let operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      let operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       annotationStorage.set(annotation.data.id, {
       annotationStorage.set(annotation.data.id, {
         value: false
         value: false
       });
       });
-      operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     });
     it("should render checkboxes for printing twice", async function () {
     it("should render checkboxes for printing twice", async function () {
@@ -1953,10 +1953,10 @@ describe("annotation", function () {
         annotationStorage.set(annotation.data.id, {
         annotationStorage.set(annotation.data.id, {
           value: true
           value: true
         });
         });
-        const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+        const operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
         expect(operatorList.argsArray.length).toEqual(3);
         expect(operatorList.argsArray.length).toEqual(3);
         expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
         expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-        expect(operatorList.argsArray[0]).toEqual(["1249R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+        expect(operatorList.argsArray[0]).toEqual(["1249R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
         expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
         expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       }
       }
     });
     });
@@ -1987,10 +1987,10 @@ describe("annotation", function () {
       const task = new _worker.WorkerTask("test print");
       const task = new _worker.WorkerTask("test print");
       const annotation = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       const annotation = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       const annotationStorage = new Map();
       const annotationStorage = new Map();
-      const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      const operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
     });
     });
     it("should save checkboxes", async function () {
     it("should save checkboxes", async function () {
@@ -2130,18 +2130,18 @@ describe("annotation", function () {
       annotationStorage.set(annotation.data.id, {
       annotationStorage.set(annotation.data.id, {
         value: true
         value: true
       });
       });
-      let operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      let operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       annotationStorage.set(annotation.data.id, {
       annotationStorage.set(annotation.data.id, {
         value: false
         value: false
       });
       });
-      operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     });
     it("should render radio buttons for printing using normal appearance", async function () {
     it("should render radio buttons for printing using normal appearance", async function () {
@@ -2172,10 +2172,10 @@ describe("annotation", function () {
       const task = new _worker.WorkerTask("test print");
       const task = new _worker.WorkerTask("test print");
       const annotation = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       const annotation = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       const annotationStorage = new Map();
       const annotationStorage = new Map();
-      const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      const operatorList = await annotation.getOperatorList(partialEvaluator, task, _util.RenderingIntentFlag.PRINT, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.argsArray.length).toEqual(3);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
       expect(operatorList.fnArray).toEqual([_util.OPS.beginAnnotation, _util.OPS.setFillRGBColor, _util.OPS.endAnnotation]);
-      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0]]);
+      expect(operatorList.argsArray[0]).toEqual(["124R", [0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0], false]);
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
       expect(operatorList.argsArray[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     });
     it("should save radio buttons", async function () {
     it("should save radio buttons", async function () {

+ 328 - 17
lib/test/unit/api_spec.js

@@ -58,6 +58,10 @@ describe("api", function () {
     }, WAIT_TIMEOUT);
     }, WAIT_TIMEOUT);
   }
   }
 
 
+  function mergeText(items) {
+    return items.map(chunk => chunk.str + (chunk.hasEOL ? "\n" : "")).join("");
+  }
+
   describe("getDocument", function () {
   describe("getDocument", function () {
     it("creates pdf doc from URL-string", async function () {
     it("creates pdf doc from URL-string", async function () {
       const urlStr = _test_utils.TEST_PDFS_PATH + basicApiFileName;
       const urlStr = _test_utils.TEST_PDFS_PATH + basicApiFileName;
@@ -311,6 +315,138 @@ describe("api", function () {
       expect(+docNum1).toBeLessThan(+docNum2);
       expect(+docNum1).toBeLessThan(+docNum2);
       await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
       await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
     });
     });
+    it("creates pdf doc from PDF file with bad XRef entry", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("PDFBOX-4352-0.pdf", {
+        rangeChunkSize: 100
+      }));
+      expect(loadingTask instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDocument = await loadingTask.promise;
+      expect(pdfDocument.numPages).toEqual(1);
+      const page = await pdfDocument.getPage(1);
+      expect(page instanceof _api.PDFPageProxy).toEqual(true);
+      const opList = await page.getOperatorList();
+      expect(opList.fnArray.length).toEqual(0);
+      expect(opList.argsArray.length).toEqual(0);
+      expect(opList.lastChunk).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("creates pdf doc from PDF file with bad XRef header", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("GHOSTSCRIPT-698804-1-fuzzed.pdf"));
+      expect(loadingTask instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDocument = await loadingTask.promise;
+      expect(pdfDocument.numPages).toEqual(1);
+      const page = await pdfDocument.getPage(1);
+      expect(page instanceof _api.PDFPageProxy).toEqual(true);
+      const opList = await page.getOperatorList();
+      expect(opList.fnArray.length).toEqual(0);
+      expect(opList.argsArray.length).toEqual(0);
+      expect(opList.lastChunk).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("creates pdf doc from PDF file with bad XRef byteWidths", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("REDHAT-1531897-0.pdf"));
+      expect(loadingTask instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+
+      try {
+        await loadingTask.promise;
+        expect(false).toEqual(true);
+      } catch (reason) {
+        expect(reason instanceof _util.InvalidPDFException).toEqual(true);
+        expect(reason.message).toEqual("Invalid PDF structure.");
+      }
+
+      await loadingTask.destroy();
+    });
+    it("creates pdf doc from PDF file with inaccessible /Pages tree", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-395-0-fuzzed.pdf"));
+      expect(loadingTask instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+
+      try {
+        await loadingTask.promise;
+        expect(false).toEqual(true);
+      } catch (reason) {
+        expect(reason instanceof _util.InvalidPDFException).toEqual(true);
+        expect(reason.message).toEqual("Invalid Root reference.");
+      }
+
+      await loadingTask.destroy();
+    });
+    it("creates pdf doc from PDF files, with bad /Pages tree /Count", async function () {
+      const loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-67295-0.pdf"));
+      const loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-85140-0.pdf"));
+      expect(loadingTask1 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      expect(loadingTask2 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDocument1 = await loadingTask1.promise;
+      const pdfDocument2 = await loadingTask2.promise;
+      expect(pdfDocument1.numPages).toEqual(1);
+      expect(pdfDocument2.numPages).toEqual(1);
+      const page = await pdfDocument1.getPage(1);
+      expect(page instanceof _api.PDFPageProxy).toEqual(true);
+      const opList = await page.getOperatorList();
+      expect(opList.fnArray.length).toBeGreaterThan(5);
+      expect(opList.argsArray.length).toBeGreaterThan(5);
+      expect(opList.lastChunk).toEqual(true);
+
+      try {
+        await pdfDocument2.getPage(1);
+        expect(false).toEqual(true);
+      } catch (reason) {
+        expect(reason instanceof _util.UnknownErrorException).toEqual(true);
+        expect(reason.message).toEqual("Bad (uncompressed) XRef entry: 3R");
+      }
+
+      await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
+    });
+    it("creates pdf doc from PDF files, with circular references", async function () {
+      const loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-91414-0-53.pdf"));
+      const loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-91414-0-54.pdf"));
+      expect(loadingTask1 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      expect(loadingTask2 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDocument1 = await loadingTask1.promise;
+      const pdfDocument2 = await loadingTask2.promise;
+      expect(pdfDocument1.numPages).toEqual(1);
+      expect(pdfDocument2.numPages).toEqual(1);
+      const pageA = await pdfDocument1.getPage(1);
+      const pageB = await pdfDocument2.getPage(1);
+      expect(pageA instanceof _api.PDFPageProxy).toEqual(true);
+      expect(pageB instanceof _api.PDFPageProxy).toEqual(true);
+
+      for (const opList of [await pageA.getOperatorList(), await pageB.getOperatorList()]) {
+        expect(opList.fnArray.length).toBeGreaterThan(5);
+        expect(opList.argsArray.length).toBeGreaterThan(5);
+        expect(opList.lastChunk).toEqual(true);
+      }
+
+      await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
+    });
+    it("creates pdf doc from PDF files, with bad /Pages tree /Kids entries", async function () {
+      const loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-742-0-fuzzed.pdf"));
+      const loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-937-0-fuzzed.pdf"));
+      expect(loadingTask1 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      expect(loadingTask2 instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDocument1 = await loadingTask1.promise;
+      const pdfDocument2 = await loadingTask2.promise;
+      expect(pdfDocument1.numPages).toEqual(1);
+      expect(pdfDocument2.numPages).toEqual(1);
+
+      try {
+        await pdfDocument1.getPage(1);
+        expect(false).toEqual(true);
+      } catch (reason) {
+        expect(reason instanceof _util.UnknownErrorException).toEqual(true);
+        expect(reason.message).toEqual("Page dictionary kids object is not an array.");
+      }
+
+      try {
+        await pdfDocument2.getPage(1);
+        expect(false).toEqual(true);
+      } catch (reason) {
+        expect(reason instanceof _util.UnknownErrorException).toEqual(true);
+        expect(reason.message).toEqual("Page dictionary kids object is not an array.");
+      }
+
+      await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
+    });
   });
   });
   describe("PDFWorker", function () {
   describe("PDFWorker", function () {
     it("worker created or destroyed", async function () {
     it("worker created or destroyed", async function () {
@@ -468,13 +604,23 @@ describe("api", function () {
         return pdfDoc.getPage(2).then(function (pdfPage) {
         return pdfDoc.getPage(2).then(function (pdfPage) {
           throw new Error("shall fail for invalid page");
           throw new Error("shall fail for invalid page");
         }, function (reason) {
         }, function (reason) {
-          expect(reason instanceof Error).toEqual(true);
+          expect(reason instanceof _util.UnknownErrorException).toEqual(true);
           expect(reason.message).toEqual("Pages tree contains circular reference.");
           expect(reason.message).toEqual("Pages tree contains circular reference.");
         });
         });
       });
       });
       await Promise.all([page1, page2]);
       await Promise.all([page1, page2]);
       await loadingTask.destroy();
       await loadingTask.destroy();
     });
     });
+    it("gets page multiple time, with working caches", async function () {
+      const promiseA = pdfDocument.getPage(1);
+      const promiseB = pdfDocument.getPage(1);
+      expect(promiseA instanceof Promise).toEqual(true);
+      expect(promiseA).toBe(promiseB);
+      const pageA = await promiseA;
+      const pageB = await promiseB;
+      expect(pageA instanceof _api.PDFPageProxy).toEqual(true);
+      expect(pageA).toBe(pageB);
+    });
     it("gets page index", async function () {
     it("gets page index", async function () {
       const ref = {
       const ref = {
         num: 17,
         num: 17,
@@ -493,7 +639,8 @@ describe("api", function () {
         await pdfDocument.getPageIndex(ref);
         await pdfDocument.getPageIndex(ref);
         expect(false).toEqual(true);
         expect(false).toEqual(true);
       } catch (reason) {
       } catch (reason) {
-        expect(reason instanceof Error).toEqual(true);
+        expect(reason instanceof _util.UnknownErrorException).toEqual(true);
+        expect(reason.message).toEqual("The reference does not point to a /Page dictionary.");
       }
       }
     });
     });
     it("gets destinations, from /Dests dictionary", async function () {
     it("gets destinations, from /Dests dictionary", async function () {
@@ -806,6 +953,16 @@ describe("api", function () {
       expect(outlineItemOne.color).toEqual(new Uint8ClampedArray([0, 0, 0]));
       expect(outlineItemOne.color).toEqual(new Uint8ClampedArray([0, 0, 0]));
       await loadingTask.destroy();
       await loadingTask.destroy();
     });
     });
+    it("gets outline with non-displayable chars", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue14267.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const outline = await pdfDoc.getOutline();
+      expect(Array.isArray(outline)).toEqual(true);
+      expect(outline.length).toEqual(1);
+      const outlineItem = outline[0];
+      expect(outlineItem.title).toEqual("hello\x11world");
+      await loadingTask.destroy();
+    });
     it("gets non-existent permissions", async function () {
     it("gets non-existent permissions", async function () {
       const permissions = await pdfDocument.getPermissions();
       const permissions = await pdfDocument.getPermissions();
       expect(permissions).toEqual(null);
       expect(permissions).toEqual(null);
@@ -844,6 +1001,8 @@ describe("api", function () {
       expect(info.Title).toEqual("Basic API Test");
       expect(info.Title).toEqual("Basic API Test");
       expect(info.Custom).toEqual(undefined);
       expect(info.Custom).toEqual(undefined);
       expect(info.PDFFormatVersion).toEqual("1.7");
       expect(info.PDFFormatVersion).toEqual("1.7");
+      expect(info.Language).toEqual("en");
+      expect(info.EncryptFilterName).toEqual(null);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
@@ -870,6 +1029,8 @@ describe("api", function () {
       expect(typeof custom === "object" && custom !== null).toEqual(true);
       expect(typeof custom === "object" && custom !== null).toEqual(true);
       expect(custom["PTEX.Fullbanner"]).toEqual("This is pdfeTeX, " + "Version 3.141592-1.21a-2.2 (Web2C 7.5.4) kpathsea version 3.5.6");
       expect(custom["PTEX.Fullbanner"]).toEqual("This is pdfeTeX, " + "Version 3.141592-1.21a-2.2 (Web2C 7.5.4) kpathsea version 3.5.6");
       expect(info.PDFFormatVersion).toEqual("1.4");
       expect(info.PDFFormatVersion).toEqual("1.4");
+      expect(info.Language).toEqual(null);
+      expect(info.EncryptFilterName).toEqual(null);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
@@ -889,7 +1050,10 @@ describe("api", function () {
         contentDispositionFilename,
         contentDispositionFilename,
         contentLength
         contentLength
       } = await pdfDoc.getMetadata();
       } = await pdfDoc.getMetadata();
+      expect(info.Custom).toEqual(undefined);
       expect(info.PDFFormatVersion).toEqual(null);
       expect(info.PDFFormatVersion).toEqual(null);
+      expect(info.Language).toEqual(null);
+      expect(info.EncryptFilterName).toEqual(null);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsLinearized).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsAcroFormPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
       expect(info.IsXFAPresent).toEqual(false);
@@ -900,6 +1064,29 @@ describe("api", function () {
       expect(contentLength).toEqual(624);
       expect(contentLength).toEqual(624);
       await loadingTask.destroy();
       await loadingTask.destroy();
     });
     });
+    it("gets metadata, with corrupt /Metadata XRef entry", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("PDFBOX-3148-2-fuzzed.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const {
+        info,
+        metadata,
+        contentDispositionFilename,
+        contentLength
+      } = await pdfDoc.getMetadata();
+      expect(info.Custom).toEqual(undefined);
+      expect(info.PDFFormatVersion).toEqual("1.6");
+      expect(info.Language).toEqual(null);
+      expect(info.EncryptFilterName).toEqual(null);
+      expect(info.IsLinearized).toEqual(false);
+      expect(info.IsAcroFormPresent).toEqual(true);
+      expect(info.IsXFAPresent).toEqual(false);
+      expect(info.IsCollectionPresent).toEqual(false);
+      expect(info.IsSignaturesPresent).toEqual(false);
+      expect(metadata).toEqual(null);
+      expect(contentDispositionFilename).toEqual(null);
+      expect(contentLength).toEqual(244351);
+      await loadingTask.destroy();
+    });
     it("gets markInfo", async function () {
     it("gets markInfo", async function () {
       const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("annotation-line.pdf"));
       const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("annotation-line.pdf"));
       const pdfDoc = await loadingTask.promise;
       const pdfDoc = await loadingTask.promise;
@@ -920,11 +1107,8 @@ describe("api", function () {
       });
       });
     });
     });
     it("gets document stats", async function () {
     it("gets document stats", async function () {
-      const stats = await pdfDocument.getStats();
-      expect(stats).toEqual({
-        streamTypes: {},
-        fontTypes: {}
-      });
+      const stats = pdfDocument.stats;
+      expect(stats).toEqual(null);
     });
     });
     it("cleans up document resources", async function () {
     it("cleans up document resources", async function () {
       await pdfDocument.cleanup();
       await pdfDocument.cleanup();
@@ -1191,10 +1375,15 @@ describe("api", function () {
       });
       });
       const data = await Promise.all([defaultPromise, parametersPromise]);
       const data = await Promise.all([defaultPromise, parametersPromise]);
       expect(!!data[0].items).toEqual(true);
       expect(!!data[0].items).toEqual(true);
-      expect(data[0].items.length).toEqual(12);
+      expect(data[0].items.length).toEqual(11);
       expect(!!data[0].styles).toEqual(true);
       expect(!!data[0].styles).toEqual(true);
+      const page1 = mergeText(data[0].items);
+      expect(page1).toEqual(`Table Of Content
+Chapter 1 .......................................................... 2
+Paragraph 1.1 ...................................................... 3
+page 1 / 3`);
       expect(!!data[1].items).toEqual(true);
       expect(!!data[1].items).toEqual(true);
-      expect(data[1].items.length).toEqual(7);
+      expect(data[1].items.length).toEqual(6);
       expect(!!data[1].styles).toEqual(true);
       expect(!!data[1].styles).toEqual(true);
     });
     });
     it("gets text content, with correct properties (issue 8276)", async function () {
     it("gets text content, with correct properties (issue 8276)", async function () {
@@ -1225,6 +1414,113 @@ describe("api", function () {
       });
       });
       await loadingTask.destroy();
       await loadingTask.destroy();
     });
     });
+    it("gets text content, with no extra spaces (issue 13226)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue13226.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text).toEqual("Mitarbeiterinnen und Mitarbeiter arbeiten in über 100 Ländern engagiert im Dienste");
+      await loadingTask.destroy();
+    });
+    it("gets text content, with merged spaces (issue 13201)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue13201.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text.includes("Abstract. A purely peer-to-peer version of electronic cash would allow online")).toEqual(true);
+      expect(text.includes("avoid mediating disputes. The cost of mediation increases transaction costs, limiting the")).toEqual(true);
+      expect(text.includes("system is secure as long as honest nodes collectively control more CPU power than any")).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("gets text content, with no spaces between letters of words (issue 11913)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue11913.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text.includes("1. The first of these cases arises from the tragic handicap which has blighted the life of the Plaintiff, and from the response of the")).toEqual(true);
+      expect(text.includes("argued in this Court the appeal raises narrower, but important, issues which may be summarised as follows:-")).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("gets text content, with merged spaces (issue 10900)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue10900.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text.includes(`3 3 3 3
+851.5 854.9 839.3 837.5
+633.6 727.8 789.9 796.2
+1,485.1 1,582.7 1,629.2 1,633.7
+114.2 121.7 125.3 130.7
+13.0x 13.0x 13.0x 12.5x`)).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("gets text content, with spaces (issue 10640)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue10640.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text.includes(`Open Sans is a humanist sans serif typeface designed by Steve Matteson.
+Open Sans was designed with an upright stress, open forms and a neu-
+tral, yet friendly appearance. It was optimized for print, web, and mobile
+interfaces, and has excellent legibility characteristics in its letterforms (see
+figure \x81 on the following page). This font is available from the Google Font
+Directory [\x81] as TrueType files licensed under the Apache License version \x82.\x80.
+This package provides support for this font in LATEX. It includes Type \x81
+versions of the fonts, converted for this package using FontForge from its
+sources, for full support with Dvips.`)).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("gets text content, with negative spaces (bug 931481)", async function () {
+      if (_is_node.isNodeJS) {
+        pending("Linked test-cases are not supported in Node.js.");
+      }
+
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug931481.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text.includes(`Kathrin Nachbaur
+Die promovierte Juristin ist 1979 in Graz geboren und aufgewachsen. Nach
+erfolgreichem Studienabschluss mit Fokus auf Europarecht absolvierte sie ein
+Praktikum bei Magna International in Kanada in der Human Resources Abteilung.
+Anschliessend wurde sie geschult in Human Resources, Arbeitsrecht und
+Kommunikation, währenddessen sie auch an ihrem Doktorat im Wirtschaftsrecht
+arbeitete. Seither arbeitete sie bei Magna International als Projekt Manager in der
+Innovationsabteilung. Seit 2009 ist sie Frank Stronachs Büroleiterin in Österreich und
+Kanada. Zusätzlich ist sie seit 2012 Vice President, Business Development der
+Stronach Group und Vizepräsidentin und Institutsleiterin des Stronach Institut für
+sozialökonomische Gerechtigkeit.`)).toEqual(true);
+      await loadingTask.destroy();
+    });
+    it("gets text content, with beginbfrange operator handled correctly (bug 1627427)", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("bug1627427_reduced.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const {
+        items
+      } = await pdfPage.getTextContent();
+      const text = mergeText(items);
+      expect(text).toEqual("침하게 흐린 품이 눈이 올 듯하더니 눈은 아니 오고 얼다가 만 비가 추");
+      await loadingTask.destroy();
+    });
     it("gets empty structure tree", async function () {
     it("gets empty structure tree", async function () {
       const tree = await page.getStructTree();
       const tree = await page.getStructTree();
       expect(tree).toEqual(null);
       expect(tree).toEqual(null);
@@ -1238,6 +1534,7 @@ describe("api", function () {
         role: "Root",
         role: "Root",
         children: [{
         children: [{
           role: "Document",
           role: "Document",
+          lang: "en-US",
           children: [{
           children: [{
             role: "H1",
             role: "H1",
             children: [{
             children: [{
@@ -1379,15 +1676,29 @@ describe("api", function () {
       expect(opListAnnotEnable.fnArray.length).toBeLessThan(opListAnnotEnableStorage.fnArray.length);
       expect(opListAnnotEnable.fnArray.length).toBeLessThan(opListAnnotEnableStorage.fnArray.length);
       await loadingTask.destroy();
       await loadingTask.destroy();
     });
     });
+    it("gets operatorList, with page resources containing corrupt /CCITTFaxDecode data", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("poppler-90-0-fuzzed.pdf"));
+      expect(loadingTask instanceof _api.PDFDocumentLoadingTask).toEqual(true);
+      const pdfDoc = await loadingTask.promise;
+      expect(pdfDoc.numPages).toEqual(16);
+      const pdfPage = await pdfDoc.getPage(6);
+      expect(pdfPage instanceof _api.PDFPageProxy).toEqual(true);
+      const opList = await pdfPage.getOperatorList();
+      expect(opList.fnArray.length).toBeGreaterThan(25);
+      expect(opList.argsArray.length).toBeGreaterThan(25);
+      expect(opList.lastChunk).toEqual(true);
+      await loadingTask.destroy();
+    });
     it("gets document stats after parsing page", async function () {
     it("gets document stats after parsing page", async function () {
-      const stats = await page.getOperatorList().then(function () {
-        return pdfDocument.getStats();
-      });
-      const expectedStreamTypes = {};
-      expectedStreamTypes[_util.StreamType.FLATE] = true;
-      const expectedFontTypes = {};
-      expectedFontTypes[_util.FontType.TYPE1STANDARD] = true;
-      expectedFontTypes[_util.FontType.CIDFONTTYPE2] = true;
+      await page.getOperatorList();
+      const stats = pdfDocument.stats;
+      const expectedStreamTypes = {
+        [_util.StreamType.FLATE]: true
+      };
+      const expectedFontTypes = {
+        [_util.FontType.TYPE1STANDARD]: true,
+        [_util.FontType.CIDFONTTYPE2]: true
+      };
       expect(stats).toEqual({
       expect(stats).toEqual({
         streamTypes: expectedStreamTypes,
         streamTypes: expectedStreamTypes,
         fontTypes: expectedFontTypes
         fontTypes: expectedFontTypes

+ 108 - 0
lib/test/unit/base_viewer_spec.js

@@ -0,0 +1,108 @@
+/**
+ * @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";
+
+var _base_viewer = require("../../web/base_viewer.js");
+
+describe("BaseViewer", function () {
+  describe("PDFPageViewBuffer", function () {
+    function createViewsMap(startId, endId) {
+      const map = new Map();
+
+      for (let id = startId; id <= endId; id++) {
+        map.set(id, {
+          id,
+          destroy: () => {}
+        });
+      }
+
+      return map;
+    }
+
+    it("handles `push` correctly", function () {
+      const buffer = new _base_viewer.PDFPageViewBuffer(3);
+      const viewsMap = createViewsMap(1, 5),
+            iterator = viewsMap.values();
+
+      for (let i = 0; i < 3; i++) {
+        const view = iterator.next().value;
+        buffer.push(view);
+      }
+
+      expect([...buffer]).toEqual([viewsMap.get(1), viewsMap.get(2), viewsMap.get(3)]);
+
+      for (let i = 3; i < 5; i++) {
+        const view = iterator.next().value;
+        buffer.push(view);
+      }
+
+      expect([...buffer]).toEqual([viewsMap.get(3), viewsMap.get(4), viewsMap.get(5)]);
+    });
+    it("handles `resize` correctly", function () {
+      const buffer = new _base_viewer.PDFPageViewBuffer(5);
+      const viewsMap = createViewsMap(1, 5),
+            iterator = viewsMap.values();
+
+      for (let i = 0; i < 5; i++) {
+        const view = iterator.next().value;
+        buffer.push(view);
+      }
+
+      buffer.resize(5);
+      expect([...buffer]).toEqual([viewsMap.get(1), viewsMap.get(2), viewsMap.get(3), viewsMap.get(4), viewsMap.get(5)]);
+      buffer.resize(10);
+      expect([...buffer]).toEqual([viewsMap.get(1), viewsMap.get(2), viewsMap.get(3), viewsMap.get(4), viewsMap.get(5)]);
+      buffer.resize(3);
+      expect([...buffer]).toEqual([viewsMap.get(3), viewsMap.get(4), viewsMap.get(5)]);
+    });
+    it("handles `resize` correctly, with `idsToKeep` provided", function () {
+      const buffer = new _base_viewer.PDFPageViewBuffer(5);
+      const viewsMap = createViewsMap(1, 5),
+            iterator = viewsMap.values();
+
+      for (let i = 0; i < 5; i++) {
+        const view = iterator.next().value;
+        buffer.push(view);
+      }
+
+      buffer.resize(5, new Set([1, 2]));
+      expect([...buffer]).toEqual([viewsMap.get(3), viewsMap.get(4), viewsMap.get(5), viewsMap.get(1), viewsMap.get(2)]);
+      buffer.resize(10, new Set([3, 4, 5]));
+      expect([...buffer]).toEqual([viewsMap.get(1), viewsMap.get(2), viewsMap.get(3), viewsMap.get(4), viewsMap.get(5)]);
+      buffer.resize(3, new Set([1, 2, 5]));
+      expect([...buffer]).toEqual([viewsMap.get(1), viewsMap.get(2), viewsMap.get(5)]);
+    });
+    it("handles `has` correctly", function () {
+      const buffer = new _base_viewer.PDFPageViewBuffer(3);
+      const viewsMap = createViewsMap(1, 2),
+            iterator = viewsMap.values();
+
+      for (let i = 0; i < 1; i++) {
+        const view = iterator.next().value;
+        buffer.push(view);
+      }
+
+      expect(buffer.has(viewsMap.get(1))).toEqual(true);
+      expect(buffer.has(viewsMap.get(2))).toEqual(false);
+    });
+  });
+});

+ 18 - 0
lib/test/unit/bidi_spec.js

@@ -24,6 +24,18 @@
 var _bidi = require("../../core/bidi.js");
 var _bidi = require("../../core/bidi.js");
 
 
 describe("bidi", function () {
 describe("bidi", function () {
+  it("should mark text as LTR if there's only LTR-characters, " + "when the string is very short", function () {
+    const str = "foo";
+    const bidiText = (0, _bidi.bidi)(str, -1, false);
+    expect(bidiText.str).toEqual("foo");
+    expect(bidiText.dir).toEqual("ltr");
+  });
+  it("should mark text as LTR if there's only LTR-characters", function () {
+    const str = "Lorem ipsum dolor sit amet, consectetur adipisicing elit.";
+    const bidiText = (0, _bidi.bidi)(str, -1, false);
+    expect(bidiText.str).toEqual("Lorem ipsum dolor sit amet, consectetur adipisicing elit.");
+    expect(bidiText.dir).toEqual("ltr");
+  });
   it("should mark text as RTL if more than 30% of text is RTL", function () {
   it("should mark text as RTL if more than 30% of text is RTL", function () {
     const test = "\u0645\u0635\u0631 Egypt";
     const test = "\u0645\u0635\u0631 Egypt";
     const result = "Egypt \u0631\u0635\u0645";
     const result = "Egypt \u0631\u0635\u0645";
@@ -38,4 +50,10 @@ describe("bidi", function () {
     expect(bidiText.str).toEqual(result);
     expect(bidiText.str).toEqual(result);
     expect(bidiText.dir).toEqual("ltr");
     expect(bidiText.dir).toEqual("ltr");
   });
   });
+  it("should mark text as RTL if less than 30% of text is RTL, " + "when the string is very short (issue 11656)", function () {
+    const str = "()\u05d1(";
+    const bidiText = (0, _bidi.bidi)(str, -1, false);
+    expect(bidiText.str).toEqual("(\u05d1)(");
+    expect(bidiText.dir).toEqual("rtl");
+  });
 });
 });

+ 259 - 0
lib/test/unit/event_utils_spec.js

@@ -0,0 +1,259 @@
+/**
+ * @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";
+
+var _event_utils = require("../../web/event_utils.js");
+
+var _is_node = require("../../shared/is_node.js");
+
+describe("event_utils", function () {
+  describe("EventBus", function () {
+    it("dispatch event", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function (evt) {
+        expect(evt).toEqual(undefined);
+        count++;
+      });
+      eventBus.dispatch("test");
+      expect(count).toEqual(1);
+    });
+    it("dispatch event with arguments", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function (evt) {
+        expect(evt).toEqual({
+          abc: 123
+        });
+        count++;
+      });
+      eventBus.dispatch("test", {
+        abc: 123
+      });
+      expect(count).toEqual(1);
+    });
+    it("dispatch different event", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function () {
+        count++;
+      });
+      eventBus.dispatch("nottest");
+      expect(count).toEqual(0);
+    });
+    it("dispatch event multiple times", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.dispatch("test");
+      eventBus.on("test", function () {
+        count++;
+      });
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      expect(count).toEqual(2);
+    });
+    it("dispatch event to multiple handlers", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function () {
+        count++;
+      });
+      eventBus.on("test", function () {
+        count++;
+      });
+      eventBus.dispatch("test");
+      expect(count).toEqual(2);
+    });
+    it("dispatch to detached", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+
+      const listener = function () {
+        count++;
+      };
+
+      eventBus.on("test", listener);
+      eventBus.dispatch("test");
+      eventBus.off("test", listener);
+      eventBus.dispatch("test");
+      expect(count).toEqual(1);
+    });
+    it("dispatch to wrong detached", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function () {
+        count++;
+      });
+      eventBus.dispatch("test");
+      eventBus.off("test", function () {
+        count++;
+      });
+      eventBus.dispatch("test");
+      expect(count).toEqual(2);
+    });
+    it("dispatch to detached during handling", function () {
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+
+      const listener1 = function () {
+        eventBus.off("test", listener2);
+        count++;
+      };
+
+      const listener2 = function () {
+        eventBus.off("test", listener1);
+        count++;
+      };
+
+      eventBus.on("test", listener1);
+      eventBus.on("test", listener2);
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      expect(count).toEqual(2);
+    });
+    it("dispatch event to handlers with/without 'once' option", function () {
+      const eventBus = new _event_utils.EventBus();
+      let multipleCount = 0,
+          onceCount = 0;
+      eventBus.on("test", function () {
+        multipleCount++;
+      });
+      eventBus.on("test", function () {
+        onceCount++;
+      }, {
+        once: true
+      });
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      eventBus.dispatch("test");
+      expect(multipleCount).toEqual(3);
+      expect(onceCount).toEqual(1);
+    });
+    it("should not re-dispatch to DOM", async function () {
+      if (_is_node.isNodeJS) {
+        pending("Document is not supported in Node.js.");
+      }
+
+      const eventBus = new _event_utils.EventBus();
+      let count = 0;
+      eventBus.on("test", function (evt) {
+        expect(evt).toEqual(undefined);
+        count++;
+      });
+
+      function domEventListener() {
+        expect(false).toEqual(true);
+      }
+
+      document.addEventListener("test", domEventListener);
+      eventBus.dispatch("test");
+      await Promise.resolve();
+      expect(count).toEqual(1);
+      document.removeEventListener("test", domEventListener);
+    });
+  });
+  describe("waitOnEventOrTimeout", function () {
+    let eventBus;
+    beforeAll(function () {
+      eventBus = new _event_utils.EventBus();
+    });
+    afterAll(function () {
+      eventBus = null;
+    });
+    it("should reject invalid parameters", async function () {
+      const invalidTarget = (0, _event_utils.waitOnEventOrTimeout)({
+        target: "window",
+        name: "DOMContentLoaded"
+      }).then(function () {
+        expect(false).toEqual(true);
+      }, function (reason) {
+        expect(reason instanceof Error).toEqual(true);
+      });
+      const invalidName = (0, _event_utils.waitOnEventOrTimeout)({
+        target: eventBus,
+        name: ""
+      }).then(function () {
+        expect(false).toEqual(true);
+      }, function (reason) {
+        expect(reason instanceof Error).toEqual(true);
+      });
+      const invalidDelay = (0, _event_utils.waitOnEventOrTimeout)({
+        target: eventBus,
+        name: "pagerendered",
+        delay: -1000
+      }).then(function () {
+        expect(false).toEqual(true);
+      }, function (reason) {
+        expect(reason instanceof Error).toEqual(true);
+      });
+      await Promise.all([invalidTarget, invalidName, invalidDelay]);
+    });
+    it("should resolve on event, using the DOM", async function () {
+      if (_is_node.isNodeJS) {
+        pending("Document is not supported in Node.js.");
+      }
+
+      const button = document.createElement("button");
+      const buttonClicked = (0, _event_utils.waitOnEventOrTimeout)({
+        target: button,
+        name: "click",
+        delay: 10000
+      });
+      button.click();
+      const type = await buttonClicked;
+      expect(type).toEqual(_event_utils.WaitOnType.EVENT);
+    });
+    it("should resolve on timeout, using the DOM", async function () {
+      if (_is_node.isNodeJS) {
+        pending("Document is not supported in Node.js.");
+      }
+
+      const button = document.createElement("button");
+      const buttonClicked = (0, _event_utils.waitOnEventOrTimeout)({
+        target: button,
+        name: "click",
+        delay: 10
+      });
+      const type = await buttonClicked;
+      expect(type).toEqual(_event_utils.WaitOnType.TIMEOUT);
+    });
+    it("should resolve on event, using the EventBus", async function () {
+      const pageRendered = (0, _event_utils.waitOnEventOrTimeout)({
+        target: eventBus,
+        name: "pagerendered",
+        delay: 10000
+      });
+      eventBus.dispatch("pagerendered");
+      const type = await pageRendered;
+      expect(type).toEqual(_event_utils.WaitOnType.EVENT);
+    });
+    it("should resolve on timeout, using the EventBus", async function () {
+      const pageRendered = (0, _event_utils.waitOnEventOrTimeout)({
+        target: eventBus,
+        name: "pagerendered",
+        delay: 10
+      });
+      const type = await pageRendered;
+      expect(type).toEqual(_event_utils.WaitOnType.TIMEOUT);
+    });
+  });
+});

+ 1 - 1
lib/test/unit/jasmine-boot.js

@@ -36,7 +36,7 @@ var _api = require("pdfjs/display/api.js");
 var _testreporter = require("./testreporter.js");
 var _testreporter = require("./testreporter.js");
 
 
 async function initializePDFJS(callback) {
 async function initializePDFJS(callback) {
-  await Promise.all(["pdfjs-test/unit/annotation_spec.js", "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", "pdfjs-test/unit/core_utils_spec.js", "pdfjs-test/unit/crypto_spec.js", "pdfjs-test/unit/custom_spec.js", "pdfjs-test/unit/default_appearance_spec.js", "pdfjs-test/unit/display_svg_spec.js", "pdfjs-test/unit/display_utils_spec.js", "pdfjs-test/unit/document_spec.js", "pdfjs-test/unit/encodings_spec.js", "pdfjs-test/unit/evaluator_spec.js", "pdfjs-test/unit/function_spec.js", "pdfjs-test/unit/fetch_stream_spec.js", "pdfjs-test/unit/message_handler_spec.js", "pdfjs-test/unit/metadata_spec.js", "pdfjs-test/unit/murmurhash3_spec.js", "pdfjs-test/unit/network_spec.js", "pdfjs-test/unit/network_utils_spec.js", "pdfjs-test/unit/parser_spec.js", "pdfjs-test/unit/pdf_find_controller_spec.js", "pdfjs-test/unit/pdf_find_utils_spec.js", "pdfjs-test/unit/pdf_history_spec.js", "pdfjs-test/unit/primitives_spec.js", "pdfjs-test/unit/scripting_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/struct_tree_spec.js", "pdfjs-test/unit/type1_parser_spec.js", "pdfjs-test/unit/ui_utils_spec.js", "pdfjs-test/unit/unicode_spec.js", "pdfjs-test/unit/util_spec.js", "pdfjs-test/unit/writer_spec.js", "pdfjs-test/unit/xfa_formcalc_spec.js", "pdfjs-test/unit/xfa_parser_spec.js", "pdfjs-test/unit/xfa_serialize_data_spec.js", "pdfjs-test/unit/xfa_tohtml_spec.js", "pdfjs-test/unit/xml_spec.js"].map(function (moduleName) {
+  await Promise.all(["pdfjs-test/unit/annotation_spec.js", "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/base_viewer_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", "pdfjs-test/unit/core_utils_spec.js", "pdfjs-test/unit/crypto_spec.js", "pdfjs-test/unit/custom_spec.js", "pdfjs-test/unit/default_appearance_spec.js", "pdfjs-test/unit/display_svg_spec.js", "pdfjs-test/unit/display_utils_spec.js", "pdfjs-test/unit/document_spec.js", "pdfjs-test/unit/encodings_spec.js", "pdfjs-test/unit/evaluator_spec.js", "pdfjs-test/unit/event_utils_spec.js", "pdfjs-test/unit/function_spec.js", "pdfjs-test/unit/fetch_stream_spec.js", "pdfjs-test/unit/message_handler_spec.js", "pdfjs-test/unit/metadata_spec.js", "pdfjs-test/unit/murmurhash3_spec.js", "pdfjs-test/unit/network_spec.js", "pdfjs-test/unit/network_utils_spec.js", "pdfjs-test/unit/parser_spec.js", "pdfjs-test/unit/pdf_find_controller_spec.js", "pdfjs-test/unit/pdf_find_utils_spec.js", "pdfjs-test/unit/pdf_history_spec.js", "pdfjs-test/unit/primitives_spec.js", "pdfjs-test/unit/scripting_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/struct_tree_spec.js", "pdfjs-test/unit/type1_parser_spec.js", "pdfjs-test/unit/ui_utils_spec.js", "pdfjs-test/unit/unicode_spec.js", "pdfjs-test/unit/util_spec.js", "pdfjs-test/unit/writer_spec.js", "pdfjs-test/unit/xfa_formcalc_spec.js", "pdfjs-test/unit/xfa_parser_spec.js", "pdfjs-test/unit/xfa_serialize_data_spec.js", "pdfjs-test/unit/xfa_tohtml_spec.js", "pdfjs-test/unit/xml_spec.js"].map(function (moduleName) {
     return import(moduleName);
     return import(moduleName);
   }));
   }));
 
 

+ 25 - 36
lib/test/unit/pdf_find_controller_spec.js

@@ -23,7 +23,7 @@
 
 
 var _test_utils = require("./test_utils.js");
 var _test_utils = require("./test_utils.js");
 
 
-var _ui_utils = require("../../web/ui_utils.js");
+var _event_utils = require("../../web/event_utils.js");
 
 
 var _api = require("../../display/api.js");
 var _api = require("../../display/api.js");
 
 
@@ -61,7 +61,7 @@ class MockLinkService extends _pdf_link_service.SimpleLinkService {
 async function initPdfFindController(filename) {
 async function initPdfFindController(filename) {
   const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename || tracemonkeyFileName));
   const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)(filename || tracemonkeyFileName));
   const pdfDocument = await loadingTask.promise;
   const pdfDocument = await loadingTask.promise;
-  const eventBus = new _ui_utils.EventBus();
+  const eventBus = new _event_utils.EventBus();
   const linkService = new MockLinkService();
   const linkService = new MockLinkService();
   linkService.setDocument(pdfDocument);
   linkService.setDocument(pdfDocument);
   const pdfFindController = new _pdf_find_controller.PDFFindController({
   const pdfFindController = new _pdf_find_controller.PDFFindController({
@@ -78,14 +78,23 @@ async function initPdfFindController(filename) {
 function testSearch({
 function testSearch({
   eventBus,
   eventBus,
   pdfFindController,
   pdfFindController,
-  parameters,
+  state,
   matchesPerPage,
   matchesPerPage,
   selectedMatch,
   selectedMatch,
   pageMatches = null,
   pageMatches = null,
   pageMatchesLength = null
   pageMatchesLength = null
 }) {
 }) {
   return new Promise(function (resolve) {
   return new Promise(function (resolve) {
-    pdfFindController.executeCommand("find", parameters);
+    const eventState = Object.assign(Object.create(null), {
+      source: this,
+      type: "",
+      query: null,
+      caseSensitive: false,
+      entireWord: false,
+      phraseSearch: true,
+      findPrevious: false
+    }, state);
+    eventBus.dispatch("find", eventState);
     let totalPages = matchesPerPage.length;
     let totalPages = matchesPerPage.length;
 
 
     for (let i = totalPages - 1; i >= 0; i--) {
     for (let i = totalPages - 1; i >= 0; i--) {
@@ -132,12 +141,8 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
-        query: "Dynamic",
-        caseSensitive: false,
-        entireWord: false,
-        phraseSearch: true,
-        findPrevious: false
+      state: {
+        query: "Dynamic"
       },
       },
       matchesPerPage: [11, 5, 0, 3, 0, 0, 0, 1, 1, 1, 0, 3, 4, 4],
       matchesPerPage: [11, 5, 0, 3, 0, 0, 0, 1, 1, 1, 0, 3, 4, 4],
       selectedMatch: {
       selectedMatch: {
@@ -154,11 +159,8 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
+      state: {
         query: "conference",
         query: "conference",
-        caseSensitive: false,
-        entireWord: false,
-        phraseSearch: true,
         findPrevious: true
         findPrevious: true
       },
       },
       matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
       matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5],
@@ -176,12 +178,9 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
+      state: {
         query: "Dynamic",
         query: "Dynamic",
-        caseSensitive: true,
-        entireWord: false,
-        phraseSearch: true,
-        findPrevious: false
+        caseSensitive: true
       },
       },
       matchesPerPage: [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
       matchesPerPage: [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3],
       selectedMatch: {
       selectedMatch: {
@@ -198,12 +197,9 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
+      state: {
         query: "Government",
         query: "Government",
-        caseSensitive: false,
-        entireWord: true,
-        phraseSearch: true,
-        findPrevious: false
+        entireWord: true
       },
       },
       matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       matchesPerPage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       selectedMatch: {
       selectedMatch: {
@@ -220,12 +216,9 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
+      state: {
         query: "alternate solution",
         query: "alternate solution",
-        caseSensitive: false,
-        entireWord: false,
-        phraseSearch: false,
-        findPrevious: false
+        phraseSearch: false
       },
       },
       matchesPerPage: [0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0, 0],
       matchesPerPage: [0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0, 0],
       selectedMatch: {
       selectedMatch: {
@@ -242,19 +235,15 @@ describe("pdf_find_controller", function () {
     await testSearch({
     await testSearch({
       eventBus,
       eventBus,
       pdfFindController,
       pdfFindController,
-      parameters: {
-        query: "fraction",
-        caseSensitive: false,
-        entireWord: false,
-        phraseSearch: true,
-        findPrevious: false
+      state: {
+        query: "fraction"
       },
       },
       matchesPerPage: [3],
       matchesPerPage: [3],
       selectedMatch: {
       selectedMatch: {
         pageIndex: 0,
         pageIndex: 0,
         matchIndex: 0
         matchIndex: 0
       },
       },
-      pageMatches: [[19, 48, 66]],
+      pageMatches: [[19, 46, 62]],
       pageMatchesLength: [[8, 8, 8]]
       pageMatchesLength: [[8, 8, 8]]
     });
     });
   });
   });

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

@@ -91,7 +91,7 @@ describe("primitives", function () {
     const checkInvalidKeyValues = function (dict) {
     const checkInvalidKeyValues = function (dict) {
       expect(dict.get()).toBeUndefined();
       expect(dict.get()).toBeUndefined();
       expect(dict.get("Prev")).toBeUndefined();
       expect(dict.get("Prev")).toBeUndefined();
-      expect(dict.get("Decode", "D")).toBeUndefined();
+      expect(dict.get("D", "Decode")).toBeUndefined();
       expect(dict.get("FontFile", "FontFile2", "FontFile3")).toBeUndefined();
       expect(dict.get("FontFile", "FontFile2", "FontFile3")).toBeUndefined();
     };
     };
 
 

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

@@ -28,6 +28,7 @@ var _api = require("../../display/api.js");
 function equalTrees(rootA, rootB) {
 function equalTrees(rootA, rootB) {
   function walk(a, b) {
   function walk(a, b) {
     expect(a.role).toEqual(b.role);
     expect(a.role).toEqual(b.role);
+    expect(a.lang).toEqual(b.lang);
     expect(a.type).toEqual(b.type);
     expect(a.type).toEqual(b.type);
     expect("children" in a).toEqual("children" in b);
     expect("children" in a).toEqual("children" in b);
 
 
@@ -58,6 +59,7 @@ describe("struct tree", function () {
         role: "Root",
         role: "Root",
         children: [{
         children: [{
           role: "Document",
           role: "Document",
+          lang: "en-US",
           children: [{
           children: [{
             role: "H1",
             role: "H1",
             children: [{
             children: [{

+ 6 - 5
lib/test/unit/test_utils.js

@@ -24,10 +24,10 @@
 Object.defineProperty(exports, "__esModule", {
 Object.defineProperty(exports, "__esModule", {
   value: true
   value: true
 });
 });
+exports.XRefMock = exports.TEST_PDFS_PATH = exports.STANDARD_FONT_DATA_URL = exports.DefaultFileReaderFactory = exports.CMAP_PARAMS = void 0;
 exports.buildGetDocumentParams = buildGetDocumentParams;
 exports.buildGetDocumentParams = buildGetDocumentParams;
 exports.createIdFactory = createIdFactory;
 exports.createIdFactory = createIdFactory;
 exports.isEmptyObj = isEmptyObj;
 exports.isEmptyObj = isEmptyObj;
-exports.XRefMock = exports.TEST_PDFS_PATH = exports.STANDARD_FONT_DATA_URL = exports.DefaultFileReaderFactory = exports.CMAP_PARAMS = void 0;
 
 
 var _primitives = require("../../core/primitives.js");
 var _primitives = require("../../core/primitives.js");
 
 
@@ -35,6 +35,8 @@ var _document = require("../../core/document.js");
 
 
 var _util = require("../../shared/util.js");
 var _util = require("../../shared/util.js");
 
 
+var _core_utils = require("../../core/core_utils.js");
+
 var _is_node = require("../../shared/is_node.js");
 var _is_node = require("../../shared/is_node.js");
 
 
 var _stream = require("../../core/stream.js");
 var _stream = require("../../core/stream.js");
@@ -98,10 +100,9 @@ function buildGetDocumentParams(filename, options) {
 class XRefMock {
 class XRefMock {
   constructor(array) {
   constructor(array) {
     this._map = Object.create(null);
     this._map = Object.create(null);
-    this.stats = {
-      streamTypes: Object.create(null),
-      fontTypes: Object.create(null)
-    };
+    this.stats = new _core_utils.DocStats({
+      send: () => {}
+    });
     this._newRefNum = null;
     this._newRefNum = null;
 
 
     for (const key in array) {
     for (const key in array) {

+ 9 - 271
lib/test/unit/ui_utils_spec.js

@@ -23,8 +23,6 @@
 
 
 var _ui_utils = require("../../web/ui_utils.js");
 var _ui_utils = require("../../web/ui_utils.js");
 
 
-var _is_node = require("../../shared/is_node.js");
-
 describe("ui_utils", function () {
 describe("ui_utils", function () {
   describe("binary search", function () {
   describe("binary search", function () {
     function isTrue(boolean) {
     function isTrue(boolean) {
@@ -54,151 +52,6 @@ describe("ui_utils", function () {
       expect((0, _ui_utils.binarySearchFirstItem)([4, 5, 6], isGreater3)).toEqual(0);
       expect((0, _ui_utils.binarySearchFirstItem)([4, 5, 6], isGreater3)).toEqual(0);
     });
     });
   });
   });
-  describe("EventBus", function () {
-    it("dispatch event", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function (evt) {
-        expect(evt).toEqual(undefined);
-        count++;
-      });
-      eventBus.dispatch("test");
-      expect(count).toEqual(1);
-    });
-    it("dispatch event with arguments", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function (evt) {
-        expect(evt).toEqual({
-          abc: 123
-        });
-        count++;
-      });
-      eventBus.dispatch("test", {
-        abc: 123
-      });
-      expect(count).toEqual(1);
-    });
-    it("dispatch different event", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function () {
-        count++;
-      });
-      eventBus.dispatch("nottest");
-      expect(count).toEqual(0);
-    });
-    it("dispatch event multiple times", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.dispatch("test");
-      eventBus.on("test", function () {
-        count++;
-      });
-      eventBus.dispatch("test");
-      eventBus.dispatch("test");
-      expect(count).toEqual(2);
-    });
-    it("dispatch event to multiple handlers", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function () {
-        count++;
-      });
-      eventBus.on("test", function () {
-        count++;
-      });
-      eventBus.dispatch("test");
-      expect(count).toEqual(2);
-    });
-    it("dispatch to detached", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-
-      const listener = function () {
-        count++;
-      };
-
-      eventBus.on("test", listener);
-      eventBus.dispatch("test");
-      eventBus.off("test", listener);
-      eventBus.dispatch("test");
-      expect(count).toEqual(1);
-    });
-    it("dispatch to wrong detached", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function () {
-        count++;
-      });
-      eventBus.dispatch("test");
-      eventBus.off("test", function () {
-        count++;
-      });
-      eventBus.dispatch("test");
-      expect(count).toEqual(2);
-    });
-    it("dispatch to detached during handling", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-
-      const listener1 = function () {
-        eventBus.off("test", listener2);
-        count++;
-      };
-
-      const listener2 = function () {
-        eventBus.off("test", listener1);
-        count++;
-      };
-
-      eventBus.on("test", listener1);
-      eventBus.on("test", listener2);
-      eventBus.dispatch("test");
-      eventBus.dispatch("test");
-      expect(count).toEqual(2);
-    });
-    it("dispatch event to handlers with/without 'once' option", function () {
-      const eventBus = new _ui_utils.EventBus();
-      let multipleCount = 0,
-          onceCount = 0;
-      eventBus.on("test", function () {
-        multipleCount++;
-      });
-      eventBus.on("test", function () {
-        onceCount++;
-      }, {
-        once: true
-      });
-      eventBus.dispatch("test");
-      eventBus.dispatch("test");
-      eventBus.dispatch("test");
-      expect(multipleCount).toEqual(3);
-      expect(onceCount).toEqual(1);
-    });
-    it("should not re-dispatch to DOM", async function () {
-      if (_is_node.isNodeJS) {
-        pending("Document is not supported in Node.js.");
-      }
-
-      const eventBus = new _ui_utils.EventBus();
-      let count = 0;
-      eventBus.on("test", function (evt) {
-        expect(evt).toEqual(undefined);
-        count++;
-      });
-
-      function domEventListener() {
-        expect(false).toEqual(true);
-      }
-
-      document.addEventListener("test", domEventListener);
-      eventBus.dispatch("test");
-      await Promise.resolve();
-      expect(count).toEqual(1);
-      document.removeEventListener("test", domEventListener);
-    });
-  });
   describe("isValidRotation", function () {
   describe("isValidRotation", function () {
     it("should reject non-integer angles", function () {
     it("should reject non-integer angles", function () {
       expect((0, _ui_utils.isValidRotation)()).toEqual(false);
       expect((0, _ui_utils.isValidRotation)()).toEqual(false);
@@ -267,91 +120,6 @@ describe("ui_utils", function () {
       expect(parameters.get("key2")).toEqual("Value2");
       expect(parameters.get("key2")).toEqual("Value2");
     });
     });
   });
   });
-  describe("waitOnEventOrTimeout", function () {
-    let eventBus;
-    beforeAll(function () {
-      eventBus = new _ui_utils.EventBus();
-    });
-    afterAll(function () {
-      eventBus = null;
-    });
-    it("should reject invalid parameters", async function () {
-      const invalidTarget = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: "window",
-        name: "DOMContentLoaded"
-      }).then(function () {
-        expect(false).toEqual(true);
-      }, function (reason) {
-        expect(reason instanceof Error).toEqual(true);
-      });
-      const invalidName = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: eventBus,
-        name: ""
-      }).then(function () {
-        expect(false).toEqual(true);
-      }, function (reason) {
-        expect(reason instanceof Error).toEqual(true);
-      });
-      const invalidDelay = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: eventBus,
-        name: "pagerendered",
-        delay: -1000
-      }).then(function () {
-        expect(false).toEqual(true);
-      }, function (reason) {
-        expect(reason instanceof Error).toEqual(true);
-      });
-      await Promise.all([invalidTarget, invalidName, invalidDelay]);
-    });
-    it("should resolve on event, using the DOM", async function () {
-      if (_is_node.isNodeJS) {
-        pending("Document is not supported in Node.js.");
-      }
-
-      const button = document.createElement("button");
-      const buttonClicked = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: button,
-        name: "click",
-        delay: 10000
-      });
-      button.click();
-      const type = await buttonClicked;
-      expect(type).toEqual(_ui_utils.WaitOnType.EVENT);
-    });
-    it("should resolve on timeout, using the DOM", async function () {
-      if (_is_node.isNodeJS) {
-        pending("Document is not supported in Node.js.");
-      }
-
-      const button = document.createElement("button");
-      const buttonClicked = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: button,
-        name: "click",
-        delay: 10
-      });
-      const type = await buttonClicked;
-      expect(type).toEqual(_ui_utils.WaitOnType.TIMEOUT);
-    });
-    it("should resolve on event, using the EventBus", async function () {
-      const pageRendered = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: eventBus,
-        name: "pagerendered",
-        delay: 10000
-      });
-      eventBus.dispatch("pagerendered");
-      const type = await pageRendered;
-      expect(type).toEqual(_ui_utils.WaitOnType.EVENT);
-    });
-    it("should resolve on timeout, using the EventBus", async function () {
-      const pageRendered = (0, _ui_utils.waitOnEventOrTimeout)({
-        target: eventBus,
-        name: "pagerendered",
-        delay: 10
-      });
-      const type = await pageRendered;
-      expect(type).toEqual(_ui_utils.WaitOnType.TIMEOUT);
-    });
-  });
   describe("getPageSizeInches", function () {
   describe("getPageSizeInches", function () {
     it("gets page size (in inches)", function () {
     it("gets page size (in inches)", function () {
       const page = {
       const page = {
@@ -431,7 +199,8 @@ describe("ui_utils", function () {
     }
     }
 
 
     function slowGetVisibleElements(scroll, pages) {
     function slowGetVisibleElements(scroll, pages) {
-      const views = [];
+      const views = [],
+            ids = new Set();
       const {
       const {
         scrollLeft,
         scrollLeft,
         scrollTop
         scrollTop
@@ -462,13 +231,15 @@ describe("ui_utils", function () {
             percent,
             percent,
             widthPercent: fractionWidth * 100 | 0
             widthPercent: fractionWidth * 100 | 0
           });
           });
+          ids.add(view.id);
         }
         }
       }
       }
 
 
       return {
       return {
         first: views[0],
         first: views[0],
         last: views[views.length - 1],
         last: views[views.length - 1],
-        views
+        views,
+        ids
       };
       };
     }
     }
 
 
@@ -564,7 +335,8 @@ describe("ui_utils", function () {
       })).toEqual({
       })).toEqual({
         first: undefined,
         first: undefined,
         last: undefined,
         last: undefined,
-        views: []
+        views: [],
+        ids: new Set()
       });
       });
     });
     });
     it("handles all views being hidden (without errors)", function () {
     it("handles all views being hidden (without errors)", function () {
@@ -581,7 +353,8 @@ describe("ui_utils", function () {
       })).toEqual({
       })).toEqual({
         first: undefined,
         first: undefined,
         last: undefined,
         last: undefined,
-        views: []
+        views: [],
+        ids: new Set()
       });
       });
     });
     });
     describe("backtrackBeforeAllVisibleElements", function () {
     describe("backtrackBeforeAllVisibleElements", function () {
@@ -611,39 +384,4 @@ describe("ui_utils", function () {
       });
       });
     });
     });
   });
   });
-  describe("moveToEndOfArray", function () {
-    it("works on empty arrays", function () {
-      const data = [];
-      (0, _ui_utils.moveToEndOfArray)(data, function () {});
-      expect(data).toEqual([]);
-    });
-    it("works when moving everything", function () {
-      const data = [1, 2, 3, 4, 5];
-      (0, _ui_utils.moveToEndOfArray)(data, function () {
-        return true;
-      });
-      expect(data).toEqual([1, 2, 3, 4, 5]);
-    });
-    it("works when moving some things", function () {
-      const data = [1, 2, 3, 4, 5];
-      (0, _ui_utils.moveToEndOfArray)(data, function (x) {
-        return x % 2 === 0;
-      });
-      expect(data).toEqual([1, 3, 5, 2, 4]);
-    });
-    it("works when moving one thing", function () {
-      const data = [1, 2, 3, 4, 5];
-      (0, _ui_utils.moveToEndOfArray)(data, function (x) {
-        return x === 1;
-      });
-      expect(data).toEqual([2, 3, 4, 5, 1]);
-    });
-    it("works when moving nothing", function () {
-      const data = [1, 2, 3, 4, 5];
-      (0, _ui_utils.moveToEndOfArray)(data, function (x) {
-        return x === 0;
-      });
-      expect(data).toEqual([1, 2, 3, 4, 5]);
-    });
-  });
 });
 });

+ 5 - 0
lib/test/unit/util_spec.js

@@ -148,6 +148,11 @@ describe("util", function () {
       const str = "string\x00With\x00Null\x00Chars";
       const str = "string\x00With\x00Null\x00Chars";
       expect((0, _util.removeNullCharacters)(str)).toEqual("stringWithNullChars");
       expect((0, _util.removeNullCharacters)(str)).toEqual("stringWithNullChars");
     });
     });
+    it("should modify string with non-displayable characters", function () {
+      const str = Array.from(Array(32).keys()).map(x => String.fromCharCode(x) + "a").join("");
+      const expected = "a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a";
+      expect((0, _util.removeNullCharacters)(str, true)).toEqual(expected);
+    });
   });
   });
   describe("ReadableStream", function () {
   describe("ReadableStream", function () {
     it("should return an Object", function () {
     it("should return an Object", function () {

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

@@ -51,7 +51,7 @@ describe("XFAParser", function () {
           forbidden
           forbidden
         </dynamicRender>
         </dynamicRender>
       </acrobat7>
       </acrobat7>
-      <autoSave>enabled</autoSave>      
+      <autoSave>enabled</autoSave>
       <submitUrl>
       <submitUrl>
                  http://d.e.f
                  http://d.e.f
       </submitUrl>
       </submitUrl>
@@ -435,7 +435,7 @@ describe("XFAParser", function () {
       const p = root.template.extras.text.$content[_xfa_object.$getChildren]()[0];
       const p = root.template.extras.text.$content[_xfa_object.$getChildren]()[0];
 
 
       expect(p.style).toEqual("text-indent:0.5in;line-height:11px;tab-stop:left 0.5in");
       expect(p.style).toEqual("text-indent:0.5in;line-height:11px;tab-stop:left 0.5in");
-      expect(p[_xfa_object.$text]()).toEqual([" The first line of this paragraph is indented a half-inch.\n", " Successive lines are not indented.\n", " This is the last line of the paragraph.\n \n"].join(""));
+      expect(p[_xfa_object.$text]()).toEqual([" The first line of this paragraph is indented a half-inch.\n", " Successive lines are not indented.\n", " This is the last line of the paragraph.\n "].join(""));
     });
     });
     it("should parse a xfa document and apply some prototypes with cycle", function () {
     it("should parse a xfa document and apply some prototypes with cycle", function () {
       const xml = `
       const xml = `

+ 3 - 2
lib/test/unit/xfa_serialize_data_spec.js

@@ -44,6 +44,7 @@ describe("Data serializer", function () {
           <Units>1</Units>
           <Units>1</Units>
           <Unit_Price>250.00</Unit_Price>
           <Unit_Price>250.00</Unit_Price>
           <Total_Price>250.00</Total_Price>
           <Total_Price>250.00</Total_Price>
+          <àé></àé>
         </Detail>
         </Detail>
         <Page>2</Page>
         <Page>2</Page>
         <Detail PartNo="RRB-LB">
         <Detail PartNo="RRB-LB">
@@ -66,14 +67,14 @@ describe("Data serializer", function () {
     const dataHandler = new _data.DataHandler(root, data);
     const dataHandler = new _data.DataHandler(root, data);
     const storage = new Map();
     const storage = new Map();
 
 
-    for (const [path, value] of [["Receipt.Detail[0].Units", "12&3"], ["Receipt.Detail[0].Unit_Price", "456>"], ["Receipt.Detail[0].Total_Price", "789"], ["Receipt.Detail[1].PartNo", "foo-bar😀"], ["Receipt.Detail[1].Description", "hello world"]]) {
+    for (const [path, value] of [["Receipt.Detail[0].Units", "12&3"], ["Receipt.Detail[0].Unit_Price", "456>"], ["Receipt.Detail[0].Total_Price", "789"], ["Receipt.Detail[0].àé", "1011"], ["Receipt.Detail[1].PartNo", "foo-bar😀"], ["Receipt.Detail[1].Description", "hello world"]]) {
       storage.set((0, _som.searchNode)(root, data, path)[0][_xfa_object.$uid], {
       storage.set((0, _som.searchNode)(root, data, path)[0][_xfa_object.$uid], {
         value
         value
       });
       });
     }
     }
 
 
     const serialized = dataHandler.serialize(storage);
     const serialized = dataHandler.serialize(storage);
-    const expected = `<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"><foo>bar</foo><bar>foo</bar><xfa:data><Receipt><Page>1</Page><Detail PartNo="GS001"><Description>Giant Slingshot</Description><Units>12&amp;3</Units><Unit_Price>456&gt;</Unit_Price><Total_Price>789</Total_Price></Detail><Page>2</Page><Detail PartNo="foo-bar&#x1F600;"><Description>hello world</Description><Units>5</Units><Unit_Price>12.00</Unit_Price><Total_Price>60.00</Total_Price></Detail><Sub_Total>310.00</Sub_Total><Tax>24.80</Tax><Total_Price>334.80</Total_Price></Receipt></xfa:data></xfa:datasets>`;
+    const expected = `<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/"><foo>bar</foo><bar>foo</bar><xfa:data><Receipt><Page>1</Page><Detail PartNo="GS001"><Description>Giant Slingshot</Description><Units>12&amp;3</Units><Unit_Price>456&gt;</Unit_Price><Total_Price>789</Total_Price><\xC3\xA0\xC3\xA9>1011</\xC3\xA0\xC3\xA9></Detail><Page>2</Page><Detail PartNo="foo-bar&#x1F600;"><Description>hello world</Description><Units>5</Units><Unit_Price>12.00</Unit_Price><Total_Price>60.00</Total_Price></Detail><Sub_Total>310.00</Sub_Total><Tax>24.80</Tax><Total_Price>334.80</Total_Price></Receipt></xfa:data></xfa:datasets>`;
     expect(serialized).toEqual(expected);
     expect(serialized).toEqual(expected);
   });
   });
 });
 });

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно