Pārlūkot izejas kodu

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

pdfjsbot 3 gadi atpakaļ
vecāks
revīzija
fdd59815fb
100 mainītis faili ar 8073 papildinājumiem un 3007 dzēšanām
  1. 1 1
      bower.json
  2. 383 275
      build/pdf.js
  3. 0 0
      build/pdf.js.map
  4. 0 0
      build/pdf.min.js
  5. 2 2
      build/pdf.sandbox.js
  6. 0 0
      build/pdf.sandbox.js.map
  7. 0 0
      build/pdf.sandbox.min.js
  8. 383 138
      build/pdf.worker.js
  9. 0 0
      build/pdf.worker.js.map
  10. 0 0
      build/pdf.worker.min.js
  11. 15 25
      image_decoders/pdf.image_decoders.js
  12. 0 0
      image_decoders/pdf.image_decoders.js.map
  13. 0 0
      image_decoders/pdf.image_decoders.min.js
  14. 687 322
      legacy/build/pdf.js
  15. 0 0
      legacy/build/pdf.js.map
  16. 0 0
      legacy/build/pdf.min.js
  17. 3 3
      legacy/build/pdf.sandbox.js
  18. 0 0
      legacy/build/pdf.sandbox.js.map
  19. 0 0
      legacy/build/pdf.sandbox.min.js
  20. 607 119
      legacy/build/pdf.worker.js
  21. 0 0
      legacy/build/pdf.worker.js.map
  22. 0 0
      legacy/build/pdf.worker.min.js
  23. 562 91
      legacy/image_decoders/pdf.image_decoders.js
  24. 0 0
      legacy/image_decoders/pdf.image_decoders.js.map
  25. 0 0
      legacy/image_decoders/pdf.image_decoders.min.js
  26. 115 105
      legacy/web/pdf_viewer.css
  27. 1 0
      legacy/web/pdf_viewer.d.ts
  28. 211 108
      legacy/web/pdf_viewer.js
  29. 0 0
      legacy/web/pdf_viewer.js.map
  30. 1 1
      lib/README.md
  31. 6 2
      lib/core/annotation.js
  32. 1 1
      lib/core/ascii_85_stream.js
  33. 1 1
      lib/core/ascii_hex_stream.js
  34. 27 0
      lib/core/calibri_factors.js
  35. 22 0
      lib/core/catalog.js
  36. 6 1
      lib/core/cff_font.js
  37. 11 2
      lib/core/cff_parser.js
  38. 1 1
      lib/core/cmap.js
  39. 5 1
      lib/core/core_utils.js
  40. 3 3
      lib/core/crypto.js
  41. 163 24
      lib/core/document.js
  42. 234 83
      lib/core/evaluator.js
  43. 132 25
      lib/core/fonts.js
  44. 1 0
      lib/core/fonts_utils.js
  45. 375 473
      lib/core/function.js
  46. 672 0
      lib/core/glyf.js
  47. 27 0
      lib/core/helvetica_factors.js
  48. 1 1
      lib/core/image.js
  49. 5 5
      lib/core/image_utils.js
  50. 1 1
      lib/core/jbig2.js
  51. 2 2
      lib/core/jpx.js
  52. 27 0
      lib/core/liberationsans_widths.js
  53. 27 0
      lib/core/myriadpro_factors.js
  54. 2 2
      lib/core/operator_list.js
  55. 8 6
      lib/core/parser.js
  56. 8 0
      lib/core/pdf_manager.js
  57. 4 19
      lib/core/primitives.js
  58. 26 28
      lib/core/ps_parser.js
  59. 27 0
      lib/core/segoeui_factors.js
  60. 46 7
      lib/core/standard_fonts.js
  61. 1 1
      lib/core/type1_parser.js
  62. 39 27
      lib/core/worker.js
  63. 19 12
      lib/core/writer.js
  64. 66 30
      lib/core/xfa/bind.js
  65. 20 3
      lib/core/xfa/builder.js
  66. 1 1
      lib/core/xfa/config.js
  67. 93 0
      lib/core/xfa/data.js
  68. 4 2
      lib/core/xfa/datasets.js
  69. 81 5
      lib/core/xfa/factory.js
  70. 191 0
      lib/core/xfa/fonts.js
  71. 297 236
      lib/core/xfa/html_utils.js
  72. 221 43
      lib/core/xfa/layout.js
  73. 17 3
      lib/core/xfa/parser.js
  74. 13 3
      lib/core/xfa/som.js
  75. 482 206
      lib/core/xfa/template.js
  76. 272 0
      lib/core/xfa/text.js
  77. 32 5
      lib/core/xfa/utils.js
  78. 222 54
      lib/core/xfa/xfa_object.js
  79. 173 12
      lib/core/xfa/xhtml.js
  80. 182 0
      lib/core/xfa_fonts.js
  81. 22 4
      lib/core/xref.js
  82. 2 2
      lib/display/annotation_layer.js
  83. 88 68
      lib/display/api.js
  84. 194 0
      lib/display/base_factory.js
  85. 142 77
      lib/display/canvas.js
  86. 69 149
      lib/display/display_utils.js
  87. 4 2
      lib/display/font_loader.js
  88. 38 27
      lib/display/node_utils.js
  89. 85 56
      lib/display/pattern_helper.js
  90. 1 3
      lib/display/svg.js
  91. 58 34
      lib/display/xfa_layer.js
  92. 1 3
      lib/examples/node/domstubs.js
  93. 21 23
      lib/pdf.js
  94. 2 2
      lib/pdf.sandbox.js
  95. 2 2
      lib/pdf.worker.js
  96. 1 0
      lib/shared/util.js
  97. 41 19
      lib/test/unit/annotation_spec.js
  98. 32 13
      lib/test/unit/api_spec.js
  99. 1 1
      lib/test/unit/evaluator_spec.js
  100. 1 1
      lib/test/unit/jasmine-boot.js

+ 1 - 1
bower.json

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

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


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


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


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


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


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


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


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


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


+ 15 - 25
image_decoders/pdf.image_decoders.js

@@ -252,6 +252,7 @@ exports.StreamType = StreamType;
 const FontType = {
   UNKNOWN: "UNKNOWN",
   TYPE1: "TYPE1",
+  TYPE1STANDARD: "TYPE1STANDARD",
   TYPE1C: "TYPE1C",
   CIDFONTTYPE0: "CIDFONTTYPE0",
   CIDFONTTYPE0C: "CIDFONTTYPE0C",
@@ -1889,7 +1890,7 @@ function decodeHalftoneRegion(mmr, patterns, template, regionWidth, regionHeight
       patternIndex = 0;
 
       for (j = bitsPerValue - 1; j >= 0; j--) {
-        bit = grayScaleBitPlanes[j][mg][ng] ^ bit;
+        bit ^= grayScaleBitPlanes[j][mg][ng];
         patternIndex |= bit << j;
       }
 
@@ -3178,7 +3179,7 @@ exports.readUint16 = readUint16;
 exports.readUint32 = readUint32;
 exports.toRomanNumerals = toRomanNumerals;
 exports.validateCSSFont = validateCSSFont;
-exports.XRefParseException = exports.XRefEntryException = exports.MissingDataException = void 0;
+exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = void 0;
 
 var _util = __w_pdfjs_require__(1);
 
@@ -3227,6 +3228,10 @@ class MissingDataException extends _util.BaseException {
 
 exports.MissingDataException = MissingDataException;
 
+class ParserEOFException extends _util.BaseException {}
+
+exports.ParserEOFException = ParserEOFException;
+
 class XRefEntryException extends _util.BaseException {}
 
 exports.XRefEntryException = XRefEntryException;
@@ -3749,25 +3754,8 @@ class Dict {
     dictArray,
     mergeSubDicts = false
   }) {
-    const mergedDict = new Dict(xref);
-
-    if (!mergeSubDicts) {
-      for (const dict of dictArray) {
-        if (!(dict instanceof Dict)) {
-          continue;
-        }
-
-        for (const [key, value] of Object.entries(dict._map)) {
-          if (mergedDict._map[key] === undefined) {
-            mergedDict._map[key] = value;
-          }
-        }
-      }
-
-      return mergedDict.size > 0 ? mergedDict : Dict.empty;
-    }
-
-    const properties = new Map();
+    const mergedDict = new Dict(xref),
+          properties = new Map();
 
     for (const dict of dictArray) {
       if (!(dict instanceof Dict)) {
@@ -3780,6 +3768,8 @@ class Dict {
         if (property === undefined) {
           property = [];
           properties.set(key, property);
+        } else if (!mergeSubDicts) {
+          continue;
         }
 
         property.push(value);
@@ -8597,7 +8587,7 @@ class Transform {
 class IrreversibleTransform extends Transform {
   filter(x, offset, length) {
     const len = length >> 1;
-    offset = offset | 0;
+    offset |= 0;
     let j, n, current, next;
     const alpha = -1.586134342059924;
     const beta = -0.052980118572961;
@@ -8683,7 +8673,7 @@ class IrreversibleTransform extends Transform {
 class ReversibleTransform extends Transform {
   filter(x, offset, length) {
     const len = length >> 1;
-    offset = offset | 0;
+    offset |= 0;
     let j, n;
 
     for (j = offset, n = len + 1; n--; j += 2) {
@@ -8773,8 +8763,8 @@ var _jpg = __w_pdfjs_require__(10);
 
 var _jpx = __w_pdfjs_require__(11);
 
-const pdfjsVersion = '2.9.359';
-const pdfjsBuild = 'e667c8cbc';
+const pdfjsVersion = '2.10.377';
+const pdfjsBuild = '156762c48';
 })();
 
 /******/ 	return __webpack_exports__;

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


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


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


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


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


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


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


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


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


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


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


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


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


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


+ 115 - 105
legacy/web/pdf_viewer.css

@@ -15,6 +15,7 @@
 
 .textLayer {
   position: absolute;
+  text-align: initial;
   left: 0;
   top: 0;
   right: 0;
@@ -34,13 +35,16 @@
 }
 
 .textLayer .highlight {
-  position: relative;
   margin: -1px;
   padding: 1px;
   background-color: rgba(180, 0, 170, 1);
   border-radius: 4px;
 }
 
+.textLayer .highlight.appended {
+  position: initial;
+}
+
 .textLayer .highlight.begin {
   border-radius: 4px 0 0 4px;
 }
@@ -211,7 +215,7 @@
    * area, causing horizontal scrolling. We avoid this by extending the width
    * when the element has focus and revert this when it loses focus.
    */
-  width: 115%;
+  width: 103%;
 }
 
 .annotationLayer .buttonWidgetAnnotation.checkBox input,
@@ -280,12 +284,26 @@
 }
 
 
+.xfaPage {
+  overflow: hidden;
+  position: relative;
+}
+
+.xfaContentarea {
+  position: absolute;
+}
+
+.xfaPrintOnly {
+  display: none;
+}
+
 .xfaLayer {
   position: absolute;
+  text-align: initial;
   top: 0;
   left: 0;
-  z-index: 200;
   transform-origin: 0 0;
+  line-height: 1.2;
 }
 
 .xfaLayer * {
@@ -294,12 +312,26 @@
   font-style: inherit;
   font-weight: inherit;
   font-kerning: inherit;
-  letter-spacing: inherit;
+  letter-spacing: -0.01px;
   text-align: inherit;
   text-decoration: inherit;
-  vertical-align: inherit;
   box-sizing: border-box;
   background: transparent;
+  padding: 0;
+  margin: 0;
+  pointer-events: auto;
+}
+
+.xfaLayer div {
+  pointer-events: none;
+}
+
+.xfaLayer svg {
+  pointer-events: none;
+}
+
+.xfaLayer svg * {
+  pointer-events: none;
 }
 
 .xfaLayer a {
@@ -321,56 +353,52 @@
   vertical-align: 0;
 }
 
-.xfaDraw {
-  z-index: 100;
+.xfaCaption {
+  overflow: hidden;
+  flex: 0 1 auto;
 }
 
-.xfaExclgroup {
-  z-index: 200;
+.xfaCaptionForCheckButton {
+  overflow: hidden;
+  flex: 1 1 auto;
 }
 
-.xfaField {
-  z-index: 300;
+.xfaLabel {
+  height: 100%;
+  width: 100%;
 }
 
-.xfaRich {
-  z-index: 300;
-  line-height: 1.2;
+.xfaLeft {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
 }
 
-.xfaSubform {
-  z-index: 200;
+.xfaLeft > .xfaCaption,
+.xfaLeft > .xfaCaptionForCheckButton {
+  max-height: 100%;
 }
 
-.xfaLabel {
+.xfaTop {
   display: flex;
-  flex-direction: row;
-  align-items: center;
-  width: 100%;
-  height: 100%;
+  flex-direction: column;
+  align-items: flex-start;
 }
 
-.xfaCaption {
-  flex: 1 1 auto;
+.xfaTop > .xfaCaption,
+.xfaTop > .xfaCaptionForCheckButton {
+  width: 100%;
 }
 
-.xfaBorderDiv {
+.xfaBorder {
   background: transparent;
   position: absolute;
   pointer-events: none;
 }
 
-.xfaWrapper {
-  position: relative;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  width: auto;
-  height: auto;
-}
-
-.xfaContentArea {
-  overflow: hidden;
+.xfaWrapped {
+  width: 100%;
+  height: 100%;
 }
 
 .xfaTextfield,
@@ -381,49 +409,18 @@
 .xfaTextfield:focus,
 .xfaSelect:focus {
   background-color: transparent;
+  outline: none;
 }
 
 .xfaTextfield,
 .xfaSelect {
   width: 100%;
   height: 100%;
-  flex: 100 1 0;
+  flex: 1 1 0;
   border: none;
   resize: none;
 }
 
-.xfaLabel > input[type="radio"] {
-  /* Use this trick to make the checkbox invisible but
-       but still focusable. */
-  position: absolute;
-  left: -99999px;
-}
-
-.xfaLabel > input[type="radio"]:focus + .xfaCheckboxMark {
-  box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);
-}
-
-.xfaCheckboxMark {
-  cursor: pointer;
-  flex: 0 0 auto;
-  border-style: solid;
-  border-width: 2px;
-  border-color: #8f8f9d;
-  font-size: 10px;
-  line-height: 10px;
-  width: 10px;
-  height: 10px;
-  text-align: center;
-  vertical-align: middle;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.xfaCheckbox:checked + .xfaCheckboxMark::after {
-  content: attr(mark);
-}
-
 .xfaButton {
   cursor: pointer;
   width: 100%;
@@ -436,22 +433,27 @@
   background: Highlight;
 }
 
-.xfaRich {
-  white-space: pre-wrap;
-}
-
-.xfaImage {
+.xfaCheckbox,
+.xfaRadio {
   width: 100%;
   height: 100%;
+  flex: 0 0 auto;
+  border: none;
 }
 
 .xfaRich {
+  white-space: pre-wrap;
   width: 100%;
-  height: auto;
+  height: 100%;
 }
 
-.xfaPosition {
-  display: block;
+.xfaImage {
+  -o-object-position: left top;
+     object-position: left top;
+  -o-object-fit: contain;
+     object-fit: contain;
+  width: 100%;
+  height: 100%;
 }
 
 .xfaLrTb,
@@ -462,24 +464,20 @@
   align-items: stretch;
 }
 
-.xfaLr,
-.xfaRl,
-.xfaTb > div {
-  flex: 1 1 auto;
-}
-
-.xfaTb > div {
-  justify-content: left;
+.xfaLr {
+  display: flex;
+  flex-direction: row;
+  align-items: stretch;
 }
 
-.xfaLr > div {
-  display: inline;
-  float: left;
+.xfaRl {
+  display: flex;
+  flex-direction: row-reverse;
+  align-items: stretch;
 }
 
-.xfaRl > div {
-  display: inline;
-  float: right;
+.xfaTb > div {
+  justify-content: left;
 }
 
 .xfaPosition {
@@ -495,34 +493,22 @@
   align-items: center;
 }
 
-.xfaLrTb > div {
-  display: inline;
-  float: left;
-}
-
-.xfaRlTb > div {
-  display: inline;
-  float: right;
-}
-
 .xfaTable {
   display: flex;
   flex-direction: column;
+  align-items: stretch;
 }
 
 .xfaTable .xfaRow {
   display: flex;
   flex-direction: row;
-  flex: 1 1 auto;
-}
-
-.xfaTable .xfaRow > div {
-  flex: 1 1 auto;
+  align-items: stretch;
 }
 
 .xfaTable .xfaRlRow {
   display: flex;
   flex-direction: row-reverse;
+  align-items: stretch;
   flex: 1;
 }
 
@@ -530,6 +516,30 @@
   flex: 1;
 }
 
+.xfaNonInteractive input,
+.xfaNonInteractive textarea,
+.xfaDisabled input,
+.xfaDisabled textarea,
+.xfaReadOnly input,
+.xfaReadOnly textarea {
+  background: initial;
+}
+
+@media print {
+  .xfaTextfield,
+  .xfaSelect {
+    background-color: transparent;
+  }
+
+  .xfaSelect {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+    text-indent: 1px;
+    text-overflow: "";
+  }
+}
+
 :root {
   --pdfViewer-padding-bottom: none;
   --page-margin: 1px auto -8px;

+ 1 - 0
legacy/web/pdf_viewer.d.ts

@@ -0,0 +1 @@
+export * from "pdfjs-dist/types/web/pdf_viewer.component.d.ts";

+ 211 - 108
legacy/web/pdf_viewer.js

@@ -58,7 +58,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -242,6 +242,7 @@ module.exports = pdfjsLib;
 Object.defineProperty(exports, "__esModule", ({
   value: true
 }));
+exports.fixupLangCode = fixupLangCode;
 exports.getL10nFallback = getL10nFallback;
 exports.NullL10n = void 0;
 
@@ -320,6 +321,27 @@ function getL10nFallback(key, args) {
   return DEFAULT_L10N_STRINGS[key] || "";
 }
 
+var PARTIAL_LANG_CODES = {
+  en: "en-US",
+  es: "es-ES",
+  fy: "fy-NL",
+  ga: "ga-IE",
+  gu: "gu-IN",
+  hi: "hi-IN",
+  hy: "hy-AM",
+  nb: "nb-NO",
+  ne: "ne-NP",
+  nn: "nn-NO",
+  pa: "pa-IN",
+  pt: "pt-PT",
+  sv: "sv-SE",
+  zh: "zh-CN"
+};
+
+function fixupLangCode(langCode) {
+  return PARTIAL_LANG_CODES[langCode === null || langCode === void 0 ? void 0 : langCode.toLowerCase()] || langCode;
+}
+
 function formatL10nValue(text, args) {
   if (!args) {
     return text;
@@ -482,11 +504,9 @@ var runtime = function (exports) {
   function GeneratorFunctionPrototype() {}
 
   var IteratorPrototype = {};
-
-  IteratorPrototype[iteratorSymbol] = function () {
+  define(IteratorPrototype, iteratorSymbol, function () {
     return this;
-  };
-
+  });
   var getProto = Object.getPrototypeOf;
   var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
 
@@ -495,8 +515,9 @@ var runtime = function (exports) {
   }
 
   var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
-  GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
-  GeneratorFunctionPrototype.constructor = GeneratorFunction;
+  GeneratorFunction.prototype = GeneratorFunctionPrototype;
+  define(Gp, "constructor", GeneratorFunctionPrototype);
+  define(GeneratorFunctionPrototype, "constructor", GeneratorFunction);
   GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction");
 
   function defineIteratorMethods(prototype) {
@@ -573,11 +594,9 @@ var runtime = function (exports) {
   }
 
   defineIteratorMethods(AsyncIterator.prototype);
-
-  AsyncIterator.prototype[asyncIteratorSymbol] = function () {
+  define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
     return this;
-  };
-
+  });
   exports.AsyncIterator = AsyncIterator;
 
   exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
@@ -714,14 +733,12 @@ var runtime = function (exports) {
 
   defineIteratorMethods(Gp);
   define(Gp, toStringTagSymbol, "Generator");
-
-  Gp[iteratorSymbol] = function () {
+  define(Gp, iteratorSymbol, function () {
     return this;
-  };
-
-  Gp.toString = function () {
+  });
+  define(Gp, "toString", function () {
     return "[object Generator]";
-  };
+  });
 
   function pushTryEntry(locs) {
     var entry = {
@@ -1000,7 +1017,11 @@ var runtime = function (exports) {
 try {
   regeneratorRuntime = runtime;
 } catch (accidentalStrictMode) {
-  Function("r", "regeneratorRuntime = r")(runtime);
+  if ((typeof globalThis === "undefined" ? "undefined" : _typeof(globalThis)) === "object") {
+    globalThis.regeneratorRuntime = runtime;
+  } else {
+    Function("r", "regeneratorRuntime = r")(runtime);
+  }
 }
 
 /***/ }),
@@ -1020,12 +1041,12 @@ var _ui_utils = __w_pdfjs_require__(7);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 
-function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
-
 function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
 
 function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
 
+function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
 
 function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
@@ -1040,8 +1061,6 @@ var PDFLinkService = /*#__PURE__*/function () {
         externalLinkTarget = _ref$externalLinkTarg === void 0 ? null : _ref$externalLinkTarg,
         _ref$externalLinkRel = _ref.externalLinkRel,
         externalLinkRel = _ref$externalLinkRel === void 0 ? null : _ref$externalLinkRel,
-        _ref$externalLinkEnab = _ref.externalLinkEnabled,
-        externalLinkEnabled = _ref$externalLinkEnab === void 0 ? true : _ref$externalLinkEnab,
         _ref$ignoreDestinatio = _ref.ignoreDestinationZoom,
         ignoreDestinationZoom = _ref$ignoreDestinatio === void 0 ? false : _ref$ignoreDestinatio;
 
@@ -1050,7 +1069,7 @@ var PDFLinkService = /*#__PURE__*/function () {
     this.eventBus = eventBus;
     this.externalLinkTarget = externalLinkTarget;
     this.externalLinkRel = externalLinkRel;
-    this.externalLinkEnabled = externalLinkEnabled;
+    this.externalLinkEnabled = true;
     this._ignoreDestinationZoom = ignoreDestinationZoom;
     this.baseUrl = null;
     this.pdfDocument = null;
@@ -1108,7 +1127,7 @@ var PDFLinkService = /*#__PURE__*/function () {
       var destRef = explicitDest[0];
       var pageNumber;
 
-      if (destRef instanceof Object) {
+      if (_typeof(destRef) === "object" && destRef !== null) {
         pageNumber = this._cachedPageNumber(destRef);
 
         if (pageNumber === null) {
@@ -1639,7 +1658,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -1718,7 +1737,7 @@ function getOutputScale(ctx) {
 }
 
 function scrollIntoView(element, spot) {
-  var skipOverflowHiddenElements = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
+  var scrollMatches = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
   var parent = element.offsetParent;
 
   if (!parent) {
@@ -1729,12 +1748,7 @@ function scrollIntoView(element, spot) {
   var offsetY = element.offsetTop + element.clientTop;
   var offsetX = element.offsetLeft + element.clientLeft;
 
-  while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || skipOverflowHiddenElements && getComputedStyle(parent).overflow === "hidden") {
-    if (parent.dataset._scaleY) {
-      offsetY /= parent.dataset._scaleY;
-      offsetX /= parent.dataset._scaleX;
-    }
-
+  while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
     offsetY += parent.offsetTop;
     offsetX += parent.offsetLeft;
     parent = parent.offsetParent;
@@ -2623,7 +2637,7 @@ var TextLayerBuilder = /*#__PURE__*/function () {
       function beginText(begin, className) {
         var divIdx = begin.divIdx;
         textDivs[divIdx].textContent = "";
-        appendTextToDiv(divIdx, 0, begin.offset, className);
+        return appendTextToDiv(divIdx, 0, begin.offset, className);
       }
 
       function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
@@ -2633,13 +2647,14 @@ var TextLayerBuilder = /*#__PURE__*/function () {
 
         if (className) {
           var span = document.createElement("span");
-          span.className = className;
+          span.className = "".concat(className, " appended");
           span.appendChild(node);
           div.appendChild(span);
-          return;
+          return className.includes("selected") ? span.offsetLeft : 0;
         }
 
         div.appendChild(node);
+        return 0;
       }
 
       var i0 = selectedMatchIdx,
@@ -2658,14 +2673,7 @@ var TextLayerBuilder = /*#__PURE__*/function () {
         var end = match.end;
         var isSelected = isSelectedPage && i === selectedMatchIdx;
         var highlightSuffix = isSelected ? " selected" : "";
-
-        if (isSelected) {
-          findController.scrollMatchIntoView({
-            element: textDivs[begin.divIdx],
-            pageIndex: pageIdx,
-            matchIndex: selectedMatchIdx
-          });
-        }
+        var selectedLeft = 0;
 
         if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
           if (prevEnd !== null) {
@@ -2678,9 +2686,9 @@ var TextLayerBuilder = /*#__PURE__*/function () {
         }
 
         if (begin.divIdx === end.divIdx) {
-          appendTextToDiv(begin.divIdx, begin.offset, end.offset, "highlight" + highlightSuffix);
+          selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, end.offset, "highlight" + highlightSuffix);
         } else {
-          appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix);
+          selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix);
 
           for (var n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
             textDivs[n0].className = "highlight middle" + highlightSuffix;
@@ -2690,6 +2698,15 @@ var TextLayerBuilder = /*#__PURE__*/function () {
         }
 
         prevEnd = end;
+
+        if (isSelected) {
+          findController.scrollMatchIntoView({
+            element: textDivs[begin.divIdx],
+            selectedLeft: selectedLeft,
+            pageIndex: pageIdx,
+            matchIndex: selectedMatchIdx
+          });
+        }
       }
 
       if (prevEnd) {
@@ -3014,7 +3031,7 @@ var GenericL10n = /*#__PURE__*/function () {
 
     this._lang = lang;
     this._ready = new Promise(function (resolve, reject) {
-      webL10n.setLanguage(lang, function () {
+      webL10n.setLanguage((0, _l10n_utils.fixupLangCode)(lang), function () {
         resolve(webL10n);
       });
     });
@@ -4001,7 +4018,7 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArra
 
 function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
 
-function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -4022,6 +4039,7 @@ var FIND_TIMEOUT = 250;
 var MATCH_SCROLL_OFFSET_TOP = -50;
 var MATCH_SCROLL_OFFSET_LEFT = -400;
 var CHARACTERS_TO_NORMALIZE = {
+  "\u2010": "-",
   "\u2018": "'",
   "\u2019": "'",
   "\u201A": "'",
@@ -4219,6 +4237,8 @@ var PDFFindController = /*#__PURE__*/function () {
     value: function scrollMatchIntoView(_ref2) {
       var _ref2$element = _ref2.element,
           element = _ref2$element === void 0 ? null : _ref2$element,
+          _ref2$selectedLeft = _ref2.selectedLeft,
+          selectedLeft = _ref2$selectedLeft === void 0 ? 0 : _ref2$selectedLeft,
           _ref2$pageIndex = _ref2.pageIndex,
           pageIndex = _ref2$pageIndex === void 0 ? -1 : _ref2$pageIndex,
           _ref2$matchIndex = _ref2.matchIndex,
@@ -4235,7 +4255,7 @@ var PDFFindController = /*#__PURE__*/function () {
       this._scrollMatches = false;
       var spot = {
         top: MATCH_SCROLL_OFFSET_TOP,
-        left: MATCH_SCROLL_OFFSET_LEFT
+        left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT
       };
       (0, _ui_utils.scrollIntoView)(element, spot, true);
     }
@@ -4918,7 +4938,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -5804,23 +5824,35 @@ var PDFPageView = /*#__PURE__*/function () {
           _this$xfaLayer,
           _this = this;
 
-      var keepZoomLayer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
-      var keepAnnotations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
-      this.cancelRendering(keepAnnotations);
+      var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+          _ref$keepZoomLayer = _ref.keepZoomLayer,
+          keepZoomLayer = _ref$keepZoomLayer === void 0 ? false : _ref$keepZoomLayer,
+          _ref$keepAnnotationLa = _ref.keepAnnotationLayer,
+          keepAnnotationLayer = _ref$keepAnnotationLa === void 0 ? false : _ref$keepAnnotationLa,
+          _ref$keepXfaLayer = _ref.keepXfaLayer,
+          keepXfaLayer = _ref$keepXfaLayer === void 0 ? false : _ref$keepXfaLayer;
+
+      this.cancelRendering({
+        keepAnnotationLayer: keepAnnotationLayer,
+        keepXfaLayer: keepXfaLayer
+      });
       this.renderingState = _pdf_rendering_queue.RenderingStates.INITIAL;
       var div = this.div;
       div.style.width = Math.floor(this.viewport.width) + "px";
       div.style.height = Math.floor(this.viewport.height) + "px";
-      var childNodes = div.childNodes;
-      var currentZoomLayerNode = keepZoomLayer && this.zoomLayer || null;
-      var currentAnnotationNode = keepAnnotations && ((_this$annotationLayer = this.annotationLayer) === null || _this$annotationLayer === void 0 ? void 0 : _this$annotationLayer.div) || null;
-      var currentXfaLayerNode = ((_this$xfaLayer = this.xfaLayer) === null || _this$xfaLayer === void 0 ? void 0 : _this$xfaLayer.div) || null;
+      var childNodes = div.childNodes,
+          zoomLayerNode = keepZoomLayer && this.zoomLayer || null,
+          annotationLayerNode = keepAnnotationLayer && ((_this$annotationLayer = this.annotationLayer) === null || _this$annotationLayer === void 0 ? void 0 : _this$annotationLayer.div) || null,
+          xfaLayerNode = keepXfaLayer && ((_this$xfaLayer = this.xfaLayer) === null || _this$xfaLayer === void 0 ? void 0 : _this$xfaLayer.div) || null;
 
       for (var i = childNodes.length - 1; i >= 0; i--) {
         var node = childNodes[i];
 
-        if (currentZoomLayerNode === node || currentAnnotationNode === node || currentXfaLayerNode === node) {
-          continue;
+        switch (node) {
+          case zoomLayerNode:
+          case annotationLayerNode:
+          case xfaLayerNode:
+            continue;
         }
 
         div.removeChild(node);
@@ -5828,14 +5860,15 @@ var PDFPageView = /*#__PURE__*/function () {
 
       div.removeAttribute("data-loaded");
 
-      if (currentAnnotationNode) {
+      if (annotationLayerNode) {
         this.annotationLayer.hide();
-      } else if (this.annotationLayer) {
-        this.annotationLayer.cancel();
-        this.annotationLayer = null;
       }
 
-      if (!currentZoomLayerNode) {
+      if (xfaLayerNode) {
+        this.xfaLayer.hide();
+      }
+
+      if (!zoomLayerNode) {
         if (this.canvas) {
           this.paintedViewportMap["delete"](this.canvas);
           this.canvas.width = 0;
@@ -5882,7 +5915,11 @@ var PDFPageView = /*#__PURE__*/function () {
       });
 
       if (this.svg) {
-        this.cssTransform(this.svg, true);
+        this.cssTransform({
+          target: this.svg,
+          redrawAnnotationLayer: true,
+          redrawXfaLayer: true
+        });
         this.eventBus.dispatch("pagerendered", {
           source: this,
           pageNumber: this.id,
@@ -5905,7 +5942,11 @@ var PDFPageView = /*#__PURE__*/function () {
 
       if (this.canvas) {
         if (this.useOnlyCssZoom || this.hasRestrictedScaling && isScalingRestricted) {
-          this.cssTransform(this.canvas, true);
+          this.cssTransform({
+            target: this.canvas,
+            redrawAnnotationLayer: true,
+            redrawXfaLayer: true
+          });
           this.eventBus.dispatch("pagerendered", {
             source: this,
             pageNumber: this.id,
@@ -5923,15 +5964,25 @@ var PDFPageView = /*#__PURE__*/function () {
       }
 
       if (this.zoomLayer) {
-        this.cssTransform(this.zoomLayer.firstChild);
+        this.cssTransform({
+          target: this.zoomLayer.firstChild
+        });
       }
 
-      this.reset(true, true);
+      this.reset({
+        keepZoomLayer: true,
+        keepAnnotationLayer: true,
+        keepXfaLayer: true
+      });
     }
   }, {
     key: "cancelRendering",
     value: function cancelRendering() {
-      var keepAnnotations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+      var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+          _ref2$keepAnnotationL = _ref2.keepAnnotationLayer,
+          keepAnnotationLayer = _ref2$keepAnnotationL === void 0 ? false : _ref2$keepAnnotationL,
+          _ref2$keepXfaLayer = _ref2.keepXfaLayer,
+          keepXfaLayer = _ref2$keepXfaLayer === void 0 ? false : _ref2$keepXfaLayer;
 
       if (this.paintTask) {
         this.paintTask.cancel();
@@ -5945,11 +5996,16 @@ var PDFPageView = /*#__PURE__*/function () {
         this.textLayer = null;
       }
 
-      if (!keepAnnotations && this.annotationLayer) {
+      if (this.annotationLayer && (!keepAnnotationLayer || !this.annotationLayer.div)) {
         this.annotationLayer.cancel();
         this.annotationLayer = null;
       }
 
+      if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
+        this.xfaLayer.cancel();
+        this.xfaLayer = null;
+      }
+
       if (this._onTextLayerRendered) {
         this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
 
@@ -5958,8 +6014,12 @@ var PDFPageView = /*#__PURE__*/function () {
     }
   }, {
     key: "cssTransform",
-    value: function cssTransform(target) {
-      var redrawAnnotations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
+    value: function cssTransform(_ref3) {
+      var target = _ref3.target,
+          _ref3$redrawAnnotatio = _ref3.redrawAnnotationLayer,
+          redrawAnnotationLayer = _ref3$redrawAnnotatio === void 0 ? false : _ref3$redrawAnnotatio,
+          _ref3$redrawXfaLayer = _ref3.redrawXfaLayer,
+          redrawXfaLayer = _ref3$redrawXfaLayer === void 0 ? false : _ref3$redrawXfaLayer;
       var width = this.viewport.width;
       var height = this.viewport.height;
       var div = this.div;
@@ -6019,11 +6079,11 @@ var PDFPageView = /*#__PURE__*/function () {
         textLayerDiv.style.transformOrigin = "0% 0%";
       }
 
-      if (redrawAnnotations && this.annotationLayer) {
+      if (redrawAnnotationLayer && this.annotationLayer) {
         this._renderAnnotationLayer();
       }
 
-      if (this.xfaLayer) {
+      if (redrawXfaLayer && this.xfaLayer) {
         this._renderXfaLayer();
       }
     }
@@ -6046,6 +6106,7 @@ var PDFPageView = /*#__PURE__*/function () {
     key: "draw",
     value: function draw() {
       var _this$annotationLayer2,
+          _this$xfaLayer2,
           _this2 = this;
 
       if (this.renderingState !== _pdf_rendering_queue.RenderingStates.INITIAL) {
@@ -6099,6 +6160,11 @@ var PDFPageView = /*#__PURE__*/function () {
       }
 
       this.textLayer = textLayer;
+
+      if ((_this$xfaLayer2 = this.xfaLayer) !== null && _this$xfaLayer2 !== void 0 && _this$xfaLayer2.div) {
+        div.appendChild(this.xfaLayer.div);
+      }
+
       var renderContinueCallback = null;
 
       if (this.renderingQueue) {
@@ -6119,7 +6185,7 @@ var PDFPageView = /*#__PURE__*/function () {
       }
 
       var finishPaintTask = /*#__PURE__*/function () {
-        var _ref = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee3() {
+        var _ref4 = _asyncToGenerator( /*#__PURE__*/_regenerator["default"].mark(function _callee3() {
           var error,
               _args3 = arguments;
           return _regenerator["default"].wrap(function _callee3$(_context3) {
@@ -6175,7 +6241,7 @@ var PDFPageView = /*#__PURE__*/function () {
         }));
 
         return function finishPaintTask() {
-          return _ref.apply(this, arguments);
+          return _ref4.apply(this, arguments);
         };
       }();
 
@@ -6616,7 +6682,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
 
 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
 
-function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
+function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
@@ -6650,7 +6716,6 @@ var PDFScriptingManager = /*#__PURE__*/function () {
     this._destroyCapability = null;
     this._scripting = null;
     this._mouseState = Object.create(null);
-    this._pageEventsReady = false;
     this._ready = false;
     this._eventBus = eventBus;
     this._sandboxBundleSrc = sandboxBundleSrc;
@@ -6734,8 +6799,22 @@ var PDFScriptingManager = /*#__PURE__*/function () {
                 return _context2.abrupt("return");
 
               case 19:
+                _context2.prev = 19;
                 this._scripting = this._createScripting();
+                _context2.next = 29;
+                break;
+
+              case 23:
+                _context2.prev = 23;
+                _context2.t0 = _context2["catch"](19);
+                console.error("PDFScriptingManager.setDocument: \"".concat(_context2.t0 === null || _context2.t0 === void 0 ? void 0 : _context2.t0.message, "\"."));
+                _context2.next = 28;
+                return this._destroyScripting();
+
+              case 28:
+                return _context2.abrupt("return");
 
+              case 29:
                 this._internalEvents.set("updatefromsandbox", function (event) {
                   if ((event === null || event === void 0 ? void 0 : event.source) !== window) {
                     return;
@@ -6846,22 +6925,22 @@ var PDFScriptingManager = /*#__PURE__*/function () {
                   _iterator2.f();
                 }
 
-                _context2.prev = 31;
-                _context2.next = 34;
+                _context2.prev = 40;
+                _context2.next = 43;
                 return this._getDocProperties();
 
-              case 34:
+              case 43:
                 docProperties = _context2.sent;
 
                 if (!(pdfDocument !== this._pdfDocument)) {
-                  _context2.next = 37;
+                  _context2.next = 46;
                   break;
                 }
 
                 return _context2.abrupt("return");
 
-              case 37:
-                _context2.next = 39;
+              case 46:
+                _context2.next = 48;
                 return this._scripting.createSandbox({
                   objects: objects,
                   calculationOrder: calculationOrder,
@@ -6874,48 +6953,48 @@ var PDFScriptingManager = /*#__PURE__*/function () {
                   })
                 });
 
-              case 39:
+              case 48:
                 this._eventBus.dispatch("sandboxcreated", {
                   source: this
                 });
 
-                _context2.next = 48;
+                _context2.next = 57;
                 break;
 
-              case 42:
-                _context2.prev = 42;
-                _context2.t0 = _context2["catch"](31);
-                console.error("PDFScriptingManager.setDocument: \"".concat(_context2.t0 === null || _context2.t0 === void 0 ? void 0 : _context2.t0.message, "\"."));
-                _context2.next = 47;
+              case 51:
+                _context2.prev = 51;
+                _context2.t1 = _context2["catch"](40);
+                console.error("PDFScriptingManager.setDocument: \"".concat(_context2.t1 === null || _context2.t1 === void 0 ? void 0 : _context2.t1.message, "\"."));
+                _context2.next = 56;
                 return this._destroyScripting();
 
-              case 47:
+              case 56:
                 return _context2.abrupt("return");
 
-              case 48:
-                _context2.next = 50;
+              case 57:
+                _context2.next = 59;
                 return (_this$_scripting = this._scripting) === null || _this$_scripting === void 0 ? void 0 : _this$_scripting.dispatchEventInSandbox({
                   id: "doc",
                   name: "Open"
                 });
 
-              case 50:
-                _context2.next = 52;
+              case 59:
+                _context2.next = 61;
                 return this._dispatchPageOpen(this._pdfViewer.currentPageNumber, true);
 
-              case 52:
+              case 61:
                 Promise.resolve().then(function () {
                   if (pdfDocument === _this2._pdfDocument) {
                     _this2._ready = true;
                   }
                 });
 
-              case 53:
+              case 62:
               case "end":
                 return _context2.stop();
             }
           }
-        }, _callee2, this, [[31, 42]]);
+        }, _callee2, this, [[19, 23], [40, 51]]);
       }));
 
       function setDocument(_x) {
@@ -7218,10 +7297,9 @@ var PDFScriptingManager = /*#__PURE__*/function () {
 
                 if (initialize) {
                   this._closeCapability = (0, _pdfjsLib.createPromiseCapability)();
-                  this._pageEventsReady = true;
                 }
 
-                if (this._pageEventsReady) {
+                if (this._closeCapability) {
                   _context9.next = 5;
                   break;
                 }
@@ -7309,7 +7387,7 @@ var PDFScriptingManager = /*#__PURE__*/function () {
               case 0:
                 pdfDocument = this._pdfDocument, visitedPages = this._visitedPages;
 
-                if (this._pageEventsReady) {
+                if (this._closeCapability) {
                   _context10.next = 3;
                   break;
                 }
@@ -7511,11 +7589,10 @@ var PDFScriptingManager = /*#__PURE__*/function () {
 
                 this._scripting = null;
                 delete this._mouseState.isDown;
-                this._pageEventsReady = false;
                 this._ready = false;
                 (_this$_destroyCapabil3 = this._destroyCapability) === null || _this$_destroyCapabil3 === void 0 ? void 0 : _this$_destroyCapabil3.resolve();
 
-              case 29:
+              case 28:
               case "end":
                 return _context12.stop();
             }
@@ -8028,7 +8105,7 @@ var BaseViewer = /*#__PURE__*/function () {
       throw new Error("Cannot initialize BaseViewer.");
     }
 
-    var viewerVersion = '2.9.359';
+    var viewerVersion = '2.10.377';
 
     if (_pdfjsLib.version !== viewerVersion) {
       throw new Error("The API version \"".concat(_pdfjsLib.version, "\" does not match the Viewer version \"").concat(viewerVersion, "\"."));
@@ -9694,12 +9771,14 @@ var XfaLayerBuilder = /*#__PURE__*/function () {
   function XfaLayerBuilder(_ref) {
     var pageDiv = _ref.pageDiv,
         pdfPage = _ref.pdfPage,
+        xfaHtml = _ref.xfaHtml,
         annotationStorage = _ref.annotationStorage;
 
     _classCallCheck(this, XfaLayerBuilder);
 
     this.pageDiv = pageDiv;
     this.pdfPage = pdfPage;
+    this.xfaHtml = xfaHtml;
     this.annotationStorage = annotationStorage;
     this.div = null;
     this._cancelled = false;
@@ -9711,6 +9790,27 @@ var XfaLayerBuilder = /*#__PURE__*/function () {
       var _this = this;
 
       var intent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "display";
+
+      if (intent === "print") {
+        var parameters = {
+          viewport: viewport.clone({
+            dontFlip: true
+          }),
+          div: this.div,
+          xfa: this.xfaHtml,
+          page: null,
+          annotationStorage: this.annotationStorage,
+          intent: intent
+        };
+        var div = document.createElement("div");
+        this.pageDiv.appendChild(div);
+        parameters.div = div;
+
+        _pdfjsLib.XfaLayer.render(parameters);
+
+        return Promise.resolve();
+      }
+
       return this.pdfPage.getXfa().then(function (xfa) {
         if (_this._cancelled) {
           return;
@@ -9723,7 +9823,8 @@ var XfaLayerBuilder = /*#__PURE__*/function () {
           div: _this.div,
           xfa: xfa,
           page: _this.pdfPage,
-          annotationStorage: _this.annotationStorage
+          annotationStorage: _this.annotationStorage,
+          intent: intent
         };
 
         if (_this.div) {
@@ -9771,10 +9872,12 @@ var DefaultXfaLayerFactory = /*#__PURE__*/function () {
     key: "createXfaLayerBuilder",
     value: function createXfaLayerBuilder(pageDiv, pdfPage) {
       var annotationStorage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
+      var xfaHtml = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
       return new XfaLayerBuilder({
         pageDiv: pageDiv,
         pdfPage: pdfPage,
-        annotationStorage: annotationStorage
+        annotationStorage: annotationStorage,
+        xfaHtml: xfaHtml
       });
     }
   }]);
@@ -10112,8 +10215,8 @@ var _pdf_single_page_viewer = __w_pdfjs_require__(20);
 
 var _pdf_viewer = __w_pdfjs_require__(24);
 
-var pdfjsVersion = '2.9.359';
-var pdfjsBuild = 'e667c8cbc';
+var pdfjsVersion = '2.10.377';
+var pdfjsBuild = '156762c48';
 })();
 
 /******/ 	return __webpack_exports__;

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


+ 1 - 1
lib/README.md

@@ -3,5 +3,5 @@ pre-built library as found in e.g. the `/build`, `/web`, and `/image_decoders`
 folders in the root of this repository.
 
 Please note that the "lib" build target exists mostly to enable unit-testing in
-Node.js/Travis, and that you'll need to handle e.g. any necessary polyfills
+Node.js/GitHub, and that you'll need to handle e.g. any necessary polyfills
 and/or Node.js dependencies yourself if using the files in this folder.

+ 6 - 2
lib/core/annotation.js

@@ -508,7 +508,7 @@ class Annotation {
     const transform = getTransformMatrix(data.rect, bbox, matrix);
     return resourcesPromise.then(resources => {
       const opList = new _operator_list.OperatorList();
-      opList.addOp(_util.OPS.beginAnnotation, [data.rect, transform, matrix]);
+      opList.addOp(_util.OPS.beginAnnotation, [data.id, data.rect, transform, matrix]);
       return evaluator.getOperatorList({
         stream: appearance,
         task,
@@ -955,7 +955,7 @@ class WidgetAnnotation extends Annotation {
       const matrix = [1, 0, 0, 1, 0, 0];
       const bbox = [0, 0, this.data.rect[2] - this.data.rect[0], this.data.rect[3] - this.data.rect[1]];
       const transform = getTransformMatrix(this.data.rect, bbox, matrix);
-      operatorList.addOp(_util.OPS.beginAnnotation, [this.data.rect, transform, matrix]);
+      operatorList.addOp(_util.OPS.beginAnnotation, [this.data.id, this.data.rect, transform, matrix]);
       const stream = new _stream.StringStream(content);
       return evaluator.getOperatorList({
         stream,
@@ -1594,6 +1594,10 @@ class ButtonWidgetAnnotation extends WidgetAnnotation {
       exportValues.push("Off");
     }
 
+    if (!exportValues.includes(this.data.fieldValue)) {
+      this.data.fieldValue = null;
+    }
+
     if (exportValues.length !== 2) {
       return;
     }

+ 1 - 1
lib/core/ascii_85_stream.js

@@ -33,7 +33,7 @@ var _core_utils = require("./core_utils.js");
 class Ascii85Stream extends _decode_stream.DecodeStream {
   constructor(str, maybeLength) {
     if (maybeLength) {
-      maybeLength = 0.8 * maybeLength;
+      maybeLength *= 0.8;
     }
 
     super(maybeLength);

+ 1 - 1
lib/core/ascii_hex_stream.js

@@ -31,7 +31,7 @@ var _decode_stream = require("./decode_stream.js");
 class AsciiHexStream extends _decode_stream.DecodeStream {
   constructor(str, maybeLength) {
     if (maybeLength) {
-      maybeLength = 0.5 * maybeLength;
+      maybeLength *= 0.5;
     }
 
     super(maybeLength);

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


+ 22 - 0
lib/core/catalog.js

@@ -64,6 +64,7 @@ class Catalog {
 
     this.fontCache = new _primitives.RefSetCache();
     this.builtInCMapCache = new Map();
+    this.standardFontDataCache = new Map();
     this.globalImageCache = new _image_utils.GlobalImageCache();
     this.pageKidsCountCache = new _primitives.RefSetCache();
     this.pageIndexCache = new _primitives.RefSetCache();
@@ -1001,6 +1002,26 @@ class Catalog {
     return (0, _util.shadow)(this, "attachments", attachments);
   }
 
+  get xfaImages() {
+    const obj = this._catDict.get("Names");
+
+    let xfaImages = null;
+
+    if (obj instanceof _primitives.Dict && obj.has("XFAImages")) {
+      const nameTree = new _name_number_tree.NameTree(obj.getRaw("XFAImages"), this.xref);
+
+      for (const [key, value] of nameTree.getAll()) {
+        if (!xfaImages) {
+          xfaImages = new _primitives.Dict(this.xref);
+        }
+
+        xfaImages.set(key, value);
+      }
+    }
+
+    return (0, _util.shadow)(this, "xfaImages", xfaImages);
+  }
+
   _collectJavaScript() {
     const obj = this._catDict.get("Names");
 
@@ -1109,6 +1130,7 @@ class Catalog {
 
       this.fontCache.clear();
       this.builtInCMapCache.clear();
+      this.standardFontDataCache.clear();
     });
   }
 

+ 6 - 1
lib/core/cff_font.js

@@ -86,7 +86,12 @@ class CFFFont {
       return charCodeToGlyphId;
     }
 
-    const encoding = cff.encoding ? cff.encoding.encoding : null;
+    let encoding = cff.encoding ? cff.encoding.encoding : null;
+
+    if (properties.isInternalFont) {
+      encoding = properties.defaultEncoding;
+    }
+
     charCodeToGlyphId = (0, _fonts_utils.type1FontGlyphMapping)(properties, encoding, charsets);
     return charCodeToGlyphId;
   }

+ 11 - 2
lib/core/cff_parser.js

@@ -608,6 +608,9 @@ const CFFParser = function CFFParserClosure() {
         } else if (value === 11) {
           state.stackSize = stackSize;
           return true;
+        } else if (value === 0 && j === data.length) {
+          data[j - 1] = 14;
+          validationCommand = CharstringValidationData[14];
         } else {
           validationCommand = CharstringValidationData[value];
         }
@@ -627,6 +630,12 @@ const CFFParser = function CFFParserClosure() {
           if ("min" in validationCommand) {
             if (!state.undefStack && stackSize < validationCommand.min) {
               (0, _util.warn)("Not enough parameters for " + validationCommand.id + "; actual: " + stackSize + ", expected: " + validationCommand.min);
+
+              if (stackSize === 0) {
+                data[j - 1] = 14;
+                return true;
+              }
+
               return false;
             }
           }
@@ -926,7 +935,7 @@ const CFFParser = function CFFParserClosure() {
         raw = bytes.subarray(dataStart, dataEnd);
       }
 
-      format = format & 0x7f;
+      format &= 0x7f;
       return new CFFEncoding(predefined, format, encoding, raw);
     }
 
@@ -1489,7 +1498,7 @@ class CFFCompiler {
     if (value >= -107 && value <= 107) {
       code = [value + 139];
     } else if (value >= 108 && value <= 1131) {
-      value = value - 108;
+      value -= 108;
       code = [(value >> 8) + 247, value & 0xff];
     } else if (value >= -1131 && value <= -108) {
       value = -value - 108;

+ 1 - 1
lib/core/cmap.js

@@ -385,7 +385,7 @@ const BinaryCMapReader = function BinaryCMapReaderClosure() {
 
       while (i >= 0) {
         while (bufferSize < 8 && stack.length > 0) {
-          buffer = stack[--sp] << bufferSize | buffer;
+          buffer |= stack[--sp] << bufferSize;
           bufferSize += 7;
         }
 

+ 5 - 1
lib/core/core_utils.js

@@ -38,7 +38,7 @@ exports.readUint16 = readUint16;
 exports.readUint32 = readUint32;
 exports.toRomanNumerals = toRomanNumerals;
 exports.validateCSSFont = validateCSSFont;
-exports.XRefParseException = exports.XRefEntryException = exports.MissingDataException = void 0;
+exports.XRefParseException = exports.XRefEntryException = exports.ParserEOFException = exports.MissingDataException = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -87,6 +87,10 @@ class MissingDataException extends _util.BaseException {
 
 exports.MissingDataException = MissingDataException;
 
+class ParserEOFException extends _util.BaseException {}
+
+exports.ParserEOFException = ParserEOFException;
+
 class XRefEntryException extends _util.BaseException {}
 
 exports.XRefEntryException = XRefEntryException;

+ 3 - 3
lib/core/crypto.js

@@ -209,7 +209,7 @@ class Word64 {
       this.low = 0;
     } else {
       this.high = this.high << places | this.low >>> 32 - places;
-      this.low = this.low << places;
+      this.low <<= places;
     }
   }
 
@@ -977,7 +977,7 @@ class AES128Cipher extends AESBaseCipher {
       t2 = s[t2];
       t3 = s[t3];
       t4 = s[t4];
-      t1 = t1 ^ rcon[i];
+      t1 ^= rcon[i];
 
       for (let n = 0; n < 4; ++n) {
         result[j] = t1 ^= result[j - 16];
@@ -1029,7 +1029,7 @@ class AES256Cipher extends AESBaseCipher {
         t2 = s[t2];
         t3 = s[t3];
         t4 = s[t4];
-        t1 = t1 ^ r;
+        t1 ^= r;
 
         if ((r <<= 1) >= 256) {
           r = (r ^ 0x1b) & 0xff;

+ 163 - 24
lib/core/document.js

@@ -42,6 +42,8 @@ var _crypto = require("./crypto.js");
 
 var _catalog = require("./catalog.js");
 
+var _xfa_fonts = require("./xfa_fonts.js");
+
 var _parser = require("./parser.js");
 
 var _object_loader = require("./object_loader.js");
@@ -71,6 +73,7 @@ class Page {
     globalIdFactory,
     fontCache,
     builtInCMapCache,
+    standardFontDataCache,
     globalImageCache,
     nonBlendModesSet,
     xfaFactory
@@ -82,6 +85,7 @@ class Page {
     this.ref = ref;
     this.fontCache = fontCache;
     this.builtInCMapCache = builtInCMapCache;
+    this.standardFontDataCache = standardFontDataCache;
     this.globalImageCache = globalImageCache;
     this.nonBlendModesSet = nonBlendModesSet;
     this.evaluatorOptions = pdfManager.evaluatorOptions;
@@ -134,11 +138,7 @@ class Page {
 
   _getBoundingBox(name) {
     if (this.xfaData) {
-      const {
-        width,
-        height
-      } = this.xfaData.attributes.style;
-      return [0, 0, parseInt(width), parseInt(height)];
+      return this.xfaData.bbox;
     }
 
     const box = this._getInheritableProperty(name, true);
@@ -200,7 +200,7 @@ class Page {
     if (rotate % 90 !== 0) {
       rotate = 0;
     } else if (rotate >= 360) {
-      rotate = rotate % 360;
+      rotate %= 360;
     } else if (rotate < 0) {
       rotate = (rotate % 360 + 360) % 360;
     }
@@ -224,7 +224,9 @@ class Page {
 
   get xfaData() {
     if (this.xfaFactory) {
-      return (0, _util.shadow)(this, "xfaData", this.xfaFactory.getPage(this.pageIndex));
+      return (0, _util.shadow)(this, "xfaData", {
+        bbox: this.xfaFactory.getBoundingBox(this.pageIndex)
+      });
     }
 
     return (0, _util.shadow)(this, "xfaData", null);
@@ -238,6 +240,7 @@ class Page {
       idFactory: this._localIdFactory,
       fontCache: this.fontCache,
       builtInCMapCache: this.builtInCMapCache,
+      standardFontDataCache: this.standardFontDataCache,
       globalImageCache: this.globalImageCache,
       options: this.evaluatorOptions
     });
@@ -287,6 +290,7 @@ class Page {
       idFactory: this._localIdFactory,
       fontCache: this.fontCache,
       builtInCMapCache: this.builtInCMapCache,
+      standardFontDataCache: this.standardFontDataCache,
       globalImageCache: this.globalImageCache,
       options: this.evaluatorOptions
     });
@@ -315,10 +319,11 @@ class Page {
         };
       }
 
+      const annotationIntent = intent.startsWith("oplist-") ? intent.split("-")[1] : intent;
       const opListPromises = [];
 
       for (const annotation of annotations) {
-        if (intent === "display" && annotation.mustBeViewed(annotationStorage) || intent === "print" && annotation.mustBePrinted(annotationStorage)) {
+        if (annotationIntent === "display" && annotation.mustBeViewed(annotationStorage) || annotationIntent === "print" && annotation.mustBePrinted(annotationStorage)) {
           opListPromises.push(annotation.getOperatorList(partialEvaluator, task, renderInteractiveForms, annotationStorage).catch(function (reason) {
             (0, _util.warn)("getOperatorList - ignoring annotation data during " + `"${task.name}" task: "${reason}".`);
             return null;
@@ -361,6 +366,7 @@ class Page {
         idFactory: this._localIdFactory,
         fontCache: this.fontCache,
         builtInCMapCache: this.builtInCMapCache,
+        standardFontDataCache: this.standardFontDataCache,
         globalImageCache: this.globalImageCache,
         options: this.evaluatorOptions
       });
@@ -762,7 +768,40 @@ class PDFDocument {
   }
 
   get isPureXfa() {
-    return this.xfaFactory !== null;
+    return this.xfaFactory && this.xfaFactory.isValid();
+  }
+
+  get htmlForXfa() {
+    if (this.xfaFactory) {
+      return this.xfaFactory.getPages();
+    }
+
+    return null;
+  }
+
+  async loadXfaImages() {
+    const xfaImagesDict = await this.pdfManager.ensureCatalog("xfaImages");
+
+    if (!xfaImagesDict) {
+      return;
+    }
+
+    const keys = xfaImagesDict.getKeys();
+    const objectLoader = new _object_loader.ObjectLoader(xfaImagesDict, keys, this.xref);
+    await objectLoader.load();
+    const xfaImages = new Map();
+
+    for (const key of keys) {
+      const stream = xfaImagesDict.get(key);
+
+      if (!(0, _primitives.isStream)(stream)) {
+        continue;
+      }
+
+      xfaImages.set(key, stream.getBytes());
+    }
+
+    this.xfaFactory.setImages(xfaImages);
   }
 
   async loadXfaFonts(handler, task) {
@@ -786,17 +825,28 @@ class PDFDocument {
       return;
     }
 
+    const options = Object.assign(Object.create(null), this.pdfManager.evaluatorOptions);
+    options.useSystemFonts = false;
     const partialEvaluator = new _evaluator.PartialEvaluator({
       xref: this.xref,
       handler,
       pageIndex: -1,
       idFactory: this._globalIdFactory,
       fontCache: this.catalog.fontCache,
-      builtInCMapCache: this.catalog.builtInCMapCache
+      builtInCMapCache: this.catalog.builtInCMapCache,
+      standardFontDataCache: this.catalog.standardFontDataCache,
+      options
     });
     const operatorList = new _operator_list.OperatorList();
+    const pdfFonts = [];
     const initialState = {
-      font: null,
+      get font() {
+        return pdfFonts[pdfFonts.length - 1];
+      },
+
+      set font(font) {
+        pdfFonts.push(font);
+      },
 
       clone() {
         return this;
@@ -816,7 +866,8 @@ class PDFDocument {
         continue;
       }
 
-      const fontFamily = descriptor.get("FontFamily");
+      let fontFamily = descriptor.get("FontFamily");
+      fontFamily = fontFamily.replace(/[ ]+([0-9])/g, "$1");
       const fontWeight = descriptor.get("FontWeight");
       const italicAngle = -descriptor.get("ItalicAngle");
       const cssFontInfo = {
@@ -836,6 +887,80 @@ class PDFDocument {
     }
 
     await Promise.all(promises);
+    const missingFonts = this.xfaFactory.setFonts(pdfFonts);
+
+    if (!missingFonts) {
+      return;
+    }
+
+    options.ignoreErrors = true;
+    promises.length = 0;
+    pdfFonts.length = 0;
+    const reallyMissingFonts = new Set();
+
+    for (const missing of missingFonts) {
+      if (!(0, _xfa_fonts.getXfaFontWidths)(`${missing}-Regular`)) {
+        reallyMissingFonts.add(missing);
+      }
+    }
+
+    if (reallyMissingFonts.size) {
+      missingFonts.push("PdfJS-Fallback");
+    }
+
+    for (const missing of missingFonts) {
+      if (reallyMissingFonts.has(missing)) {
+        continue;
+      }
+
+      for (const fontInfo of [{
+        name: "Regular",
+        fontWeight: 400,
+        italicAngle: 0
+      }, {
+        name: "Bold",
+        fontWeight: 700,
+        italicAngle: 0
+      }, {
+        name: "Italic",
+        fontWeight: 400,
+        italicAngle: 12
+      }, {
+        name: "BoldItalic",
+        fontWeight: 700,
+        italicAngle: 12
+      }]) {
+        const name = `${missing}-${fontInfo.name}`;
+        const widths = (0, _xfa_fonts.getXfaFontWidths)(name);
+        const dict = new _primitives.Dict(null);
+        dict.set("BaseFont", _primitives.Name.get(name));
+        dict.set("Type", _primitives.Name.get("Font"));
+        dict.set("Subtype", _primitives.Name.get("TrueType"));
+        dict.set("Encoding", _primitives.Name.get("WinAnsiEncoding"));
+        const descriptor = new _primitives.Dict(null);
+        descriptor.set("Widths", widths);
+        dict.set("FontDescriptor", descriptor);
+        promises.push(partialEvaluator.handleSetFont(resources, [_primitives.Name.get(name), 1], null, operatorList, task, initialState, dict, {
+          fontFamily: missing,
+          fontWeight: fontInfo.fontWeight,
+          italicAngle: fontInfo.italicAngle
+        }).catch(function (reason) {
+          (0, _util.warn)(`loadXfaFonts: "${reason}".`);
+          return null;
+        }));
+      }
+    }
+
+    await Promise.all(promises);
+    this.xfaFactory.appendFonts(pdfFonts, reallyMissingFonts);
+  }
+
+  async serializeXfaData(annotationStorage) {
+    if (this.xfaFactory) {
+      return this.xfaFactory.serializeData(annotationStorage);
+    }
+
+    return null;
   }
 
   get formInfo() {
@@ -948,24 +1073,36 @@ class PDFDocument {
     return (0, _util.shadow)(this, "documentInfo", docInfo);
   }
 
-  get fingerprint() {
-    let hash;
-    const idArray = this.xref.trailer.get("ID");
+  get fingerprints() {
+    function validate(data) {
+      return typeof data === "string" && data.length > 0 && data !== EMPTY_FINGERPRINT;
+    }
 
-    if (Array.isArray(idArray) && idArray[0] && (0, _util.isString)(idArray[0]) && idArray[0] !== EMPTY_FINGERPRINT) {
-      hash = (0, _util.stringToBytes)(idArray[0]);
-    } else {
-      hash = (0, _crypto.calculateMD5)(this.stream.getByteRange(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
+    function hexString(hash) {
+      const buf = [];
+
+      for (let i = 0, ii = hash.length; i < ii; i++) {
+        const hex = hash[i].toString(16);
+        buf.push(hex.padStart(2, "0"));
+      }
+
+      return buf.join("");
     }
 
-    const fingerprintBuf = [];
+    const idArray = this.xref.trailer.get("ID");
+    let hashOriginal, hashModified;
 
-    for (let i = 0, ii = hash.length; i < ii; i++) {
-      const hex = hash[i].toString(16);
-      fingerprintBuf.push(hex.padStart(2, "0"));
+    if (Array.isArray(idArray) && validate(idArray[0])) {
+      hashOriginal = (0, _util.stringToBytes)(idArray[0]);
+
+      if (idArray[1] !== idArray[0] && validate(idArray[1])) {
+        hashModified = (0, _util.stringToBytes)(idArray[1]);
+      }
+    } else {
+      hashOriginal = (0, _crypto.calculateMD5)(this.stream.getByteRange(0, FINGERPRINT_FIRST_BYTES), 0, FINGERPRINT_FIRST_BYTES);
     }
 
-    return (0, _util.shadow)(this, "fingerprint", fingerprintBuf.join(""));
+    return (0, _util.shadow)(this, "fingerprints", [hexString(hashOriginal), hashModified ? hexString(hashModified) : null]);
   }
 
   _getLinearizationPage(pageIndex) {
@@ -1012,6 +1149,7 @@ class PDFDocument {
         globalIdFactory: this._globalIdFactory,
         fontCache: catalog.fontCache,
         builtInCMapCache: catalog.builtInCMapCache,
+        standardFontDataCache: catalog.standardFontDataCache,
         globalImageCache: catalog.globalImageCache,
         nonBlendModesSet: catalog.nonBlendModesSet,
         xfaFactory: this.xfaFactory
@@ -1029,6 +1167,7 @@ class PDFDocument {
         globalIdFactory: this._globalIdFactory,
         fontCache: catalog.fontCache,
         builtInCMapCache: catalog.builtInCMapCache,
+        standardFontDataCache: catalog.standardFontDataCache,
         globalImageCache: catalog.globalImageCache,
         nonBlendModesSet: catalog.nonBlendModesSet,
         xfaFactory: null

+ 234 - 83
lib/core/evaluator.js

@@ -38,10 +38,10 @@ var _fonts_utils = require("./fonts_utils.js");
 
 var _encodings = require("./encodings.js");
 
-var _unicode = require("./unicode.js");
-
 var _standard_fonts = require("./standard_fonts.js");
 
+var _unicode = require("./unicode.js");
+
 var _pattern = require("./pattern.js");
 
 var _to_unicode_map = require("./to_unicode_map.js");
@@ -52,6 +52,8 @@ var _parser = require("./parser.js");
 
 var _image_utils = require("./image_utils.js");
 
+var _stream = require("./stream.js");
+
 var _bidi = require("./bidi.js");
 
 var _colorspace = require("./colorspace.js");
@@ -64,9 +66,9 @@ var _core_utils = require("./core_utils.js");
 
 var _metrics = require("./metrics.js");
 
-var _murmurhash = require("./murmurhash3.js");
+var _xfa_fonts = require("./xfa_fonts.js");
 
-var _stream = require("./stream.js");
+var _murmurhash = require("./murmurhash3.js");
 
 var _operator_list = require("./operator_list.js");
 
@@ -77,7 +79,10 @@ const DefaultPartialEvaluatorOptions = Object.freeze({
   disableFontFace: false,
   ignoreErrors: false,
   isEvalSupported: true,
-  fontExtraProperties: false
+  fontExtraProperties: false,
+  useSystemFonts: true,
+  cMapUrl: null,
+  standardFontDataUrl: null
 });
 const PatternType = {
   TILING: 1,
@@ -203,6 +208,7 @@ class PartialEvaluator {
     idFactory,
     fontCache,
     builtInCMapCache,
+    standardFontDataCache,
     globalImageCache,
     options = null
   }) {
@@ -212,6 +218,7 @@ class PartialEvaluator {
     this.idFactory = idFactory;
     this.fontCache = fontCache;
     this.builtInCMapCache = builtInCMapCache;
+    this.standardFontDataCache = standardFontDataCache;
     this.globalImageCache = globalImageCache;
     this.options = options || DefaultPartialEvaluatorOptions;
     this.parsingType3Font = false;
@@ -226,9 +233,9 @@ class PartialEvaluator {
     return (0, _util.shadow)(this, "_pdfFunctionFactory", pdfFunctionFactory);
   }
 
-  clone(newOptions = DefaultPartialEvaluatorOptions) {
+  clone(newOptions = null) {
     const newEvaluator = Object.create(this);
-    newEvaluator.options = newOptions;
+    newEvaluator.options = Object.assign(Object.create(null), this.options, newOptions);
     return newEvaluator;
   }
 
@@ -358,27 +365,25 @@ class PartialEvaluator {
       return cachedData;
     }
 
-    const readableStream = this.handler.sendWithStream("FetchBuiltInCMap", {
-      name
-    });
-    const reader = readableStream.getReader();
-    const data = await new Promise(function (resolve, reject) {
-      function pump() {
-        reader.read().then(function ({
-          value,
-          done
-        }) {
-          if (done) {
-            return;
-          }
+    let data;
 
-          resolve(value);
-          pump();
-        }, reject);
+    if (this.options.cMapUrl !== null) {
+      const url = `${this.options.cMapUrl}${name}.bcmap`;
+      const response = await fetch(url);
+
+      if (!response.ok) {
+        throw new Error(`fetchBuiltInCMap: failed to fetch file "${url}" with "${response.statusText}".`);
       }
 
-      pump();
-    });
+      data = {
+        cMapData: new Uint8Array(await response.arrayBuffer()),
+        compressionType: _util.CMapCompressionType.BINARY
+      };
+    } else {
+      data = await this.handler.sendWithPromise("FetchBuiltInCMap", {
+        name
+      });
+    }
 
     if (data.compressionType !== _util.CMapCompressionType.NONE) {
       this.builtInCMapCache.set(name, data);
@@ -387,6 +392,48 @@ class PartialEvaluator {
     return data;
   }
 
+  async fetchStandardFontData(name) {
+    const cachedData = this.standardFontDataCache.get(name);
+
+    if (cachedData) {
+      return new _stream.Stream(cachedData);
+    }
+
+    if (this.options.useSystemFonts && name !== "Symbol" && name !== "ZapfDingbats") {
+      return null;
+    }
+
+    const standardFontNameToFileName = (0, _standard_fonts.getFontNameToFileMap)(),
+          filename = standardFontNameToFileName[name];
+    let data;
+
+    if (this.options.standardFontDataUrl !== null) {
+      const url = `${this.options.standardFontDataUrl}${filename}`;
+      const response = await fetch(url);
+
+      if (!response.ok) {
+        (0, _util.warn)(`fetchStandardFontData: failed to fetch file "${url}" with "${response.statusText}".`);
+      } else {
+        data = await response.arrayBuffer();
+      }
+    } else {
+      try {
+        data = await this.handler.sendWithPromise("FetchStandardFontData", {
+          filename
+        });
+      } catch (e) {
+        (0, _util.warn)(`fetchStandardFontData: failed to fetch file "${filename}" with "${e}".`);
+      }
+    }
+
+    if (!data) {
+      return null;
+    }
+
+    this.standardFontDataCache.set(name, data);
+    return new _stream.Stream(data);
+  }
+
   async buildFormXObject(resources, xobj, smask, operatorList, task, initialState, localColorSpaceCache) {
     const dict = xobj.dict;
     const matrix = dict.getArray("Matrix");
@@ -1113,7 +1160,29 @@ class PartialEvaluator {
     });
   }
 
-  handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache) {
+  parseShading({
+    keyObj,
+    shading,
+    resources,
+    localColorSpaceCache,
+    localShadingPatternCache,
+    matrix = null
+  }) {
+    let id = localShadingPatternCache.get(keyObj);
+
+    if (!id) {
+      var shadingFill = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this._pdfFunctionFactory, localColorSpaceCache);
+
+      const patternIR = shadingFill.getIR();
+      id = `pattern_${this.idFactory.createObjId()}`;
+      localShadingPatternCache.set(keyObj, id);
+      this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
+    }
+
+    return id;
+  }
+
+  handleColorN(operatorList, fn, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache) {
     const patternName = args.pop();
 
     if (patternName instanceof _primitives.Name) {
@@ -1129,7 +1198,7 @@ class PartialEvaluator {
         } catch (ex) {}
       }
 
-      let pattern = patterns.get(name);
+      const pattern = patterns.get(name);
 
       if (pattern) {
         const dict = (0, _primitives.isStream)(pattern) ? pattern.dict : pattern;
@@ -1141,8 +1210,15 @@ class PartialEvaluator {
         } else if (typeNum === PatternType.SHADING) {
           const shading = dict.get("Shading");
           const matrix = dict.getArray("Matrix");
-          pattern = _pattern.Pattern.parseShading(shading, matrix, this.xref, resources, this.handler, this._pdfFunctionFactory, localColorSpaceCache);
-          operatorList.addOp(fn, pattern.getIR());
+          const objId = this.parseShading({
+            keyObj: pattern,
+            shading,
+            matrix,
+            resources,
+            localColorSpaceCache,
+            localShadingPatternCache
+          });
+          operatorList.addOp(fn, ["Shading", objId]);
           return undefined;
         }
 
@@ -1283,6 +1359,7 @@ class PartialEvaluator {
     const localColorSpaceCache = new _image_utils.LocalColorSpaceCache();
     const localGStateCache = new _image_utils.LocalGStateCache();
     const localTilingPatternCache = new _image_utils.LocalTilingPatternCache();
+    const localShadingPatternCache = new Map();
 
     const xobjs = resources.get("XObject") || _primitives.Dict.empty;
 
@@ -1312,7 +1389,7 @@ class PartialEvaluator {
       task.ensureNotTerminated();
       timeSlotManager.reset();
       const operation = {};
-      let stop, i, ii, cs, name;
+      let stop, i, ii, cs, name, isValidName;
 
       while (!(stop = timeSlotManager.check())) {
         operation.args = null;
@@ -1326,9 +1403,10 @@ class PartialEvaluator {
 
         switch (fn | 0) {
           case _util.OPS.paintXObject:
+            isValidName = args[0] instanceof _primitives.Name;
             name = args[0].name;
 
-            if (name) {
+            if (isValidName) {
               const localImage = localImageCache.getByName(name);
 
               if (localImage) {
@@ -1339,7 +1417,7 @@ class PartialEvaluator {
             }
 
             next(new Promise(function (resolveXObject, rejectXObject) {
-              if (!name) {
+              if (!isValidName) {
                 throw new _util.FormatError("XObject must be referred to by name.");
               }
 
@@ -1611,7 +1689,7 @@ class PartialEvaluator {
             cs = stateManager.state.fillColorSpace;
 
             if (cs.name === "Pattern") {
-              next(self.handleColorN(operatorList, _util.OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache));
+              next(self.handleColorN(operatorList, _util.OPS.setFillColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache));
               return;
             }
 
@@ -1623,7 +1701,7 @@ class PartialEvaluator {
             cs = stateManager.state.strokeColorSpace;
 
             if (cs.name === "Pattern") {
-              next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache));
+              next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task, localColorSpaceCache, localTilingPatternCache, localShadingPatternCache));
               return;
             }
 
@@ -1644,17 +1722,22 @@ class PartialEvaluator {
               throw new _util.FormatError("No shading object found");
             }
 
-            var shadingFill = _pattern.Pattern.parseShading(shading, null, xref, resources, self.handler, self._pdfFunctionFactory, localColorSpaceCache);
-
-            var patternIR = shadingFill.getIR();
-            args = [patternIR];
+            const patternId = self.parseShading({
+              keyObj: shading,
+              shading,
+              resources,
+              localColorSpaceCache,
+              localShadingPatternCache
+            });
+            args = [patternId];
             fn = _util.OPS.shadingFill;
             break;
 
           case _util.OPS.setGState:
+            isValidName = args[0] instanceof _primitives.Name;
             name = args[0].name;
 
-            if (name) {
+            if (isValidName) {
               const localGStateObj = localGStateCache.getByName(name);
 
               if (localGStateObj) {
@@ -1668,7 +1751,7 @@ class PartialEvaluator {
             }
 
             next(new Promise(function (resolveGState, rejectGState) {
-              if (!name) {
+              if (!isValidName) {
                 throw new _util.FormatError("GState must be referred to by name.");
               }
 
@@ -1857,7 +1940,7 @@ class PartialEvaluator {
       const font = textState.font;
       const tsm = [textState.fontSize * textState.textHScale, 0, 0, textState.fontSize, 0, textState.textRise];
 
-      if (font.isType3Font && textState.fontSize <= 1 && !(0, _util.isArrayEqual)(textState.fontMatrix, _util.FONT_IDENTITY_MATRIX)) {
+      if (font.isType3Font && (textState.fontSize <= 1 || font.isCharBBox) && !(0, _util.isArrayEqual)(textState.fontMatrix, _util.FONT_IDENTITY_MATRIX)) {
         const glyphHeight = font.bbox[3] - font.bbox[1];
 
         if (glyphHeight > 0) {
@@ -1973,6 +2056,14 @@ class PartialEvaluator {
 
     function handleSetFont(fontName, fontRef) {
       return self.loadFont(fontName, fontRef, resources).then(function (translated) {
+        if (!translated.font.isType3Font) {
+          return translated;
+        }
+
+        return translated.loadType3Data(self, resources, task).catch(function () {}).then(function () {
+          return translated;
+        });
+      }).then(function (translated) {
         textState.font = translated.font;
         textState.fontMatrix = translated.font.fontMatrix || _util.FONT_IDENTITY_MATRIX;
       });
@@ -2444,14 +2535,15 @@ class PartialEvaluator {
               xobjs = resources.get("XObject") || _primitives.Dict.empty;
             }
 
+            var isValidName = args[0] instanceof _primitives.Name;
             var name = args[0].name;
 
-            if (name && emptyXObjectCache.getByName(name)) {
+            if (isValidName && emptyXObjectCache.getByName(name)) {
               break;
             }
 
             next(new Promise(function (resolveXObject, rejectXObject) {
-              if (!name) {
+              if (!isValidName) {
                 throw new _util.FormatError("XObject must be referred to by name.");
               }
 
@@ -2547,14 +2639,15 @@ class PartialEvaluator {
             return;
 
           case _util.OPS.setGState:
+            isValidName = args[0] instanceof _primitives.Name;
             name = args[0].name;
 
-            if (name && emptyGStateCache.getByName(name)) {
+            if (isValidName && emptyGStateCache.getByName(name)) {
               break;
             }
 
             next(new Promise(function (resolveGState, rejectGState) {
-              if (!name) {
+              if (!isValidName) {
                 throw new _util.FormatError("GState must be referred to by name.");
               }
 
@@ -2740,7 +2833,7 @@ class PartialEvaluator {
       if (isSymbolicFont) {
         encoding = _encodings.MacRomanEncoding;
 
-        if (!properties.file) {
+        if (!properties.file || properties.isInternalFont) {
           if (/Symbol/i.test(properties.name)) {
             encoding = _encodings.SymbolSetEncoding;
           } else if (/Dingbats|Wingdings/i.test(properties.name)) {
@@ -2770,7 +2863,7 @@ class PartialEvaluator {
     });
   }
 
-  _buildSimpleFontToUnicode(properties, forceGlyphs = false) {
+  _simpleFontToUnicode(properties, forceGlyphs = false) {
     (0, _util.assert)(!properties.composite, "Must be a simple font.");
     const toUnicode = [];
     const encoding = properties.defaultEncoding.slice();
@@ -2825,7 +2918,7 @@ class PartialEvaluator {
               code = +codeStr;
 
               if (Number.isNaN(code) && Number.isInteger(parseInt(codeStr, 16))) {
-                return this._buildSimpleFontToUnicode(properties, true);
+                return this._simpleFontToUnicode(properties, true);
               }
             }
 
@@ -2859,53 +2952,53 @@ class PartialEvaluator {
       toUnicode[charcode] = String.fromCharCode(glyphsUnicodeMap[glyphName]);
     }
 
-    return new _to_unicode_map.ToUnicodeMap(toUnicode);
+    return toUnicode;
   }
 
-  buildToUnicode(properties) {
+  async buildToUnicode(properties) {
     properties.hasIncludedToUnicodeMap = !!properties.toUnicode && properties.toUnicode.length > 0;
 
     if (properties.hasIncludedToUnicodeMap) {
       if (!properties.composite && properties.hasEncoding) {
-        properties.fallbackToUnicode = this._buildSimpleFontToUnicode(properties);
+        properties.fallbackToUnicode = this._simpleFontToUnicode(properties);
       }
 
-      return Promise.resolve(properties.toUnicode);
+      return properties.toUnicode;
     }
 
     if (!properties.composite) {
-      return Promise.resolve(this._buildSimpleFontToUnicode(properties));
+      return new _to_unicode_map.ToUnicodeMap(this._simpleFontToUnicode(properties));
     }
 
     if (properties.composite && (properties.cMap.builtInCMap && !(properties.cMap instanceof _cmap.IdentityCMap) || properties.cidSystemInfo.registry === "Adobe" && (properties.cidSystemInfo.ordering === "GB1" || properties.cidSystemInfo.ordering === "CNS1" || properties.cidSystemInfo.ordering === "Japan1" || properties.cidSystemInfo.ordering === "Korea1"))) {
-      const registry = properties.cidSystemInfo.registry;
-      const ordering = properties.cidSystemInfo.ordering;
+      const {
+        registry,
+        ordering
+      } = properties.cidSystemInfo;
 
-      const ucs2CMapName = _primitives.Name.get(registry + "-" + ordering + "-UCS2");
+      const ucs2CMapName = _primitives.Name.get(`${registry}-${ordering}-UCS2`);
 
-      return _cmap.CMapFactory.create({
+      const ucs2CMap = await _cmap.CMapFactory.create({
         encoding: ucs2CMapName,
         fetchBuiltInCMap: this._fetchBuiltInCMapBound,
         useCMap: null
-      }).then(function (ucs2CMap) {
-        const cMap = properties.cMap;
-        const toUnicode = [];
-        cMap.forEach(function (charcode, cid) {
-          if (cid > 0xffff) {
-            throw new _util.FormatError("Max size of CID is 65,535");
-          }
+      });
+      const toUnicode = [];
+      properties.cMap.forEach(function (charcode, cid) {
+        if (cid > 0xffff) {
+          throw new _util.FormatError("Max size of CID is 65,535");
+        }
 
-          const ucs2 = ucs2CMap.lookup(cid);
+        const ucs2 = ucs2CMap.lookup(cid);
 
-          if (ucs2) {
-            toUnicode[charcode] = String.fromCharCode((ucs2.charCodeAt(0) << 8) + ucs2.charCodeAt(1));
-          }
-        });
-        return new _to_unicode_map.ToUnicodeMap(toUnicode);
+        if (ucs2) {
+          toUnicode[charcode] = String.fromCharCode((ucs2.charCodeAt(0) << 8) + ucs2.charCodeAt(1));
+        }
       });
+      return new _to_unicode_map.ToUnicodeMap(toUnicode);
     }
 
-    return Promise.resolve(new _to_unicode_map.IdentityToUnicodeMap(properties.firstChar, properties.lastChar));
+    return new _to_unicode_map.IdentityToUnicodeMap(properties.firstChar, properties.lastChar);
   }
 
   readToUnicode(cmapObj) {
@@ -3327,15 +3420,28 @@ class PartialEvaluator {
         properties = {
           type,
           name: baseFontName,
+          loadedName: baseDict.loadedName,
           widths: metrics.widths,
           defaultWidth: metrics.defaultWidth,
           flags,
           firstChar,
           lastChar,
           toUnicode,
+          xHeight: 0,
+          capHeight: 0,
+          italicAngle: 0,
           isType3Font
         };
         const widths = dict.get("Widths");
+        const standardFontName = (0, _standard_fonts.getStandardFontName)(baseFontName);
+        let file = null;
+
+        if (standardFontName) {
+          properties.isStandardFont = true;
+          file = await this.fetchStandardFontData(standardFontName);
+          properties.isInternalFont = !!file;
+        }
+
         return this.extractDataStructures(dict, dict, properties).then(newProperties => {
           if (widths) {
             const glyphWidths = [];
@@ -3350,7 +3456,7 @@ class PartialEvaluator {
             newProperties.widths = this.buildCharCodeToWidth(metrics.widths, newProperties);
           }
 
-          return new _fonts.Font(baseFontName, null, newProperties);
+          return new _fonts.Font(baseFontName, file, newProperties);
         });
       }
     }
@@ -3398,6 +3504,10 @@ class PartialEvaluator {
       fontFile = new _stream.NullStream();
     }
 
+    let isStandardFont = false;
+    let isInternalFont = false;
+    let glyphScaleFactors = null;
+
     if (fontFile) {
       if (fontFile.dict) {
         const subtypeEntry = fontFile.dict.get("Subtype");
@@ -3410,6 +3520,25 @@ class PartialEvaluator {
         length2 = fontFile.dict.get("Length2");
         length3 = fontFile.dict.get("Length3");
       }
+    } else if (cssFontInfo) {
+      const standardFontName = (0, _xfa_fonts.getXfaFontName)(fontName.name);
+
+      if (standardFontName) {
+        cssFontInfo.fontFamily = `${cssFontInfo.fontFamily}-PdfJS-XFA`;
+        cssFontInfo.lineHeight = standardFontName.lineHeight || null;
+        glyphScaleFactors = standardFontName.factors || null;
+        fontFile = await this.fetchStandardFontData(standardFontName.name);
+        isInternalFont = !!fontFile;
+        type = "TrueType";
+      }
+    } else if (!isType3Font) {
+      const standardFontName = (0, _standard_fonts.getStandardFontName)(fontName.name);
+
+      if (standardFontName) {
+        isStandardFont = true;
+        fontFile = await this.fetchStandardFontData(standardFontName);
+        isInternalFont = !!fontFile;
+      }
     }
 
     properties = {
@@ -3420,6 +3549,8 @@ class PartialEvaluator {
       length1,
       length2,
       length3,
+      isStandardFont,
+      isInternalFont,
       loadedName: baseDict.loadedName,
       composite,
       fixedPitch: false,
@@ -3427,15 +3558,16 @@ class PartialEvaluator {
       firstChar,
       lastChar,
       toUnicode,
-      bbox: descriptor.getArray("FontBBox"),
+      bbox: descriptor.getArray("FontBBox") || dict.getArray("FontBBox"),
       ascent: descriptor.get("Ascent"),
       descent: descriptor.get("Descent"),
-      xHeight: descriptor.get("XHeight"),
-      capHeight: descriptor.get("CapHeight"),
+      xHeight: descriptor.get("XHeight") || 0,
+      capHeight: descriptor.get("CapHeight") || 0,
       flags: descriptor.get("Flags"),
-      italicAngle: descriptor.get("ItalicAngle"),
+      italicAngle: descriptor.get("ItalicAngle") || 0,
       isType3Font,
-      cssFontInfo
+      cssFontInfo,
+      scaleFactors: glyphScaleFactors
     };
 
     if (composite) {
@@ -3549,9 +3681,9 @@ class TranslatedFont {
       throw new Error("Must be a Type3 font.");
     }
 
-    const type3Options = Object.create(evaluator.options);
-    type3Options.ignoreErrors = false;
-    const type3Evaluator = evaluator.clone(type3Options);
+    const type3Evaluator = evaluator.clone({
+      ignoreErrors: false
+    });
     type3Evaluator.parsingType3Font = true;
     const translatedFont = this.font,
           type3Dependencies = this.type3Dependencies;
@@ -3559,6 +3691,7 @@ class TranslatedFont {
     const charProcs = this.dict.get("CharProcs");
     const fontResources = this.dict.get("Resources") || resources;
     const charProcOperatorList = Object.create(null);
+    const isEmptyBBox = !translatedFont.bbox || (0, _util.isArrayEqual)(translatedFont.bbox, [0, 0, 0, 0]);
 
     for (const key of charProcs.getKeys()) {
       loadCharProcsPromise = loadCharProcsPromise.then(() => {
@@ -3571,7 +3704,7 @@ class TranslatedFont {
           operatorList
         }).then(() => {
           if (operatorList.fnArray[0] === _util.OPS.setCharWidthAndBounds) {
-            this._removeType3ColorOperators(operatorList);
+            this._removeType3ColorOperators(operatorList, isEmptyBBox);
           }
 
           charProcOperatorList[key] = operatorList.getIR();
@@ -3587,13 +3720,31 @@ class TranslatedFont {
       });
     }
 
-    this.type3Loaded = loadCharProcsPromise.then(function () {
+    this.type3Loaded = loadCharProcsPromise.then(() => {
       translatedFont.charProcOperatorList = charProcOperatorList;
+
+      if (this._bbox) {
+        translatedFont.isCharBBox = true;
+        translatedFont.bbox = this._bbox;
+      }
     });
     return this.type3Loaded;
   }
 
-  _removeType3ColorOperators(operatorList) {
+  _removeType3ColorOperators(operatorList, isEmptyBBox = false) {
+    if (isEmptyBBox) {
+      if (!this._bbox) {
+        this._bbox = [Infinity, Infinity, -Infinity, -Infinity];
+      }
+
+      const charBBox = _util.Util.normalizeRect(operatorList.argsArray[0].slice(2));
+
+      this._bbox[0] = Math.min(this._bbox[0], charBBox[0]);
+      this._bbox[1] = Math.min(this._bbox[1], charBBox[1]);
+      this._bbox[2] = Math.max(this._bbox[2], charBBox[2]);
+      this._bbox[3] = Math.max(this._bbox[3], charBBox[3]);
+    }
+
     let i = 1,
         ii = operatorList.length;
 

+ 132 - 25
lib/core/fonts.js

@@ -46,6 +46,8 @@ var _cff_font = require("./cff_font.js");
 
 var _font_renderer = require("./font_renderer.js");
 
+var _glyf = require("./glyf.js");
+
 var _cmap = require("./cmap.js");
 
 var _opentype_file_builder = require("./opentype_file_builder.js");
@@ -81,7 +83,7 @@ function adjustWidths(properties) {
 }
 
 function adjustToUnicode(properties, builtInEncoding) {
-  if (properties.hasIncludedToUnicodeMap) {
+  if (properties.isInternalFont) {
     return;
   }
 
@@ -97,8 +99,14 @@ function adjustToUnicode(properties, builtInEncoding) {
         glyphsUnicodeMap = (0, _glyphlist.getGlyphsUnicode)();
 
   for (const charCode in builtInEncoding) {
-    if (properties.hasEncoding && properties.differences[charCode] !== undefined) {
-      continue;
+    if (properties.hasIncludedToUnicodeMap) {
+      if (properties.toUnicode.has(charCode)) {
+        continue;
+      }
+    } else {
+      if (properties.hasEncoding && properties.differences[charCode] !== undefined) {
+        continue;
+      }
     }
 
     const glyphName = builtInEncoding[charCode];
@@ -109,11 +117,38 @@ function adjustToUnicode(properties, builtInEncoding) {
     }
   }
 
-  properties.toUnicode.amend(toUnicode);
+  if (toUnicode.length > 0) {
+    properties.toUnicode.amend(toUnicode);
+  }
+}
+
+function amendFallbackToUnicode(properties) {
+  if (!properties.fallbackToUnicode) {
+    return;
+  }
+
+  if (properties.toUnicode instanceof _to_unicode_map.IdentityToUnicodeMap) {
+    return;
+  }
+
+  const toUnicode = [];
+
+  for (const charCode in properties.fallbackToUnicode) {
+    if (properties.toUnicode.has(charCode)) {
+      continue;
+    }
+
+    toUnicode[charCode] = properties.fallbackToUnicode[charCode];
+  }
+
+  if (toUnicode.length > 0) {
+    properties.toUnicode.amend(toUnicode);
+  }
 }
 
 class Glyph {
-  constructor(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
+  constructor(originalCharCode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
+    this.originalCharCode = originalCharCode;
     this.fontChar = fontChar;
     this.unicode = unicode;
     this.accent = accent;
@@ -124,8 +159,8 @@ class Glyph {
     this.isInFont = isInFont;
   }
 
-  matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
-    return this.fontChar === fontChar && this.unicode === unicode && this.accent === accent && this.width === width && this.vmetric === vmetric && this.operatorListId === operatorListId && this.isSpace === isSpace && this.isInFont === isInFont;
+  matchesForCache(originalCharCode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont) {
+    return this.originalCharCode === originalCharCode && this.fontChar === fontChar && this.unicode === unicode && this.accent === accent && this.width === width && this.vmetric === vmetric && this.operatorListId === operatorListId && this.isSpace === isSpace && this.isInFont === isInFont;
   }
 
 }
@@ -556,7 +591,7 @@ function createOS2Table(properties, charstrings, override) {
 
 function createPostTable(properties) {
   const angle = Math.floor(properties.italicAngle * 2 ** 16);
-  return "\x00\x03\x00\x00" + (0, _util.string32)(angle) + "\x00\x00" + "\x00\x00" + (0, _util.string32)(properties.fixedPitch) + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00";
+  return "\x00\x03\x00\x00" + (0, _util.string32)(angle) + "\x00\x00" + "\x00\x00" + (0, _util.string32)(properties.fixedPitch ? 1 : 0) + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00";
 }
 
 function createPostscriptName(name) {
@@ -609,6 +644,7 @@ function createNameTable(name, proto) {
 class Font {
   constructor(name, file, properties) {
     this.name = name;
+    this.psName = null;
     this.mimetype = null;
     this.disableFontFace = false;
     this.loadedName = properties.loadedName;
@@ -641,11 +677,11 @@ class Font {
     this.capHeight = properties.capHeight / PDF_GLYPH_SPACE_UNITS;
     this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS;
     this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS;
+    this.lineHeight = this.ascent - this.descent;
     this.fontMatrix = properties.fontMatrix;
     this.bbox = properties.bbox;
     this.defaultEncoding = properties.defaultEncoding;
     this.toUnicode = properties.toUnicode;
-    this.fallbackToUnicode = properties.fallbackToUnicode || new _to_unicode_map.ToUnicodeMap();
     this.toFontChar = [];
 
     if (properties.type === "Type3") {
@@ -717,8 +753,9 @@ class Font {
       return;
     }
 
+    amendFallbackToUnicode(properties);
     this.data = data;
-    this.fontType = (0, _fonts_utils.getFontType)(type, subtype);
+    this.fontType = (0, _fonts_utils.getFontType)(type, subtype, properties.isStandardFont);
     this.fontMatrix = properties.fontMatrix;
     this.widths = properties.widths;
     this.defaultWidth = properties.defaultWidth;
@@ -753,7 +790,7 @@ class Font {
     const name = this.name;
     const type = this.type;
     const subtype = this.subtype;
-    let fontName = name.replace(/[,_]/g, "-").replace(/\s/g, "");
+    let fontName = (0, _fonts_utils.normalizeFontName)(name);
     const stdFontMap = (0, _standard_fonts.getStdFontMap)(),
           nonStdFontMap = (0, _standard_fonts.getNonStdFontMap)();
     const isStandardFont = !!stdFontMap[fontName];
@@ -847,8 +884,9 @@ class Font {
       this.toFontChar = map;
     }
 
+    amendFallbackToUnicode(properties);
     this.loadedName = fontName.split("-")[0];
-    this.fontType = (0, _fonts_utils.getFontType)(type, subtype);
+    this.fontType = (0, _fonts_utils.getFontType)(type, subtype, properties.isStandardFont);
   }
 
   checkAndRepair(name, font, properties) {
@@ -1054,7 +1092,20 @@ class Font {
           }
         } else if (isSymbolicFont && platformId === 3 && encodingId === 0) {
           useTable = true;
-          canBreak = true;
+          let correctlySorted = true;
+
+          if (i < numTables - 1) {
+            const nextBytes = file.peekBytes(2),
+                  nextPlatformId = int16(nextBytes[0], nextBytes[1]);
+
+            if (nextPlatformId < platformId) {
+              correctlySorted = false;
+            }
+          }
+
+          if (correctlySorted) {
+            canBreak = true;
+          }
         }
 
         if (useTable) {
@@ -1211,7 +1262,7 @@ class Font {
       };
     }
 
-    function sanitizeMetrics(file, header, metrics, numGlyphs, dupFirstEntry) {
+    function sanitizeMetrics(file, header, metrics, headTable, numGlyphs, dupFirstEntry) {
       if (!header) {
         if (metrics) {
           metrics.data = null;
@@ -1231,13 +1282,22 @@ class Font {
       file.pos += 2;
       file.pos += 2;
       file.pos += 2;
-      file.pos += 2;
+      const caretOffset = file.getUint16();
       file.pos += 8;
       file.pos += 2;
       let numOfMetrics = file.getUint16();
 
+      if (caretOffset !== 0) {
+        const macStyle = int16(headTable.data[44], headTable.data[45]);
+
+        if (!(macStyle & 2)) {
+          header.data[22] = 0;
+          header.data[23] = 0;
+        }
+      }
+
       if (numOfMetrics > numGlyphs) {
-        (0, _util.info)("The numOfMetrics (" + numOfMetrics + ") should not be " + "greater than the numGlyphs (" + numGlyphs + ")");
+        (0, _util.info)(`The numOfMetrics (${numOfMetrics}) should not be ` + `greater than the numGlyphs (${numGlyphs}).`);
         numOfMetrics = numGlyphs;
         header.data[34] = (numOfMetrics & 0xff00) >> 8;
         header.data[35] = numOfMetrics & 0x00ff;
@@ -2007,6 +2067,44 @@ class Font {
     font.pos = (font.start || 0) + tables.maxp.offset;
     const version = font.getInt32();
     const numGlyphs = font.getUint16();
+
+    if (properties.scaleFactors && properties.scaleFactors.length === numGlyphs && isTrueType) {
+      const {
+        scaleFactors
+      } = properties;
+      const isGlyphLocationsLong = int16(tables.head.data[50], tables.head.data[51]);
+      const glyphs = new _glyf.GlyfTable({
+        glyfTable: tables.glyf.data,
+        isGlyphLocationsLong,
+        locaTable: tables.loca.data,
+        numGlyphs
+      });
+      glyphs.scale(scaleFactors);
+      const {
+        glyf,
+        loca,
+        isLocationLong
+      } = glyphs.write();
+      tables.glyf.data = glyf;
+      tables.loca.data = loca;
+
+      if (isLocationLong !== !!isGlyphLocationsLong) {
+        tables.head.data[50] = 0;
+        tables.head.data[51] = isLocationLong ? 1 : 0;
+      }
+
+      const metrics = tables.hmtx.data;
+
+      for (let i = 0; i < numGlyphs; i++) {
+        const j = 4 * i;
+        const advanceWidth = Math.round(scaleFactors[i] * int16(metrics[j], metrics[j + 1]));
+        metrics[j] = advanceWidth >> 8 & 0xff;
+        metrics[j + 1] = advanceWidth & 0xff;
+        const lsb = Math.round(scaleFactors[i] * signedInt16(metrics[j + 2], metrics[j + 3]));
+        writeSignedInt16(metrics, j + 2, lsb);
+      }
+    }
+
     let numGlyphsOut = numGlyphs + 1;
     let dupFirstEntry = true;
 
@@ -2044,7 +2142,7 @@ class Font {
       delete tables["cvt "];
     }
 
-    sanitizeMetrics(font, tables.hhea, tables.hmtx, numGlyphsOut, dupFirstEntry);
+    sanitizeMetrics(font, tables.hhea, tables.hmtx, tables.head, numGlyphsOut, dupFirstEntry);
 
     if (!tables.head) {
       throw new _util.FormatError('Required "head" table is not found');
@@ -2077,11 +2175,19 @@ class Font {
       unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
       yMax: int16(tables.head.data[42], tables.head.data[43]),
       yMin: signedInt16(tables.head.data[38], tables.head.data[39]),
-      ascent: int16(tables.hhea.data[4], tables.hhea.data[5]),
-      descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7])
+      ascent: signedInt16(tables.hhea.data[4], tables.hhea.data[5]),
+      descent: signedInt16(tables.hhea.data[6], tables.hhea.data[7]),
+      lineGap: signedInt16(tables.hhea.data[8], tables.hhea.data[9])
     };
     this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
     this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
+    this.lineGap = metricsOverride.lineGap / metricsOverride.unitsPerEm;
+
+    if (this.cssFontInfo && this.cssFontInfo.lineHeight) {
+      this.lineHeight = this.cssFontInfo.lineHeight;
+    } else {
+      this.lineHeight = this.ascent - this.descent + this.lineGap;
+    }
 
     if (tables.post) {
       readPostScriptTable(tables.post, properties, numGlyphs);
@@ -2135,9 +2241,9 @@ class Font {
         for (let charCode = 0; charCode < 256; charCode++) {
           let glyphName;
 
-          if (this.differences && charCode in this.differences) {
+          if (this.differences[charCode] !== undefined) {
             glyphName = this.differences[charCode];
-          } else if (charCode in baseEncoding && baseEncoding[charCode] !== "") {
+          } else if (baseEncoding[charCode] !== "") {
             glyphName = baseEncoding[charCode];
           } else {
             glyphName = _encodings.StandardEncoding[charCode];
@@ -2249,6 +2355,7 @@ class Font {
     } else {
       const namePrototype = readNameTable(tables.name);
       tables.name.data = createNameTable(name, namePrototype);
+      this.psName = namePrototype[0][6] || null;
     }
 
     const builder = new _opentype_file_builder.OpenTypeFileBuilder(header.version);
@@ -2440,13 +2547,13 @@ class Font {
     width = this.widths[widthCode];
     width = (0, _util.isNum)(width) ? width : this.defaultWidth;
     const vmetric = this.vmetrics && this.vmetrics[widthCode];
-    let unicode = this.toUnicode.get(charcode) || this.fallbackToUnicode.get(charcode) || charcode;
+    let unicode = this.toUnicode.get(charcode) || charcode;
 
     if (typeof unicode === "number") {
       unicode = String.fromCharCode(unicode);
     }
 
-    let isInFont = (charcode in this.toFontChar);
+    let isInFont = this.toFontChar[charcode] !== undefined;
     fontCharCode = this.toFontChar[charcode] || charcode;
 
     if (this.missingFile) {
@@ -2487,8 +2594,8 @@ class Font {
 
     let glyph = this._glyphCache[charcode];
 
-    if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) {
-      glyph = new Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);
+    if (!glyph || !glyph.matchesForCache(charcode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) {
+      glyph = new Glyph(charcode, fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);
       this._glyphCache[charcode] = glyph;
     }
 

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


+ 375 - 473
lib/core/function.js

@@ -152,506 +152,410 @@ function toNumberArray(arr) {
   return arr;
 }
 
-const PDFFunction = function PDFFunctionClosure() {
-  const CONSTRUCT_SAMPLED = 0;
-  const CONSTRUCT_INTERPOLATED = 2;
-  const CONSTRUCT_STICHED = 3;
-  const CONSTRUCT_POSTSCRIPT = 4;
-  return {
-    getSampleArray(size, outputSize, bps, stream) {
-      let i, ii;
-      let length = 1;
-
-      for (i = 0, ii = size.length; i < ii; i++) {
-        length *= size[i];
-      }
+class PDFFunction {
+  static getSampleArray(size, outputSize, bps, stream) {
+    let i, ii;
+    let length = 1;
 
-      length *= outputSize;
-      const array = new Array(length);
-      let codeSize = 0;
-      let codeBuf = 0;
-      const sampleMul = 1.0 / (2.0 ** bps - 1);
-      const strBytes = stream.getBytes((length * bps + 7) / 8);
-      let strIdx = 0;
-
-      for (i = 0; i < length; i++) {
-        while (codeSize < bps) {
-          codeBuf <<= 8;
-          codeBuf |= strBytes[strIdx++];
-          codeSize += 8;
-        }
+    for (i = 0, ii = size.length; i < ii; i++) {
+      length *= size[i];
+    }
 
-        codeSize -= bps;
-        array[i] = (codeBuf >> codeSize) * sampleMul;
-        codeBuf &= (1 << codeSize) - 1;
+    length *= outputSize;
+    const array = new Array(length);
+    let codeSize = 0;
+    let codeBuf = 0;
+    const sampleMul = 1.0 / (2.0 ** bps - 1);
+    const strBytes = stream.getBytes((length * bps + 7) / 8);
+    let strIdx = 0;
+
+    for (i = 0; i < length; i++) {
+      while (codeSize < bps) {
+        codeBuf <<= 8;
+        codeBuf |= strBytes[strIdx++];
+        codeSize += 8;
       }
 
-      return array;
-    },
+      codeSize -= bps;
+      array[i] = (codeBuf >> codeSize) * sampleMul;
+      codeBuf &= (1 << codeSize) - 1;
+    }
 
-    getIR({
-      xref,
-      isEvalSupported,
-      fn
-    }) {
-      let dict = fn.dict;
+    return array;
+  }
 
-      if (!dict) {
-        dict = fn;
-      }
+  static parse({
+    xref,
+    isEvalSupported,
+    fn
+  }) {
+    const dict = fn.dict || fn;
+    const typeNum = dict.get("FunctionType");
 
-      const types = [this.constructSampled, null, this.constructInterpolated, this.constructStiched, this.constructPostScript];
-      const typeNum = dict.get("FunctionType");
-      const typeFn = types[typeNum];
+    switch (typeNum) {
+      case 0:
+        return this.constructSampled({
+          xref,
+          isEvalSupported,
+          fn,
+          dict
+        });
 
-      if (!typeFn) {
-        throw new _util.FormatError("Unknown type of function");
-      }
+      case 1:
+        break;
 
-      return typeFn.call(this, {
-        xref,
-        isEvalSupported,
-        fn,
-        dict
-      });
-    },
-
-    fromIR({
-      xref,
-      isEvalSupported,
-      IR
-    }) {
-      const type = IR[0];
-
-      switch (type) {
-        case CONSTRUCT_SAMPLED:
-          return this.constructSampledFromIR({
-            xref,
-            isEvalSupported,
-            IR
-          });
-
-        case CONSTRUCT_INTERPOLATED:
-          return this.constructInterpolatedFromIR({
-            xref,
-            isEvalSupported,
-            IR
-          });
-
-        case CONSTRUCT_STICHED:
-          return this.constructStichedFromIR({
-            xref,
-            isEvalSupported,
-            IR
-          });
+      case 2:
+        return this.constructInterpolated({
+          xref,
+          isEvalSupported,
+          dict
+        });
 
-        default:
-          return this.constructPostScriptFromIR({
-            xref,
-            isEvalSupported,
-            IR
-          });
-      }
-    },
-
-    parse({
-      xref,
-      isEvalSupported,
-      fn
-    }) {
-      const IR = this.getIR({
+      case 3:
+        return this.constructStiched({
+          xref,
+          isEvalSupported,
+          dict
+        });
+
+      case 4:
+        return this.constructPostScript({
+          xref,
+          isEvalSupported,
+          fn,
+          dict
+        });
+    }
+
+    throw new _util.FormatError("Unknown type of function");
+  }
+
+  static parseArray({
+    xref,
+    isEvalSupported,
+    fnObj
+  }) {
+    if (!Array.isArray(fnObj)) {
+      return this.parse({
         xref,
         isEvalSupported,
-        fn
+        fn: fnObj
       });
-      return this.fromIR({
+    }
+
+    const fnArray = [];
+
+    for (let j = 0, jj = fnObj.length; j < jj; j++) {
+      fnArray.push(this.parse({
         xref,
         isEvalSupported,
-        IR
-      });
-    },
-
-    parseArray({
-      xref,
-      isEvalSupported,
-      fnObj
-    }) {
-      if (!Array.isArray(fnObj)) {
-        return this.parse({
-          xref,
-          isEvalSupported,
-          fn: fnObj
-        });
+        fn: xref.fetchIfRef(fnObj[j])
+      }));
+    }
+
+    return function (src, srcOffset, dest, destOffset) {
+      for (let i = 0, ii = fnArray.length; i < ii; i++) {
+        fnArray[i](src, srcOffset, dest, destOffset + i);
       }
+    };
+  }
 
-      const fnArray = [];
+  static constructSampled({
+    xref,
+    isEvalSupported,
+    fn,
+    dict
+  }) {
+    function toMultiArray(arr) {
+      const inputLength = arr.length;
+      const out = [];
+      let index = 0;
 
-      for (let j = 0, jj = fnObj.length; j < jj; j++) {
-        fnArray.push(this.parse({
-          xref,
-          isEvalSupported,
-          fn: xref.fetchIfRef(fnObj[j])
-        }));
+      for (let i = 0; i < inputLength; i += 2) {
+        out[index++] = [arr[i], arr[i + 1]];
       }
 
-      return function (src, srcOffset, dest, destOffset) {
-        for (let i = 0, ii = fnArray.length; i < ii; i++) {
-          fnArray[i](src, srcOffset, dest, destOffset + i);
-        }
-      };
-    },
-
-    constructSampled({
-      xref,
-      isEvalSupported,
-      fn,
-      dict
-    }) {
-      function toMultiArray(arr) {
-        const inputLength = arr.length;
-        const out = [];
-        let index = 0;
-
-        for (let i = 0; i < inputLength; i += 2) {
-          out[index] = [arr[i], arr[i + 1]];
-          ++index;
-        }
+      return out;
+    }
 
-        return out;
-      }
+    function interpolate(x, xmin, xmax, ymin, ymax) {
+      return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));
+    }
 
-      let domain = toNumberArray(dict.getArray("Domain"));
-      let range = toNumberArray(dict.getArray("Range"));
+    let domain = toNumberArray(dict.getArray("Domain"));
+    let range = toNumberArray(dict.getArray("Range"));
 
-      if (!domain || !range) {
-        throw new _util.FormatError("No domain or range");
-      }
+    if (!domain || !range) {
+      throw new _util.FormatError("No domain or range");
+    }
 
-      const inputSize = domain.length / 2;
-      const outputSize = range.length / 2;
-      domain = toMultiArray(domain);
-      range = toMultiArray(range);
-      const size = toNumberArray(dict.getArray("Size"));
-      const bps = dict.get("BitsPerSample");
-      const order = dict.get("Order") || 1;
+    const inputSize = domain.length / 2;
+    const outputSize = range.length / 2;
+    domain = toMultiArray(domain);
+    range = toMultiArray(range);
+    const size = toNumberArray(dict.getArray("Size"));
+    const bps = dict.get("BitsPerSample");
+    const order = dict.get("Order") || 1;
 
-      if (order !== 1) {
-        (0, _util.info)("No support for cubic spline interpolation: " + order);
-      }
+    if (order !== 1) {
+      (0, _util.info)("No support for cubic spline interpolation: " + order);
+    }
 
-      let encode = toNumberArray(dict.getArray("Encode"));
+    let encode = toNumberArray(dict.getArray("Encode"));
 
-      if (!encode) {
-        encode = [];
+    if (!encode) {
+      encode = [];
 
-        for (let i = 0; i < inputSize; ++i) {
-          encode.push([0, size[i] - 1]);
-        }
-      } else {
-        encode = toMultiArray(encode);
+      for (let i = 0; i < inputSize; ++i) {
+        encode.push([0, size[i] - 1]);
       }
+    } else {
+      encode = toMultiArray(encode);
+    }
 
-      let decode = toNumberArray(dict.getArray("Decode"));
+    let decode = toNumberArray(dict.getArray("Decode"));
 
-      if (!decode) {
-        decode = range;
-      } else {
-        decode = toMultiArray(decode);
-      }
+    if (!decode) {
+      decode = range;
+    } else {
+      decode = toMultiArray(decode);
+    }
+
+    const samples = this.getSampleArray(size, outputSize, bps, fn);
+    return function constructSampledFn(src, srcOffset, dest, destOffset) {
+      const cubeVertices = 1 << inputSize;
+      const cubeN = new Float64Array(cubeVertices);
+      const cubeVertex = new Uint32Array(cubeVertices);
+      let i, j;
 
-      const samples = this.getSampleArray(size, outputSize, bps, fn);
-      return [CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, outputSize, 2 ** bps - 1, range];
-    },
-
-    constructSampledFromIR({
-      xref,
-      isEvalSupported,
-      IR
-    }) {
-      function interpolate(x, xmin, xmax, ymin, ymax) {
-        return ymin + (x - xmin) * ((ymax - ymin) / (xmax - xmin));
+      for (j = 0; j < cubeVertices; j++) {
+        cubeN[j] = 1;
       }
 
-      return function constructSampledFromIRResult(src, srcOffset, dest, destOffset) {
-        const m = IR[1];
-        const domain = IR[2];
-        const encode = IR[3];
-        const decode = IR[4];
-        const samples = IR[5];
-        const size = IR[6];
-        const n = IR[7];
-        const range = IR[9];
-        const cubeVertices = 1 << m;
-        const cubeN = new Float64Array(cubeVertices);
-        const cubeVertex = new Uint32Array(cubeVertices);
-        let i, j;
+      let k = outputSize,
+          pos = 1;
+
+      for (i = 0; i < inputSize; ++i) {
+        const domain_2i = domain[i][0];
+        const domain_2i_1 = domain[i][1];
+        const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);
+        let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);
+        const size_i = size[i];
+        e = Math.min(Math.max(e, 0), size_i - 1);
+        const e0 = e < size_i - 1 ? Math.floor(e) : e - 1;
+        const n0 = e0 + 1 - e;
+        const n1 = e - e0;
+        const offset0 = e0 * k;
+        const offset1 = offset0 + k;
 
         for (j = 0; j < cubeVertices; j++) {
-          cubeN[j] = 1;
-        }
-
-        let k = n,
-            pos = 1;
-
-        for (i = 0; i < m; ++i) {
-          const domain_2i = domain[i][0];
-          const domain_2i_1 = domain[i][1];
-          const xi = Math.min(Math.max(src[srcOffset + i], domain_2i), domain_2i_1);
-          let e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]);
-          const size_i = size[i];
-          e = Math.min(Math.max(e, 0), size_i - 1);
-          const e0 = e < size_i - 1 ? Math.floor(e) : e - 1;
-          const n0 = e0 + 1 - e;
-          const n1 = e - e0;
-          const offset0 = e0 * k;
-          const offset1 = offset0 + k;
-
-          for (j = 0; j < cubeVertices; j++) {
-            if (j & pos) {
-              cubeN[j] *= n1;
-              cubeVertex[j] += offset1;
-            } else {
-              cubeN[j] *= n0;
-              cubeVertex[j] += offset0;
-            }
+          if (j & pos) {
+            cubeN[j] *= n1;
+            cubeVertex[j] += offset1;
+          } else {
+            cubeN[j] *= n0;
+            cubeVertex[j] += offset0;
           }
-
-          k *= size_i;
-          pos <<= 1;
         }
 
-        for (j = 0; j < n; ++j) {
-          let rj = 0;
+        k *= size_i;
+        pos <<= 1;
+      }
 
-          for (i = 0; i < cubeVertices; i++) {
-            rj += samples[cubeVertex[i] + j] * cubeN[i];
-          }
+      for (j = 0; j < outputSize; ++j) {
+        let rj = 0;
 
-          rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
-          dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
+        for (i = 0; i < cubeVertices; i++) {
+          rj += samples[cubeVertex[i] + j] * cubeN[i];
         }
-      };
-    },
-
-    constructInterpolated({
-      xref,
-      isEvalSupported,
-      fn,
-      dict
-    }) {
-      const c0 = toNumberArray(dict.getArray("C0")) || [0];
-      const c1 = toNumberArray(dict.getArray("C1")) || [1];
-      const n = dict.get("N");
-      const length = c0.length;
-      const diff = [];
-
-      for (let i = 0; i < length; ++i) {
-        diff.push(c1[i] - c0[i]);
-      }
 
-      return [CONSTRUCT_INTERPOLATED, c0, diff, n];
-    },
-
-    constructInterpolatedFromIR({
-      xref,
-      isEvalSupported,
-      IR
-    }) {
-      const c0 = IR[1];
-      const diff = IR[2];
-      const n = IR[3];
-      const length = diff.length;
-      return function constructInterpolatedFromIRResult(src, srcOffset, dest, destOffset) {
-        const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
-
-        for (let j = 0; j < length; ++j) {
-          dest[destOffset + j] = c0[j] + x * diff[j];
-        }
-      };
-    },
-
-    constructStiched({
-      xref,
-      isEvalSupported,
-      fn,
-      dict
-    }) {
-      const domain = toNumberArray(dict.getArray("Domain"));
-
-      if (!domain) {
-        throw new _util.FormatError("No domain");
+        rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]);
+        dest[destOffset + j] = Math.min(Math.max(rj, range[j][0]), range[j][1]);
       }
+    };
+  }
+
+  static constructInterpolated({
+    xref,
+    isEvalSupported,
+    dict
+  }) {
+    const c0 = toNumberArray(dict.getArray("C0")) || [0];
+    const c1 = toNumberArray(dict.getArray("C1")) || [1];
+    const n = dict.get("N");
+    const diff = [];
+
+    for (let i = 0, ii = c0.length; i < ii; ++i) {
+      diff.push(c1[i] - c0[i]);
+    }
 
-      const inputSize = domain.length / 2;
+    const length = diff.length;
+    return function constructInterpolatedFn(src, srcOffset, dest, destOffset) {
+      const x = n === 1 ? src[srcOffset] : src[srcOffset] ** n;
 
-      if (inputSize !== 1) {
-        throw new _util.FormatError("Bad domain for stiched function");
+      for (let j = 0; j < length; ++j) {
+        dest[destOffset + j] = c0[j] + x * diff[j];
       }
+    };
+  }
 
-      const fnRefs = dict.get("Functions");
-      const fns = [];
+  static constructStiched({
+    xref,
+    isEvalSupported,
+    dict
+  }) {
+    const domain = toNumberArray(dict.getArray("Domain"));
 
-      for (let i = 0, ii = fnRefs.length; i < ii; ++i) {
-        fns.push(this.parse({
-          xref,
-          isEvalSupported,
-          fn: xref.fetchIfRef(fnRefs[i])
-        }));
-      }
+    if (!domain) {
+      throw new _util.FormatError("No domain");
+    }
 
-      const bounds = toNumberArray(dict.getArray("Bounds"));
-      const encode = toNumberArray(dict.getArray("Encode"));
-      return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
-    },
-
-    constructStichedFromIR({
-      xref,
-      isEvalSupported,
-      IR
-    }) {
-      const domain = IR[1];
-      const bounds = IR[2];
-      const encode = IR[3];
-      const fns = IR[4];
-      const tmpBuf = new Float32Array(1);
-      return function constructStichedFromIRResult(src, srcOffset, dest, destOffset) {
-        const clip = function constructStichedFromIRClip(v, min, max) {
-          if (v > max) {
-            v = max;
-          } else if (v < min) {
-            v = min;
-          }
+    const inputSize = domain.length / 2;
 
-          return v;
-        };
+    if (inputSize !== 1) {
+      throw new _util.FormatError("Bad domain for stiched function");
+    }
 
-        const v = clip(src[srcOffset], domain[0], domain[1]);
-        const length = bounds.length;
-        let i;
+    const fnRefs = dict.get("Functions");
+    const fns = [];
 
-        for (i = 0; i < length; ++i) {
-          if (v < bounds[i]) {
-            break;
-          }
+    for (let i = 0, ii = fnRefs.length; i < ii; ++i) {
+      fns.push(this.parse({
+        xref,
+        isEvalSupported,
+        fn: xref.fetchIfRef(fnRefs[i])
+      }));
+    }
+
+    const bounds = toNumberArray(dict.getArray("Bounds"));
+    const encode = toNumberArray(dict.getArray("Encode"));
+    const tmpBuf = new Float32Array(1);
+    return function constructStichedFn(src, srcOffset, dest, destOffset) {
+      const clip = function constructStichedFromIRClip(v, min, max) {
+        if (v > max) {
+          v = max;
+        } else if (v < min) {
+          v = min;
         }
 
-        let dmin = domain[0];
+        return v;
+      };
+
+      const v = clip(src[srcOffset], domain[0], domain[1]);
+      const length = bounds.length;
+      let i;
 
-        if (i > 0) {
-          dmin = bounds[i - 1];
+      for (i = 0; i < length; ++i) {
+        if (v < bounds[i]) {
+          break;
         }
+      }
 
-        let dmax = domain[1];
+      let dmin = domain[0];
 
-        if (i < bounds.length) {
-          dmax = bounds[i];
-        }
+      if (i > 0) {
+        dmin = bounds[i - 1];
+      }
 
-        const rmin = encode[2 * i];
-        const rmax = encode[2 * i + 1];
-        tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
-        fns[i](tmpBuf, 0, dest, destOffset);
-      };
-    },
-
-    constructPostScript({
-      xref,
-      isEvalSupported,
-      fn,
-      dict
-    }) {
-      const domain = toNumberArray(dict.getArray("Domain"));
-      const range = toNumberArray(dict.getArray("Range"));
-
-      if (!domain) {
-        throw new _util.FormatError("No domain.");
+      let dmax = domain[1];
+
+      if (i < bounds.length) {
+        dmax = bounds[i];
       }
 
-      if (!range) {
-        throw new _util.FormatError("No range.");
+      const rmin = encode[2 * i];
+      const rmax = encode[2 * i + 1];
+      tmpBuf[0] = dmin === dmax ? rmin : rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin);
+      fns[i](tmpBuf, 0, dest, destOffset);
+    };
+  }
+
+  static constructPostScript({
+    xref,
+    isEvalSupported,
+    fn,
+    dict
+  }) {
+    const domain = toNumberArray(dict.getArray("Domain"));
+    const range = toNumberArray(dict.getArray("Range"));
+
+    if (!domain) {
+      throw new _util.FormatError("No domain.");
+    }
+
+    if (!range) {
+      throw new _util.FormatError("No range.");
+    }
+
+    const lexer = new _ps_parser.PostScriptLexer(fn);
+    const parser = new _ps_parser.PostScriptParser(lexer);
+    const code = parser.parse();
+
+    if (isEvalSupported && _util.IsEvalSupportedCached.value) {
+      const compiled = new PostScriptCompiler().compile(code, domain, range);
+
+      if (compiled) {
+        return new Function("src", "srcOffset", "dest", "destOffset", compiled);
       }
+    }
 
-      const lexer = new _ps_parser.PostScriptLexer(fn);
-      const parser = new _ps_parser.PostScriptParser(lexer);
-      const code = parser.parse();
-      return [CONSTRUCT_POSTSCRIPT, domain, range, code];
-    },
-
-    constructPostScriptFromIR({
-      xref,
-      isEvalSupported,
-      IR
-    }) {
-      const domain = IR[1];
-      const range = IR[2];
-      const code = IR[3];
-
-      if (isEvalSupported && _util.IsEvalSupportedCached.value) {
-        const compiled = new PostScriptCompiler().compile(code, domain, range);
-
-        if (compiled) {
-          return new Function("src", "srcOffset", "dest", "destOffset", compiled);
-        }
+    (0, _util.info)("Unable to compile PS function");
+    const numOutputs = range.length >> 1;
+    const numInputs = domain.length >> 1;
+    const evaluator = new PostScriptEvaluator(code);
+    const cache = Object.create(null);
+    const MAX_CACHE_SIZE = 2048 * 4;
+    let cache_available = MAX_CACHE_SIZE;
+    const tmpBuf = new Float32Array(numInputs);
+    return function constructPostScriptFn(src, srcOffset, dest, destOffset) {
+      let i, value;
+      let key = "";
+      const input = tmpBuf;
+
+      for (i = 0; i < numInputs; i++) {
+        value = src[srcOffset + i];
+        input[i] = value;
+        key += value + "_";
       }
 
-      (0, _util.info)("Unable to compile PS function");
-      const numOutputs = range.length >> 1;
-      const numInputs = domain.length >> 1;
-      const evaluator = new PostScriptEvaluator(code);
-      const cache = Object.create(null);
-      const MAX_CACHE_SIZE = 2048 * 4;
-      let cache_available = MAX_CACHE_SIZE;
-      const tmpBuf = new Float32Array(numInputs);
-      return function constructPostScriptFromIRResult(src, srcOffset, dest, destOffset) {
-        let i, value;
-        let key = "";
-        const input = tmpBuf;
-
-        for (i = 0; i < numInputs; i++) {
-          value = src[srcOffset + i];
-          input[i] = value;
-          key += value + "_";
-        }
+      const cachedValue = cache[key];
 
-        const cachedValue = cache[key];
+      if (cachedValue !== undefined) {
+        dest.set(cachedValue, destOffset);
+        return;
+      }
 
-        if (cachedValue !== undefined) {
-          dest.set(cachedValue, destOffset);
-          return;
-        }
+      const output = new Float32Array(numOutputs);
+      const stack = evaluator.execute(input);
+      const stackIndex = stack.length - numOutputs;
 
-        const output = new Float32Array(numOutputs);
-        const stack = evaluator.execute(input);
-        const stackIndex = stack.length - numOutputs;
+      for (i = 0; i < numOutputs; i++) {
+        value = stack[stackIndex + i];
+        let bound = range[i * 2];
 
-        for (i = 0; i < numOutputs; i++) {
-          value = stack[stackIndex + i];
-          let bound = range[i * 2];
+        if (value < bound) {
+          value = bound;
+        } else {
+          bound = range[i * 2 + 1];
 
-          if (value < bound) {
+          if (value > bound) {
             value = bound;
-          } else {
-            bound = range[i * 2 + 1];
-
-            if (value > bound) {
-              value = bound;
-            }
           }
-
-          output[i] = value;
         }
 
-        if (cache_available > 0) {
-          cache_available--;
-          cache[key] = output;
-        }
+        output[i] = value;
+      }
 
-        dest.set(output, destOffset);
-      };
-    }
+      if (cache_available > 0) {
+        cache_available--;
+        cache[key] = output;
+      }
 
-  };
-}();
+      dest.set(output, destOffset);
+    };
+  }
+
+}
 
 function isPDFFunction(v) {
   let fnDict;
@@ -669,75 +573,73 @@ function isPDFFunction(v) {
   return fnDict.has("FunctionType");
 }
 
-const PostScriptStack = function PostScriptStackClosure() {
-  const MAX_STACK_SIZE = 100;
-
-  class PostScriptStack {
-    constructor(initialStack) {
-      this.stack = !initialStack ? [] : Array.prototype.slice.call(initialStack, 0);
-    }
+class PostScriptStack {
+  static get MAX_STACK_SIZE() {
+    return (0, _util.shadow)(this, "MAX_STACK_SIZE", 100);
+  }
 
-    push(value) {
-      if (this.stack.length >= MAX_STACK_SIZE) {
-        throw new Error("PostScript function stack overflow.");
-      }
+  constructor(initialStack) {
+    this.stack = !initialStack ? [] : Array.prototype.slice.call(initialStack, 0);
+  }
 
-      this.stack.push(value);
+  push(value) {
+    if (this.stack.length >= PostScriptStack.MAX_STACK_SIZE) {
+      throw new Error("PostScript function stack overflow.");
     }
 
-    pop() {
-      if (this.stack.length <= 0) {
-        throw new Error("PostScript function stack underflow.");
-      }
+    this.stack.push(value);
+  }
 
-      return this.stack.pop();
+  pop() {
+    if (this.stack.length <= 0) {
+      throw new Error("PostScript function stack underflow.");
     }
 
-    copy(n) {
-      if (this.stack.length + n >= MAX_STACK_SIZE) {
-        throw new Error("PostScript function stack overflow.");
-      }
-
-      const stack = this.stack;
+    return this.stack.pop();
+  }
 
-      for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
-        stack.push(stack[i]);
-      }
+  copy(n) {
+    if (this.stack.length + n >= PostScriptStack.MAX_STACK_SIZE) {
+      throw new Error("PostScript function stack overflow.");
     }
 
-    index(n) {
-      this.push(this.stack[this.stack.length - n - 1]);
+    const stack = this.stack;
+
+    for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
+      stack.push(stack[i]);
     }
+  }
 
-    roll(n, p) {
-      const stack = this.stack;
-      const l = stack.length - n;
-      const r = stack.length - 1;
-      const c = l + (p - Math.floor(p / n) * n);
+  index(n) {
+    this.push(this.stack[this.stack.length - n - 1]);
+  }
 
-      for (let i = l, j = r; i < j; i++, j--) {
-        const t = stack[i];
-        stack[i] = stack[j];
-        stack[j] = t;
-      }
+  roll(n, p) {
+    const stack = this.stack;
+    const l = stack.length - n;
+    const r = stack.length - 1;
+    const c = l + (p - Math.floor(p / n) * n);
 
-      for (let i = l, j = c - 1; i < j; i++, j--) {
-        const t = stack[i];
-        stack[i] = stack[j];
-        stack[j] = t;
-      }
+    for (let i = l, j = r; i < j; i++, j--) {
+      const t = stack[i];
+      stack[i] = stack[j];
+      stack[j] = t;
+    }
 
-      for (let i = c, j = r; i < j; i++, j--) {
-        const t = stack[i];
-        stack[i] = stack[j];
-        stack[j] = t;
-      }
+    for (let i = l, j = c - 1; i < j; i++, j--) {
+      const t = stack[i];
+      stack[i] = stack[j];
+      stack[j] = t;
     }
 
+    for (let i = c, j = r; i < j; i++, j--) {
+      const t = stack[i];
+      stack[i] = stack[j];
+      stack[j] = t;
+    }
   }
 
-  return PostScriptStack;
-}();
+}
 
 class PostScriptEvaluator {
   constructor(operators) {

+ 672 - 0
lib/core/glyf.js

@@ -0,0 +1,672 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.GlyfTable = void 0;
+const ON_CURVE_POINT = 1 << 0;
+const X_SHORT_VECTOR = 1 << 1;
+const Y_SHORT_VECTOR = 1 << 2;
+const REPEAT_FLAG = 1 << 3;
+const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 1 << 4;
+const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 1 << 5;
+const OVERLAP_SIMPLE = 1 << 6;
+const ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const ARGS_ARE_XY_VALUES = 1 << 1;
+const WE_HAVE_A_SCALE = 1 << 3;
+const MORE_COMPONENTS = 1 << 5;
+const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const WE_HAVE_INSTRUCTIONS = 1 << 8;
+
+class GlyfTable {
+  constructor({
+    glyfTable,
+    isGlyphLocationsLong,
+    locaTable,
+    numGlyphs
+  }) {
+    this.glyphs = [];
+    const loca = new DataView(locaTable.buffer, locaTable.byteOffset, locaTable.byteLength);
+    const glyf = new DataView(glyfTable.buffer, glyfTable.byteOffset, glyfTable.byteLength);
+    const offsetSize = isGlyphLocationsLong ? 4 : 2;
+    let prev = isGlyphLocationsLong ? loca.getUint32(0) : 2 * loca.getUint16(0);
+    let pos = 0;
+
+    for (let i = 0; i < numGlyphs; i++) {
+      pos += offsetSize;
+      const next = isGlyphLocationsLong ? loca.getUint32(pos) : 2 * loca.getUint16(pos);
+
+      if (next === prev) {
+        this.glyphs.push(new Glyph({}));
+        continue;
+      }
+
+      const glyph = Glyph.parse(prev, glyf);
+      this.glyphs.push(glyph);
+      prev = next;
+    }
+  }
+
+  getSize() {
+    return this.glyphs.reduce((a, g) => {
+      const size = g.getSize();
+      return a + (size + 3 & ~3);
+    }, 0);
+  }
+
+  write() {
+    const totalSize = this.getSize();
+    const glyfTable = new DataView(new ArrayBuffer(totalSize));
+    const isLocationLong = totalSize > 0x1fffe;
+    const offsetSize = isLocationLong ? 4 : 2;
+    const locaTable = new DataView(new ArrayBuffer((this.glyphs.length + 1) * offsetSize));
+
+    if (isLocationLong) {
+      locaTable.setUint32(0, 0);
+    } else {
+      locaTable.setUint16(0, 0);
+    }
+
+    let pos = 0;
+    let locaIndex = 0;
+
+    for (const glyph of this.glyphs) {
+      pos += glyph.write(pos, glyfTable);
+      pos = pos + 3 & ~3;
+      locaIndex += offsetSize;
+
+      if (isLocationLong) {
+        locaTable.setUint32(locaIndex, pos);
+      } else {
+        locaTable.setUint16(locaIndex, pos >> 1);
+      }
+    }
+
+    return {
+      isLocationLong,
+      loca: new Uint8Array(locaTable.buffer),
+      glyf: new Uint8Array(glyfTable.buffer)
+    };
+  }
+
+  scale(factors) {
+    for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
+      this.glyphs[i].scale(factors[i]);
+    }
+  }
+
+}
+
+exports.GlyfTable = GlyfTable;
+
+class Glyph {
+  constructor({
+    header = null,
+    simple = null,
+    composites = null
+  }) {
+    this.header = header;
+    this.simple = simple;
+    this.composites = composites;
+  }
+
+  static parse(pos, glyf) {
+    const [read, header] = GlyphHeader.parse(pos, glyf);
+    pos += read;
+
+    if (header.numberOfContours < 0) {
+      const composites = [];
+
+      while (true) {
+        const [n, composite] = CompositeGlyph.parse(pos, glyf);
+        pos += n;
+        composites.push(composite);
+
+        if (!(composite.flags & MORE_COMPONENTS)) {
+          break;
+        }
+      }
+
+      return new Glyph({
+        header,
+        composites
+      });
+    }
+
+    const simple = SimpleGlyph.parse(pos, glyf, header.numberOfContours);
+    return new Glyph({
+      header,
+      simple
+    });
+  }
+
+  getSize() {
+    if (!this.header) {
+      return 0;
+    }
+
+    const size = this.simple ? this.simple.getSize() : this.composites.reduce((a, c) => a + c.getSize(), 0);
+    return this.header.getSize() + size;
+  }
+
+  write(pos, buf) {
+    if (!this.header) {
+      return 0;
+    }
+
+    const spos = pos;
+    pos += this.header.write(pos, buf);
+
+    if (this.simple) {
+      pos += this.simple.write(pos, buf);
+    } else {
+      for (const composite of this.composites) {
+        pos += composite.write(pos, buf);
+      }
+    }
+
+    return pos - spos;
+  }
+
+  scale(factor) {
+    if (!this.header) {
+      return;
+    }
+
+    const xMiddle = (this.header.xMin + this.header.xMax) / 2;
+    this.header.scale(xMiddle, factor);
+
+    if (this.simple) {
+      this.simple.scale(xMiddle, factor);
+    } else {
+      for (const composite of this.composites) {
+        composite.scale(xMiddle, factor);
+      }
+    }
+  }
+
+}
+
+class GlyphHeader {
+  constructor({
+    numberOfContours,
+    xMin,
+    yMin,
+    xMax,
+    yMax
+  }) {
+    this.numberOfContours = numberOfContours;
+    this.xMin = xMin;
+    this.yMin = yMin;
+    this.xMax = xMax;
+    this.yMax = yMax;
+  }
+
+  static parse(pos, glyf) {
+    return [10, new GlyphHeader({
+      numberOfContours: glyf.getInt16(pos),
+      xMin: glyf.getInt16(pos + 2),
+      yMin: glyf.getInt16(pos + 4),
+      xMax: glyf.getInt16(pos + 6),
+      yMax: glyf.getInt16(pos + 8)
+    })];
+  }
+
+  getSize() {
+    return 10;
+  }
+
+  write(pos, buf) {
+    buf.setInt16(pos, this.numberOfContours);
+    buf.setInt16(pos + 2, this.xMin);
+    buf.setInt16(pos + 4, this.yMin);
+    buf.setInt16(pos + 6, this.xMax);
+    buf.setInt16(pos + 8, this.yMax);
+    return 10;
+  }
+
+  scale(x, factor) {
+    this.xMin = Math.round(x + (this.xMin - x) * factor);
+    this.xMax = Math.round(x + (this.xMax - x) * factor);
+  }
+
+}
+
+class Contour {
+  constructor({
+    flags,
+    xCoordinates,
+    yCoordinates
+  }) {
+    this.xCoordinates = xCoordinates;
+    this.yCoordinates = yCoordinates;
+    this.flags = flags;
+  }
+
+}
+
+class SimpleGlyph {
+  constructor({
+    contours,
+    instructions
+  }) {
+    this.contours = contours;
+    this.instructions = instructions;
+  }
+
+  static parse(pos, glyf, numberOfContours) {
+    const endPtsOfContours = [];
+
+    for (let i = 0; i < numberOfContours; i++) {
+      const endPt = glyf.getUint16(pos);
+      pos += 2;
+      endPtsOfContours.push(endPt);
+    }
+
+    const numberOfPt = endPtsOfContours[numberOfContours - 1] + 1;
+    const instructionLength = glyf.getUint16(pos);
+    pos += 2;
+    const instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);
+    pos += instructionLength;
+    const flags = [];
+
+    for (let i = 0; i < numberOfPt; pos++, i++) {
+      let flag = glyf.getUint8(pos);
+      flags.push(flag);
+
+      if (flag & REPEAT_FLAG) {
+        const count = glyf.getUint8(++pos);
+        flag ^= REPEAT_FLAG;
+
+        for (let m = 0; m < count; m++) {
+          flags.push(flag);
+        }
+
+        i += count;
+      }
+    }
+
+    const allXCoordinates = [];
+    let xCoordinates = [];
+    let yCoordinates = [];
+    let pointFlags = [];
+    const contours = [];
+    let endPtsOfContoursIndex = 0;
+    let lastCoordinate = 0;
+
+    for (let i = 0; i < numberOfPt; i++) {
+      const flag = flags[i];
+
+      if (flag & X_SHORT_VECTOR) {
+        const x = glyf.getUint8(pos++);
+        lastCoordinate += flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR ? x : -x;
+        xCoordinates.push(lastCoordinate);
+      } else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
+        xCoordinates.push(lastCoordinate);
+      } else {
+        lastCoordinate += glyf.getInt16(pos);
+        pos += 2;
+        xCoordinates.push(lastCoordinate);
+      }
+
+      if (endPtsOfContours[endPtsOfContoursIndex] === i) {
+        endPtsOfContoursIndex++;
+        allXCoordinates.push(xCoordinates);
+        xCoordinates = [];
+      }
+    }
+
+    lastCoordinate = 0;
+    endPtsOfContoursIndex = 0;
+
+    for (let i = 0; i < numberOfPt; i++) {
+      const flag = flags[i];
+
+      if (flag & Y_SHORT_VECTOR) {
+        const y = glyf.getUint8(pos++);
+        lastCoordinate += flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR ? y : -y;
+        yCoordinates.push(lastCoordinate);
+      } else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {
+        yCoordinates.push(lastCoordinate);
+      } else {
+        lastCoordinate += glyf.getInt16(pos);
+        pos += 2;
+        yCoordinates.push(lastCoordinate);
+      }
+
+      pointFlags.push(flag & ON_CURVE_POINT | flag & OVERLAP_SIMPLE);
+
+      if (endPtsOfContours[endPtsOfContoursIndex] === i) {
+        xCoordinates = allXCoordinates[endPtsOfContoursIndex];
+        endPtsOfContoursIndex++;
+        contours.push(new Contour({
+          flags: pointFlags,
+          xCoordinates,
+          yCoordinates
+        }));
+        yCoordinates = [];
+        pointFlags = [];
+      }
+    }
+
+    return new SimpleGlyph({
+      contours,
+      instructions
+    });
+  }
+
+  getSize() {
+    let size = this.contours.length * 2 + 2 + this.instructions.length;
+    let lastX = 0;
+    let lastY = 0;
+
+    for (const contour of this.contours) {
+      size += contour.flags.length;
+
+      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
+        const x = contour.xCoordinates[i];
+        const y = contour.yCoordinates[i];
+        let abs = Math.abs(x - lastX);
+
+        if (abs > 255) {
+          size += 2;
+        } else if (abs > 0) {
+          size += 1;
+        }
+
+        lastX = x;
+        abs = Math.abs(y - lastY);
+
+        if (abs > 255) {
+          size += 2;
+        } else if (abs > 0) {
+          size += 1;
+        }
+
+        lastY = y;
+      }
+    }
+
+    return size;
+  }
+
+  write(pos, buf) {
+    const spos = pos;
+    const xCoordinates = [];
+    const yCoordinates = [];
+    const flags = [];
+    let lastX = 0;
+    let lastY = 0;
+
+    for (const contour of this.contours) {
+      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
+        let flag = contour.flags[i];
+        const x = contour.xCoordinates[i];
+        let delta = x - lastX;
+
+        if (delta === 0) {
+          flag |= X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR;
+          xCoordinates.push(0);
+        } else {
+          const abs = Math.abs(delta);
+
+          if (abs <= 255) {
+            flag |= delta >= 0 ? X_SHORT_VECTOR | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR : X_SHORT_VECTOR;
+            xCoordinates.push(abs);
+          } else {
+            xCoordinates.push(delta);
+          }
+        }
+
+        lastX = x;
+        const y = contour.yCoordinates[i];
+        delta = y - lastY;
+
+        if (delta === 0) {
+          flag |= Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR;
+          yCoordinates.push(0);
+        } else {
+          const abs = Math.abs(delta);
+
+          if (abs <= 255) {
+            flag |= delta >= 0 ? Y_SHORT_VECTOR | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR : Y_SHORT_VECTOR;
+            yCoordinates.push(abs);
+          } else {
+            yCoordinates.push(delta);
+          }
+        }
+
+        lastY = y;
+        flags.push(flag);
+      }
+
+      buf.setUint16(pos, xCoordinates.length - 1);
+      pos += 2;
+    }
+
+    buf.setUint16(pos, this.instructions.length);
+    pos += 2;
+
+    if (this.instructions.length) {
+      new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos);
+      pos += this.instructions.length;
+    }
+
+    for (const flag of flags) {
+      buf.setUint8(pos++, flag);
+    }
+
+    for (let i = 0, ii = xCoordinates.length; i < ii; i++) {
+      const x = xCoordinates[i];
+      const flag = flags[i];
+
+      if (flag & X_SHORT_VECTOR) {
+        buf.setUint8(pos++, x);
+      } else if (!(flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)) {
+        buf.setInt16(pos, x);
+        pos += 2;
+      }
+    }
+
+    for (let i = 0, ii = yCoordinates.length; i < ii; i++) {
+      const y = yCoordinates[i];
+      const flag = flags[i];
+
+      if (flag & Y_SHORT_VECTOR) {
+        buf.setUint8(pos++, y);
+      } else if (!(flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)) {
+        buf.setInt16(pos, y);
+        pos += 2;
+      }
+    }
+
+    return pos - spos;
+  }
+
+  scale(x, factor) {
+    for (const contour of this.contours) {
+      if (contour.xCoordinates.length === 0) {
+        continue;
+      }
+
+      for (let i = 0, ii = contour.xCoordinates.length; i < ii; i++) {
+        contour.xCoordinates[i] = Math.round(x + (contour.xCoordinates[i] - x) * factor);
+      }
+    }
+  }
+
+}
+
+class CompositeGlyph {
+  constructor({
+    flags,
+    glyphIndex,
+    argument1,
+    argument2,
+    transf,
+    instructions
+  }) {
+    this.flags = flags;
+    this.glyphIndex = glyphIndex;
+    this.argument1 = argument1;
+    this.argument2 = argument2;
+    this.transf = transf;
+    this.instructions = instructions;
+  }
+
+  static parse(pos, glyf) {
+    const spos = pos;
+    const transf = [];
+    let flags = glyf.getUint16(pos);
+    const glyphIndex = glyf.getUint16(pos + 2);
+    pos += 4;
+    let argument1, argument2;
+
+    if (flags & ARG_1_AND_2_ARE_WORDS) {
+      if (flags & ARGS_ARE_XY_VALUES) {
+        argument1 = glyf.getInt16(pos);
+        argument2 = glyf.getInt16(pos + 2);
+      } else {
+        argument1 = glyf.getUint16(pos);
+        argument2 = glyf.getUint16(pos + 2);
+      }
+
+      pos += 4;
+      flags ^= ARG_1_AND_2_ARE_WORDS;
+    } else {
+      argument1 = glyf.getUint8(pos);
+      argument2 = glyf.getUint8(pos + 1);
+
+      if (flags & ARGS_ARE_XY_VALUES) {
+        const abs1 = argument1 & 0x7f;
+        argument1 = argument1 & 0x80 ? -abs1 : abs1;
+        const abs2 = argument2 & 0x7f;
+        argument2 = argument2 & 0x80 ? -abs2 : abs2;
+      }
+
+      pos += 2;
+    }
+
+    if (flags & WE_HAVE_A_SCALE) {
+      transf.push(glyf.getUint16(pos));
+      pos += 2;
+    } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
+      transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2));
+      pos += 4;
+    } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
+      transf.push(glyf.getUint16(pos), glyf.getUint16(pos + 2), glyf.getUint16(pos + 4), glyf.getUint16(pos + 6));
+      pos += 8;
+    }
+
+    let instructions = null;
+
+    if (flags & WE_HAVE_INSTRUCTIONS) {
+      const instructionLength = glyf.getUint16(pos);
+      pos += 2;
+      instructions = new Uint8Array(glyf).slice(pos, pos + instructionLength);
+      pos += instructionLength;
+    }
+
+    return [pos - spos, new CompositeGlyph({
+      flags,
+      glyphIndex,
+      argument1,
+      argument2,
+      transf,
+      instructions
+    })];
+  }
+
+  getSize() {
+    let size = 2 + 2 + this.transf.length * 2;
+
+    if (this.flags & WE_HAVE_INSTRUCTIONS) {
+      size += 2 + this.instructions.length;
+    }
+
+    size += 2;
+
+    if (this.flags & 2) {
+      if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) {
+        size += 2;
+      }
+    } else {
+      if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) {
+        size += 2;
+      }
+    }
+
+    return size;
+  }
+
+  write(pos, buf) {
+    const spos = pos;
+
+    if (this.flags & ARGS_ARE_XY_VALUES) {
+      if (!(this.argument1 >= -128 && this.argument1 <= 127 && this.argument2 >= -128 && this.argument2 <= 127)) {
+        this.flags |= ARG_1_AND_2_ARE_WORDS;
+      }
+    } else {
+      if (!(this.argument1 >= 0 && this.argument1 <= 255 && this.argument2 >= 0 && this.argument2 <= 255)) {
+        this.flags |= ARG_1_AND_2_ARE_WORDS;
+      }
+    }
+
+    buf.setUint16(pos, this.flags);
+    buf.setUint16(pos + 2, this.glyphIndex);
+    pos += 4;
+
+    if (this.flags & ARG_1_AND_2_ARE_WORDS) {
+      if (this.flags & ARGS_ARE_XY_VALUES) {
+        buf.setInt16(pos, this.argument1);
+        buf.setInt16(pos + 2, this.argument2);
+      } else {
+        buf.setUint16(pos, this.argument1);
+        buf.setUint16(pos + 2, this.argument2);
+      }
+
+      pos += 4;
+    } else {
+      buf.setUint8(pos, this.argument1);
+      buf.setUint8(pos + 1, this.argument2);
+      pos += 2;
+    }
+
+    if (this.flags & WE_HAVE_INSTRUCTIONS) {
+      buf.setUint16(pos, this.instructions.length);
+      pos += 2;
+
+      if (this.instructions.length) {
+        new Uint8Array(buf.buffer, 0, buf.buffer.byteLength).set(this.instructions, pos);
+        pos += this.instructions.length;
+      }
+    }
+
+    return pos - spos;
+  }
+
+  scale(x, factor) {}
+
+}

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


+ 1 - 1
lib/core/image.js

@@ -434,7 +434,7 @@ class PDFImage {
         }
 
         output[i] = value;
-        buf = buf & (1 << remainingBits) - 1;
+        buf &= (1 << remainingBits) - 1;
         bits = remainingBits;
       }
     }

+ 5 - 5
lib/core/image_utils.js

@@ -66,7 +66,7 @@ class BaseLocalCache {
 
 class LocalImageCache extends BaseLocalCache {
   set(name, ref = null, data) {
-    if (!name) {
+    if (typeof name !== "string") {
       throw new Error('LocalImageCache.set - expected "name" argument.');
     }
 
@@ -95,7 +95,7 @@ exports.LocalImageCache = LocalImageCache;
 
 class LocalColorSpaceCache extends BaseLocalCache {
   set(name = null, ref = null, data) {
-    if (!name && !ref) {
+    if (typeof name !== "string" && !ref) {
       throw new Error('LocalColorSpaceCache.set - expected "name" and/or "ref" argument.');
     }
 
@@ -104,7 +104,7 @@ class LocalColorSpaceCache extends BaseLocalCache {
         return;
       }
 
-      if (name) {
+      if (name !== null) {
         this._nameRefMap.set(name, ref);
       }
 
@@ -153,7 +153,7 @@ exports.LocalFunctionCache = LocalFunctionCache;
 
 class LocalGStateCache extends BaseLocalCache {
   set(name, ref = null, data) {
-    if (!name) {
+    if (typeof name !== "string") {
       throw new Error('LocalGStateCache.set - expected "name" argument.');
     }
 
@@ -182,7 +182,7 @@ exports.LocalGStateCache = LocalGStateCache;
 
 class LocalTilingPatternCache extends BaseLocalCache {
   set(name, ref = null, data) {
-    if (!name) {
+    if (typeof name !== "string") {
       throw new Error('LocalTilingPatternCache.set - expected "name" argument.');
     }
 

+ 1 - 1
lib/core/jbig2.js

@@ -948,7 +948,7 @@ function decodeHalftoneRegion(mmr, patterns, template, regionWidth, regionHeight
       patternIndex = 0;
 
       for (j = bitsPerValue - 1; j >= 0; j--) {
-        bit = grayScaleBitPlanes[j][mg][ng] ^ bit;
+        bit ^= grayScaleBitPlanes[j][mg][ng];
         patternIndex |= bit << j;
       }
 

+ 2 - 2
lib/core/jpx.js

@@ -2250,7 +2250,7 @@ class Transform {
 class IrreversibleTransform extends Transform {
   filter(x, offset, length) {
     const len = length >> 1;
-    offset = offset | 0;
+    offset |= 0;
     let j, n, current, next;
     const alpha = -1.586134342059924;
     const beta = -0.052980118572961;
@@ -2336,7 +2336,7 @@ class IrreversibleTransform extends Transform {
 class ReversibleTransform extends Transform {
   filter(x, offset, length) {
     const len = length >> 1;
-    offset = offset | 0;
+    offset |= 0;
     let j, n;
 
     for (j = offset, n = len + 1; n--; j += 2) {

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


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


+ 2 - 2
lib/core/operator_list.js

@@ -537,7 +537,7 @@ class OperatorList {
   }
 
   static get CHUNK_SIZE_ABOUT() {
-    return (0, _util.shadow)(this, "CHUNK_SIZE_ABOUT", OperatorList.CHUNK_SIZE - 5);
+    return (0, _util.shadow)(this, "CHUNK_SIZE_ABOUT", this.CHUNK_SIZE - 5);
   }
 
   constructor(intent, streamSink) {
@@ -545,7 +545,7 @@ class OperatorList {
     this.fnArray = [];
     this.argsArray = [];
 
-    if (streamSink && intent !== "oplist") {
+    if (streamSink && !(intent && intent.startsWith("oplist-"))) {
       this.optimizer = new QueueOptimizer(this);
     } else {
       this.optimizer = new NullOptimizer(this);

+ 8 - 6
lib/core/parser.js

@@ -130,11 +130,11 @@ class Parser {
           }
 
           if ((0, _primitives.isEOF)(this.buf1)) {
-            if (!this.recoveryMode) {
-              throw new _util.FormatError("End of file inside array");
+            if (this.recoveryMode) {
+              return array;
             }
 
-            return array;
+            throw new _core_utils.ParserEOFException("End of file inside array.");
           }
 
           this.shift();
@@ -161,11 +161,11 @@ class Parser {
           }
 
           if ((0, _primitives.isEOF)(this.buf1)) {
-            if (!this.recoveryMode) {
-              throw new _util.FormatError("End of file inside dictionary");
+            if (this.recoveryMode) {
+              return dict;
             }
 
-            return dict;
+            throw new _core_utils.ParserEOFException("End of file inside dictionary.");
           }
 
           if ((0, _primitives.isCmd)(this.buf2, "stream")) {
@@ -1069,6 +1069,8 @@ class Lexer {
 
     if (strBuf.length > 127) {
       (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(""));

+ 8 - 0
lib/core/pdf_manager.js

@@ -97,6 +97,14 @@ class BasePdfManager {
     return this.pdfDocument.loadXfaFonts(handler, task);
   }
 
+  loadXfaImages() {
+    return this.pdfDocument.loadXfaImages();
+  }
+
+  serializeXfaData(annotationStorage) {
+    return this.pdfDocument.serializeXfaData(annotationStorage);
+  }
+
   cleanup(manuallyTriggered = false) {
     return this.pdfDocument.cleanup(manuallyTriggered);
   }

+ 4 - 19
lib/core/primitives.js

@@ -215,25 +215,8 @@ class Dict {
     dictArray,
     mergeSubDicts = false
   }) {
-    const mergedDict = new Dict(xref);
-
-    if (!mergeSubDicts) {
-      for (const dict of dictArray) {
-        if (!(dict instanceof Dict)) {
-          continue;
-        }
-
-        for (const [key, value] of Object.entries(dict._map)) {
-          if (mergedDict._map[key] === undefined) {
-            mergedDict._map[key] = value;
-          }
-        }
-      }
-
-      return mergedDict.size > 0 ? mergedDict : Dict.empty;
-    }
-
-    const properties = new Map();
+    const mergedDict = new Dict(xref),
+          properties = new Map();
 
     for (const dict of dictArray) {
       if (!(dict instanceof Dict)) {
@@ -246,6 +229,8 @@ class Dict {
         if (property === undefined) {
           property = [];
           properties.set(key, property);
+        } else if (!mergeSubDicts) {
+          continue;
         }
 
         property.push(value);

+ 26 - 28
lib/core/ps_parser.js

@@ -121,45 +121,43 @@ const PostScriptTokenTypes = {
   IFELSE: 5
 };
 
-const PostScriptToken = function PostScriptTokenClosure() {
-  const opCache = Object.create(null);
+class PostScriptToken {
+  static get opCache() {
+    return (0, _util.shadow)(this, "opCache", Object.create(null));
+  }
 
-  class PostScriptToken {
-    constructor(type, value) {
-      this.type = type;
-      this.value = value;
-    }
+  constructor(type, value) {
+    this.type = type;
+    this.value = value;
+  }
 
-    static getOperator(op) {
-      const opValue = opCache[op];
+  static getOperator(op) {
+    const opValue = PostScriptToken.opCache[op];
 
-      if (opValue) {
-        return opValue;
-      }
-
-      return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
+    if (opValue) {
+      return opValue;
     }
 
-    static get LBRACE() {
-      return (0, _util.shadow)(this, "LBRACE", new PostScriptToken(PostScriptTokenTypes.LBRACE, "{"));
-    }
+    return PostScriptToken.opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
+  }
 
-    static get RBRACE() {
-      return (0, _util.shadow)(this, "RBRACE", new PostScriptToken(PostScriptTokenTypes.RBRACE, "}"));
-    }
+  static get LBRACE() {
+    return (0, _util.shadow)(this, "LBRACE", new PostScriptToken(PostScriptTokenTypes.LBRACE, "{"));
+  }
 
-    static get IF() {
-      return (0, _util.shadow)(this, "IF", new PostScriptToken(PostScriptTokenTypes.IF, "IF"));
-    }
+  static get RBRACE() {
+    return (0, _util.shadow)(this, "RBRACE", new PostScriptToken(PostScriptTokenTypes.RBRACE, "}"));
+  }
 
-    static get IFELSE() {
-      return (0, _util.shadow)(this, "IFELSE", new PostScriptToken(PostScriptTokenTypes.IFELSE, "IFELSE"));
-    }
+  static get IF() {
+    return (0, _util.shadow)(this, "IF", new PostScriptToken(PostScriptTokenTypes.IF, "IF"));
+  }
 
+  static get IFELSE() {
+    return (0, _util.shadow)(this, "IFELSE", new PostScriptToken(PostScriptTokenTypes.IFELSE, "IFELSE"));
   }
 
-  return PostScriptToken;
-}();
+}
 
 class PostScriptLexer {
   constructor(stream) {

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


+ 46 - 7
lib/core/standard_fonts.js

@@ -24,11 +24,28 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.getSymbolsFonts = exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForArialBlack = exports.getStdFontMap = exports.getSerifFonts = exports.getNonStdFontMap = exports.getGlyphMapForStandardFonts = void 0;
+exports.getStandardFontName = getStandardFontName;
+exports.getSymbolsFonts = exports.getSupplementalGlyphMapForCalibri = exports.getSupplementalGlyphMapForArialBlack = exports.getStdFontMap = exports.getSerifFonts = exports.getNonStdFontMap = exports.getGlyphMapForStandardFonts = exports.getFontNameToFileMap = void 0;
 
 var _core_utils = require("./core_utils.js");
 
+var _fonts_utils = require("./fonts_utils.js");
+
 const getStdFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
+  t["Times-Roman"] = "Times-Roman";
+  t.Helvetica = "Helvetica";
+  t.Courier = "Courier";
+  t.Symbol = "Symbol";
+  t["Times-Bold"] = "Times-Bold";
+  t["Helvetica-Bold"] = "Helvetica-Bold";
+  t["Courier-Bold"] = "Courier-Bold";
+  t.ZapfDingbats = "ZapfDingbats";
+  t["Times-Italic"] = "Times-Italic";
+  t["Helvetica-Oblique"] = "Helvetica-Oblique";
+  t["Courier-Oblique"] = "Courier-Oblique";
+  t["Times-BoldItalic"] = "Times-BoldItalic";
+  t["Helvetica-BoldOblique"] = "Helvetica-BoldOblique";
+  t["Courier-BoldOblique"] = "Courier-BoldOblique";
   t.ArialNarrow = "Helvetica";
   t["ArialNarrow-Bold"] = "Helvetica-Bold";
   t["ArialNarrow-BoldItalic"] = "Helvetica-BoldOblique";
@@ -49,7 +66,6 @@ const getStdFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
   t["Arial-BoldMT"] = "Helvetica-Bold";
   t["Arial-ItalicMT"] = "Helvetica-Oblique";
   t.ArialMT = "Helvetica";
-  t["Courier-Bold"] = "Courier-Bold";
   t["Courier-BoldItalic"] = "Courier-BoldOblique";
   t["Courier-Italic"] = "Courier-Oblique";
   t.CourierNew = "Courier";
@@ -60,12 +76,8 @@ const getStdFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
   t["CourierNewPS-BoldMT"] = "Courier-Bold";
   t["CourierNewPS-ItalicMT"] = "Courier-Oblique";
   t.CourierNewPSMT = "Courier";
-  t.Helvetica = "Helvetica";
-  t["Helvetica-Bold"] = "Helvetica-Bold";
   t["Helvetica-BoldItalic"] = "Helvetica-BoldOblique";
-  t["Helvetica-BoldOblique"] = "Helvetica-BoldOblique";
   t["Helvetica-Italic"] = "Helvetica-Oblique";
-  t["Helvetica-Oblique"] = "Helvetica-Oblique";
   t["Symbol-Bold"] = "Symbol";
   t["Symbol-BoldItalic"] = "Symbol";
   t["Symbol-Italic"] = "Symbol";
@@ -86,6 +98,27 @@ const getStdFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
   t["TimesNewRomanPSMT-Italic"] = "Times-Italic";
 });
 exports.getStdFontMap = getStdFontMap;
+const getFontNameToFileMap = (0, _core_utils.getLookupTableFactory)(function (t) {
+  t.Courier = "FoxitFixed.pfb";
+  t["Courier-Bold"] = "FoxitFixedBold.pfb";
+  t["Courier-BoldOblique"] = "FoxitFixedBoldItalic.pfb";
+  t["Courier-Oblique"] = "FoxitFixedItalic.pfb";
+  t.Helvetica = "FoxitSans.pfb";
+  t["Helvetica-Bold"] = "FoxitSansBold.pfb";
+  t["Helvetica-BoldOblique"] = "FoxitSansBoldItalic.pfb";
+  t["Helvetica-Oblique"] = "FoxitSansItalic.pfb";
+  t["Times-Roman"] = "FoxitSerif.pfb";
+  t["Times-Bold"] = "FoxitSerifBold.pfb";
+  t["Times-BoldItalic"] = "FoxitSerifBoldItalic.pfb";
+  t["Times-Italic"] = "FoxitSerifItalic.pfb";
+  t.Symbol = "FoxitSymbol.pfb";
+  t.ZapfDingbats = "FoxitDingbats.pfb";
+  t["LiberationSans-Regular"] = "LiberationSans-Regular.ttf";
+  t["LiberationSans-Bold"] = "LiberationSans-Bold.ttf";
+  t["LiberationSans-Italic"] = "LiberationSans-Italic.ttf";
+  t["LiberationSans-BoldItalic"] = "LiberationSans-BoldItalic.ttf";
+});
+exports.getFontNameToFileMap = getFontNameToFileMap;
 const getNonStdFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
   t.Calibri = "Helvetica";
   t["Calibri-Bold"] = "Helvetica-Bold";
@@ -756,4 +789,10 @@ const getSupplementalGlyphMapForCalibri = (0, _core_utils.getLookupTableFactory)
   t[1085] = 43;
   t[1086] = 45;
 });
-exports.getSupplementalGlyphMapForCalibri = getSupplementalGlyphMapForCalibri;
+exports.getSupplementalGlyphMapForCalibri = getSupplementalGlyphMapForCalibri;
+
+function getStandardFontName(name) {
+  const fontName = (0, _fonts_utils.normalizeFontName)(name);
+  const stdFontMap = getStdFontMap();
+  return stdFontMap[fontName];
+}

+ 1 - 1
lib/core/type1_parser.js

@@ -293,7 +293,7 @@ const Type1CharString = function Type1CharStringClosure() {
 
           continue;
         } else if (value <= 246) {
-          value = value - 139;
+          value -= 139;
         } else if (value <= 250) {
           value = (value - 247) * 256 + encoded[++i] + 108;
         } else if (value <= 254) {

+ 39 - 27
lib/core/worker.js

@@ -107,7 +107,7 @@ class WorkerMessageHandler {
     const WorkerTasks = [];
     const verbosity = (0, _util.getVerbosityLevel)();
     const apiVersion = docParams.apiVersion;
-    const workerVersion = '2.9.359';
+    const workerVersion = '2.10.377';
 
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
@@ -158,18 +158,20 @@ class WorkerMessageHandler {
         await pdfManager.ensureDoc("checkFirstPage");
       }
 
-      const [numPages, fingerprint, isPureXfa] = await Promise.all([pdfManager.ensureDoc("numPages"), pdfManager.ensureDoc("fingerprint"), pdfManager.ensureDoc("isPureXfa")]);
+      const isPureXfa = await pdfManager.ensureDoc("isPureXfa");
 
       if (isPureXfa) {
         const task = new WorkerTask("loadXfaFonts");
         startWorkerTask(task);
-        await pdfManager.loadXfaFonts(handler, task).catch(reason => {}).then(() => finishWorkerTask(task));
+        await Promise.all([pdfManager.loadXfaFonts(handler, task).catch(reason => {}).then(() => finishWorkerTask(task)), pdfManager.loadXfaImages()]);
       }
 
+      const [numPages, fingerprints] = await Promise.all([pdfManager.ensureDoc("numPages"), pdfManager.ensureDoc("fingerprints")]);
+      const htmlForXfa = isPureXfa ? await pdfManager.ensureDoc("htmlForXfa") : null;
       return {
         numPages,
-        fingerprint,
-        isPureXfa
+        fingerprints,
+        htmlForXfa
       };
     }
 
@@ -351,7 +353,10 @@ class WorkerMessageHandler {
         disableFontFace: data.disableFontFace,
         ignoreErrors: data.ignoreErrors,
         isEvalSupported: data.isEvalSupported,
-        fontExtraProperties: data.fontExtraProperties
+        fontExtraProperties: data.fontExtraProperties,
+        useSystemFonts: data.useSystemFonts,
+        cMapUrl: data.cMapUrl,
+        standardFontDataUrl: data.standardFontDataUrl
       };
       getPdfManager(data, evaluatorOptions, data.enableXfa).then(function (newPdfManager) {
         if (terminated) {
@@ -424,13 +429,6 @@ class WorkerMessageHandler {
         return pdfManager.ensure(page, "jsActions");
       });
     });
-    handler.on("GetPageXfa", function wphSetupGetXfa({
-      pageIndex
-    }) {
-      return pdfManager.getPage(pageIndex).then(function (page) {
-        return pdfManager.ensure(page, "xfaData");
-      });
-    });
     handler.on("GetOutline", function wphSetupGetOutline(data) {
       return pdfManager.ensureCatalog("documentOutline");
     });
@@ -473,6 +471,7 @@ class WorkerMessageHandler {
       return pdfManager.ensureDoc("calculationOrderIds");
     });
     handler.on("SaveDocument", function ({
+      isPureXfa,
       numPages,
       annotationStorage,
       filename
@@ -480,25 +479,37 @@ class WorkerMessageHandler {
       pdfManager.requestLoadedStream();
       const promises = [pdfManager.onLoadedStream(), pdfManager.ensureCatalog("acroForm"), pdfManager.ensureDoc("xref"), pdfManager.ensureDoc("startXRef")];
 
-      for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {
-        promises.push(pdfManager.getPage(pageIndex).then(function (page) {
-          const task = new WorkerTask(`Save: page ${pageIndex}`);
-          startWorkerTask(task);
-          return page.save(handler, task, annotationStorage).finally(function () {
-            finishWorkerTask(task);
-          });
-        }));
+      if (isPureXfa) {
+        promises.push(pdfManager.serializeXfaData(annotationStorage));
+      } else {
+        for (let pageIndex = 0; pageIndex < numPages; pageIndex++) {
+          promises.push(pdfManager.getPage(pageIndex).then(function (page) {
+            const task = new WorkerTask(`Save: page ${pageIndex}`);
+            return page.save(handler, task, annotationStorage).finally(function () {
+              finishWorkerTask(task);
+            });
+          }));
+        }
       }
 
       return Promise.all(promises).then(function ([stream, acroForm, xref, startXRef, ...refs]) {
         let newRefs = [];
+        let xfaData = null;
 
-        for (const ref of refs) {
-          newRefs = ref.filter(x => x !== null).reduce((a, b) => a.concat(b), newRefs);
-        }
+        if (isPureXfa) {
+          xfaData = refs[0];
 
-        if (newRefs.length === 0) {
-          return stream.bytes;
+          if (!xfaData) {
+            return stream.bytes;
+          }
+        } else {
+          for (const ref of refs) {
+            newRefs = ref.filter(x => x !== null).reduce((a, b) => a.concat(b), newRefs);
+          }
+
+          if (newRefs.length === 0) {
+            return stream.bytes;
+          }
         }
 
         const xfa = acroForm instanceof _primitives.Dict && acroForm.get("XFA") || [];
@@ -546,7 +557,8 @@ class WorkerMessageHandler {
           xrefInfo: newXrefInfo,
           newRefs,
           xref,
-          datasetsRef: xfaDatasets
+          datasetsRef: xfaDatasets,
+          xfaData
         });
       });
     });

+ 19 - 12
lib/core/writer.js

@@ -154,13 +154,7 @@ function computeMD5(filesize, xrefInfo) {
   return (0, _util.bytesToString)((0, _crypto.calculateMD5)(array));
 }
 
-function updateXFA(datasetsRef, newRefs, xref) {
-  if (datasetsRef === null || xref === null) {
-    return;
-  }
-
-  const datasets = xref.fetchIfRef(datasetsRef);
-  const str = datasets.getString();
+function writeXFADataForAcroform(str, newRefs) {
   const xml = new _xml_parser.SimpleXMLParser({
     hasAttributes: true
   }).parseFromString(str);
@@ -192,15 +186,27 @@ function updateXFA(datasetsRef, newRefs, xref) {
 
   const buffer = [];
   xml.documentElement.dump(buffer);
-  let updatedXml = buffer.join("");
+  return buffer.join("");
+}
+
+function updateXFA(xfaData, datasetsRef, newRefs, xref) {
+  if (datasetsRef === null || xref === null) {
+    return;
+  }
+
+  if (xfaData === null) {
+    const datasets = xref.fetchIfRef(datasetsRef);
+    xfaData = writeXFADataForAcroform(datasets.getString(), newRefs);
+  }
+
   const encrypt = xref.encrypt;
 
   if (encrypt) {
     const transform = encrypt.createCipherTransform(datasetsRef.num, datasetsRef.gen);
-    updatedXml = transform.encryptString(updatedXml);
+    xfaData = transform.encryptString(xfaData);
   }
 
-  const data = `${datasetsRef.num} ${datasetsRef.gen} obj\n` + `<< /Type /EmbeddedFile /Length ${updatedXml.length}>>\nstream\n` + updatedXml + "\nendstream\nendobj\n";
+  const data = `${datasetsRef.num} ${datasetsRef.gen} obj\n` + `<< /Type /EmbeddedFile /Length ${xfaData.length}>>\nstream\n` + xfaData + "\nendstream\nendobj\n";
   newRefs.push({
     ref: datasetsRef,
     data
@@ -212,9 +218,10 @@ function incrementalUpdate({
   xrefInfo,
   newRefs,
   xref = null,
-  datasetsRef = null
+  datasetsRef = null,
+  xfaData = null
 }) {
-  updateXFA(datasetsRef, newRefs, xref);
+  updateXFA(xfaData, datasetsRef, newRefs, xref);
   const newXref = new _primitives.Dict(null);
   const refForXrefTable = xrefInfo.newRef;
   let buffer, baseOffset;

+ 66 - 30
lib/core/xfa/bind.js

@@ -48,13 +48,12 @@ class Binder {
     this.datasets = root.datasets;
 
     if (root.datasets && root.datasets.data) {
-      this.emptyMerge = false;
       this.data = root.datasets.data;
     } else {
-      this.emptyMerge = true;
       this.data = new _xfa_object.XmlObject(_namespaces.NamespaceIds.datasets.id, "data");
     }
 
+    this.emptyMerge = this.data[_xfa_object.$getChildren]().length === 0;
     this.root.form = this.form = root.template[_xfa_object.$clone]();
   }
 
@@ -77,26 +76,22 @@ class Binder {
   }
 
   _bindValue(formNode, data, picture) {
+    formNode[_xfa_object.$data] = data;
+
     if (formNode[_xfa_object.$hasSettableValue]()) {
       if (data[_xfa_object.$isDataValue]()) {
         const value = data[_xfa_object.$getDataValue]();
 
         formNode[_xfa_object.$setValue](createText(value));
-
-        formNode[_xfa_object.$data] = data;
       } else if (formNode instanceof _template.Field && formNode.ui && formNode.ui.choiceList && formNode.ui.choiceList.open === "multiSelect") {
         const value = data[_xfa_object.$getChildren]().map(child => child[_xfa_object.$content].trim()).join("\n");
 
         formNode[_xfa_object.$setValue](createText(value));
-
-        formNode[_xfa_object.$data] = data;
       } else if (this._isConsumeData()) {
         (0, _util.warn)(`XFA - Nodes haven't the same type.`);
       }
     } else if (!data[_xfa_object.$isDataValue]() || this._isMatchTemplate()) {
       this._bindElement(formNode, data);
-
-      formNode[_xfa_object.$data] = data;
     } else {
       (0, _util.warn)(`XFA - Nodes haven't the same type.`);
     }
@@ -135,18 +130,11 @@ class Binder {
       return null;
     }
 
-    generator = this.data[_xfa_object.$getRealChildrenByNameIt](name, false, false);
-
-    while (true) {
-      match = generator.next().value;
-
-      if (!match) {
-        break;
-      }
+    generator = this.data[_xfa_object.$getRealChildrenByNameIt](name, true, false);
+    match = generator.next().value;
 
-      if (match[_xfa_object.$global]) {
-        return match;
-      }
+    if (match) {
+      return match;
     }
 
     generator = this.data[_xfa_object.$getAttributeIt](name, true);
@@ -177,25 +165,29 @@ class Binder {
         continue;
       }
 
-      const [node] = (0, _som.searchNode)(this.root, dataNode, ref, false, false);
+      const nodes = (0, _som.searchNode)(this.root, dataNode, ref, false, false);
 
-      if (!node) {
+      if (!nodes) {
         (0, _util.warn)(`XFA - Invalid reference: ${ref}.`);
         continue;
       }
 
+      const [node] = nodes;
+
       if (!node[_xfa_object.$isDescendent](this.data)) {
         (0, _util.warn)(`XFA - Invalid node: must be a data node.`);
         continue;
       }
 
-      const [targetNode] = (0, _som.searchNode)(this.root, formNode, target, false, false);
+      const targetNodes = (0, _som.searchNode)(this.root, formNode, target, false, false);
 
-      if (!targetNode) {
+      if (!targetNodes) {
         (0, _util.warn)(`XFA - Invalid target: ${target}.`);
         continue;
       }
 
+      const [targetNode] = targetNodes;
+
       if (!targetNode[_xfa_object.$isDescendent](formNode)) {
         (0, _util.warn)(`XFA - Invalid target: must be a property or subproperty.`);
         continue;
@@ -285,25 +277,29 @@ class Binder {
           continue;
         }
 
-        const [labelNode] = (0, _som.searchNode)(this.root, node, labelRef, true, false);
+        const labelNodes = (0, _som.searchNode)(this.root, node, labelRef, true, false);
 
-        if (!labelNode) {
+        if (!labelNodes) {
           (0, _util.warn)(`XFA - Invalid label: ${labelRef}.`);
           continue;
         }
 
+        const [labelNode] = labelNodes;
+
         if (!labelNode[_xfa_object.$isDescendent](this.datasets)) {
           (0, _util.warn)(`XFA - Invalid label: must be a datasets child.`);
           continue;
         }
 
-        const [valueNode] = (0, _som.searchNode)(this.root, node, valueRef, true, false);
+        const valueNodes = (0, _som.searchNode)(this.root, node, valueRef, true, false);
 
-        if (!valueNode) {
+        if (!valueNodes) {
           (0, _util.warn)(`XFA - Invalid value: ${valueRef}.`);
           continue;
         }
 
+        const [valueNode] = valueNodes;
+
         if (!valueNode[_xfa_object.$isDescendent](this.datasets)) {
           (0, _util.warn)(`XFA - Invalid value: must be a datasets child.`);
           continue;
@@ -421,6 +417,24 @@ class Binder {
 
       if (this._mergeMode === undefined && child[_xfa_object.$nodeName] === "subform") {
         this._mergeMode = child.mergeMode === "consumeData";
+
+        const dataChildren = dataNode[_xfa_object.$getChildren]();
+
+        if (dataChildren.length > 0) {
+          this._bindOccurrences(child, [dataChildren[0]], null);
+        } else if (this.emptyMerge) {
+          const dataChild = child[_xfa_object.$data] = new _xfa_object.XmlObject(dataNode[_xfa_object.$namespaceId], child.name || "root");
+
+          dataNode[_xfa_object.$appendChild](dataChild);
+
+          this._bindElement(child, dataChild);
+        }
+
+        continue;
+      }
+
+      if (!child[_xfa_object.$isBindable]()) {
+        continue;
       }
 
       let global = false;
@@ -468,11 +482,17 @@ class Binder {
         if (match === null) {
           match = (0, _som.createDataNode)(this.data, dataNode, ref);
 
+          if (!match) {
+            continue;
+          }
+
           if (this._isConsumeData()) {
             match[_xfa_object.$consumed] = true;
           }
 
-          match = [match];
+          this._bindElement(child, match);
+
+          continue;
         } else {
           if (this._isConsumeData()) {
             match = match.filter(node => !node[_xfa_object.$consumed]);
@@ -513,12 +533,28 @@ class Binder {
 
           match = matches.length > 0 ? matches : null;
         } else {
-          match = dataNode[_xfa_object.$getRealChildrenByNameIt](child.name, false, false).next().value;
+          match = dataNode[_xfa_object.$getRealChildrenByNameIt](child.name, false, this.emptyMerge).next().value;
 
           if (!match) {
-            match = new _xfa_object.XmlObject(dataNode[_xfa_object.$namespaceId], child.name);
+            match = child[_xfa_object.$data] = new _xfa_object.XmlObject(dataNode[_xfa_object.$namespaceId], child.name);
+
+            if (this.emptyMerge) {
+              match[_xfa_object.$consumed] = true;
+            }
 
             dataNode[_xfa_object.$appendChild](match);
+
+            this._setProperties(child, match);
+
+            this._bindItems(child, match);
+
+            this._bindElement(child, match);
+
+            continue;
+          }
+
+          if (this.emptyMerge) {
+            match[_xfa_object.$consumed] = true;
           }
 
           match = [match];

+ 20 - 3
lib/core/xfa/builder.js

@@ -54,6 +54,8 @@ class Root extends _xfa_object.XFAObject {
     super[_xfa_object.$finalize]();
 
     if (this.element.template instanceof _template.Template) {
+      this[_xfa_object.$ids].set(_xfa_object.$root, this.element);
+
       this.element.template[_xfa_object.$resolvePrototypes](this[_xfa_object.$ids]);
 
       this.element.template[_xfa_object.$ids] = this[_xfa_object.$ids];
@@ -76,6 +78,7 @@ class Empty extends _xfa_object.XFAObject {
 class Builder {
   constructor() {
     this._namespaceStack = [];
+    this._nsAgnosticLevel = 0;
     this._namespacePrefixes = new Map();
     this._namespaces = new Map();
     this._nextNsId = Math.max(...Object.values(_namespaces.NamespaceIds).map(({
@@ -134,16 +137,25 @@ class Builder {
 
     const node = namespaceToUse && namespaceToUse[_namespaces.$buildXFAObject](name, attributes) || new Empty();
 
-    if (hasNamespaceDef || prefixes) {
+    if (node[_xfa_object.$isNsAgnostic]()) {
+      this._nsAgnosticLevel++;
+    }
+
+    if (hasNamespaceDef || prefixes || node[_xfa_object.$isNsAgnostic]()) {
       node[_xfa_object.$cleanup] = {
         hasNamespace: hasNamespaceDef,
-        prefixes
+        prefixes,
+        nsAgnostic: node[_xfa_object.$isNsAgnostic]()
       };
     }
 
     return node;
   }
 
+  isNsAgnostic() {
+    return this._nsAgnosticLevel > 0;
+  }
+
   _searchNamespace(nsName) {
     let ns = this._namespaces.get(nsName);
 
@@ -211,7 +223,8 @@ class Builder {
   clean(data) {
     const {
       hasNamespace,
-      prefixes
+      prefixes,
+      nsAgnostic
     } = data;
 
     if (hasNamespace) {
@@ -225,6 +238,10 @@ class Builder {
         this._namespacePrefixes.get(prefix).pop();
       });
     }
+
+    if (nsAgnostic) {
+      this._nsAgnosticLevel--;
+    }
   }
 
 }

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

@@ -170,7 +170,7 @@ class BehaviorOverride extends _xfa_object.ContentObject {
   }
 
   [_xfa_object.$finalize]() {
-    this[_xfa_object.$content] = new Map(this[_xfa_object.$content].trim().split(/\s+/).filter(x => !!x && x.include(":")).map(x => x.split(":", 2)));
+    this[_xfa_object.$content] = new Map(this[_xfa_object.$content].trim().split(/\s+/).filter(x => x.includes(":")).map(x => x.split(":", 2)));
   }
 
 }

+ 93 - 0
lib/core/xfa/data.js

@@ -0,0 +1,93 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.DataHandler = void 0;
+
+var _xfa_object = require("./xfa_object.js");
+
+class DataHandler {
+  constructor(root, data) {
+    this.data = data;
+    this.dataset = root.datasets || null;
+  }
+
+  serialize(storage) {
+    const stack = [[-1, this.data[_xfa_object.$getChildren]()]];
+
+    while (stack.length > 0) {
+      const last = stack[stack.length - 1];
+      const [i, children] = last;
+
+      if (i + 1 === children.length) {
+        stack.pop();
+        continue;
+      }
+
+      const child = children[++last[0]];
+      const storageEntry = storage.get(child[_xfa_object.$uid]);
+
+      if (storageEntry) {
+        child[_xfa_object.$setValue](storageEntry);
+      } else {
+        const attributes = child[_xfa_object.$getAttributes]();
+
+        for (const value of attributes.values()) {
+          const entry = storage.get(value[_xfa_object.$uid]);
+
+          if (entry) {
+            value[_xfa_object.$setValue](entry);
+
+            break;
+          }
+        }
+      }
+
+      const nodes = child[_xfa_object.$getChildren]();
+
+      if (nodes.length > 0) {
+        stack.push([-1, nodes]);
+      }
+    }
+
+    const buf = [`<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">`];
+
+    if (this.dataset) {
+      for (const child of this.dataset[_xfa_object.$getChildren]()) {
+        if (child[_xfa_object.$nodeName] !== "data") {
+          child[_xfa_object.$toString](buf);
+        }
+      }
+    }
+
+    this.data[_xfa_object.$toString](buf);
+
+    buf.push("</xfa:datasets>");
+    return buf.join("");
+  }
+
+}
+
+exports.DataHandler = DataHandler;

+ 4 - 2
lib/core/xfa/datasets.js

@@ -37,6 +37,10 @@ class Data extends _xfa_object.XmlObject {
     super(DATASETS_NS_ID, "data", attributes);
   }
 
+  [_xfa_object.$isNsAgnostic]() {
+    return true;
+  }
+
 }
 
 class Datasets extends _xfa_object.XFAObject {
@@ -51,8 +55,6 @@ class Datasets extends _xfa_object.XFAObject {
 
     if (name === "data" && child[_xfa_object.$namespaceId] === DATASETS_NS_ID || name === "Signature" && child[_xfa_object.$namespaceId] === _namespaces.NamespaceIds.signature.id) {
       this[name] = child;
-    } else {
-      child[_xfa_object.$global] = true;
     }
 
     this[_xfa_object.$appendChild](child);

+ 81 - 5
lib/core/xfa/factory.js

@@ -30,25 +30,101 @@ var _xfa_object = require("./xfa_object.js");
 
 var _bind = require("./bind.js");
 
+var _data = require("./data.js");
+
+var _fonts = require("./fonts.js");
+
+var _utils = require("./utils.js");
+
+var _util = require("../../shared/util.js");
+
 var _parser = require("./parser.js");
 
 class XFAFactory {
   constructor(data) {
     try {
       this.root = new _parser.XFAParser().parse(XFAFactory._createDocument(data));
-      this.form = new _bind.Binder(this.root).bind();
+      const binder = new _bind.Binder(this.root);
+      this.form = binder.bind();
+      this.dataHandler = new _data.DataHandler(this.root, binder.getData());
+      this.form[_xfa_object.$globalData].template = this.form;
+    } catch (e) {
+      (0, _util.warn)(`XFA - an error occurred during parsing and binding: ${e}`);
+    }
+  }
+
+  isValid() {
+    return this.root && this.form;
+  }
+
+  _createPages() {
+    try {
       this.pages = this.form[_xfa_object.$toHTML]();
+      this.dims = this.pages.children.map(c => {
+        const {
+          width,
+          height
+        } = c.attributes.style;
+        return [0, 0, parseInt(width), parseInt(height)];
+      });
     } catch (e) {
-      console.log(e);
+      (0, _util.warn)(`XFA - an error occurred during layout: ${e}`);
     }
   }
 
-  getPage(pageIndex) {
-    return this.pages.children[pageIndex];
+  getBoundingBox(pageIndex) {
+    return this.dims[pageIndex];
   }
 
   get numberPages() {
-    return this.pages.children.length;
+    if (!this.pages) {
+      this._createPages();
+    }
+
+    return this.dims.length;
+  }
+
+  setImages(images) {
+    this.form[_xfa_object.$globalData].images = images;
+  }
+
+  setFonts(fonts) {
+    this.form[_xfa_object.$globalData].fontFinder = new _fonts.FontFinder(fonts);
+    const missingFonts = [];
+
+    for (let typeface of this.form[_xfa_object.$globalData].usedTypefaces) {
+      typeface = (0, _utils.stripQuotes)(typeface);
+
+      const font = this.form[_xfa_object.$globalData].fontFinder.find(typeface);
+
+      if (!font) {
+        missingFonts.push(typeface);
+      }
+    }
+
+    if (missingFonts.length > 0) {
+      return missingFonts;
+    }
+
+    return null;
+  }
+
+  appendFonts(fonts, reallyMissingFonts) {
+    this.form[_xfa_object.$globalData].fontFinder.add(fonts, reallyMissingFonts);
+  }
+
+  getPages() {
+    if (!this.pages) {
+      this._createPages();
+    }
+
+    const pages = this.pages;
+    this.pages = null;
+    return pages;
+  }
+
+  serializeData(storage) {
+    return this.dataHandler.serialize(storage);
   }
 
   static _createDocument(data) {

+ 191 - 0
lib/core/xfa/fonts.js

@@ -0,0 +1,191 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.selectFont = selectFont;
+exports.FontFinder = void 0;
+
+var _util = require("../../shared/util.js");
+
+class FontFinder {
+  constructor(pdfFonts) {
+    this.fonts = new Map();
+    this.cache = new Map();
+    this.warned = new Set();
+    this.defaultFont = null;
+    this.add(pdfFonts);
+  }
+
+  add(pdfFonts, reallyMissingFonts = null) {
+    for (const pdfFont of pdfFonts) {
+      this.addPdfFont(pdfFont);
+    }
+
+    for (const pdfFont of this.fonts.values()) {
+      if (!pdfFont.regular) {
+        pdfFont.regular = pdfFont.italic || pdfFont.bold || pdfFont.bolditalic;
+      }
+    }
+
+    if (!reallyMissingFonts || reallyMissingFonts.size === 0) {
+      return;
+    }
+
+    const myriad = this.fonts.get("PdfJS-Fallback-PdfJS-XFA");
+
+    for (const missing of reallyMissingFonts) {
+      this.fonts.set(missing, myriad);
+    }
+  }
+
+  addPdfFont(pdfFont) {
+    const cssFontInfo = pdfFont.cssFontInfo;
+    const name = cssFontInfo.fontFamily;
+    let font = this.fonts.get(name);
+
+    if (!font) {
+      font = Object.create(null);
+      this.fonts.set(name, font);
+
+      if (!this.defaultFont) {
+        this.defaultFont = font;
+      }
+    }
+
+    let property = "";
+    const fontWeight = parseFloat(cssFontInfo.fontWeight);
+
+    if (parseFloat(cssFontInfo.italicAngle) !== 0) {
+      property = fontWeight >= 700 ? "bolditalic" : "italic";
+    } else if (fontWeight >= 700) {
+      property = "bold";
+    }
+
+    if (!property) {
+      if (pdfFont.name.includes("Bold") || pdfFont.psName && pdfFont.psName.includes("Bold")) {
+        property = "bold";
+      }
+
+      if (pdfFont.name.includes("Italic") || pdfFont.name.endsWith("It") || pdfFont.psName && (pdfFont.psName.includes("Italic") || pdfFont.psName.endsWith("It"))) {
+        property += "italic";
+      }
+    }
+
+    if (!property) {
+      property = "regular";
+    }
+
+    font[property] = pdfFont;
+  }
+
+  getDefault() {
+    return this.defaultFont;
+  }
+
+  find(fontName, mustWarn = true) {
+    let font = this.fonts.get(fontName) || this.cache.get(fontName);
+
+    if (font) {
+      return font;
+    }
+
+    const pattern = /,|-|_| |bolditalic|bold|italic|regular|it/gi;
+    let name = fontName.replace(pattern, "");
+    font = this.fonts.get(name);
+
+    if (font) {
+      this.cache.set(fontName, font);
+      return font;
+    }
+
+    name = name.toLowerCase();
+    const maybe = [];
+
+    for (const [family, pdfFont] of this.fonts.entries()) {
+      if (family.replace(pattern, "").toLowerCase().startsWith(name)) {
+        maybe.push(pdfFont);
+      }
+    }
+
+    if (maybe.length === 0) {
+      for (const [, pdfFont] of this.fonts.entries()) {
+        if (pdfFont.regular.name && pdfFont.regular.name.replace(pattern, "").toLowerCase().startsWith(name)) {
+          maybe.push(pdfFont);
+        }
+      }
+    }
+
+    if (maybe.length === 0) {
+      name = name.replace(/psmt|mt/gi, "");
+
+      for (const [family, pdfFont] of this.fonts.entries()) {
+        if (family.replace(pattern, "").toLowerCase().startsWith(name)) {
+          maybe.push(pdfFont);
+        }
+      }
+    }
+
+    if (maybe.length === 0) {
+      for (const pdfFont of this.fonts.values()) {
+        if (pdfFont.regular.name && pdfFont.regular.name.replace(pattern, "").toLowerCase().startsWith(name)) {
+          maybe.push(pdfFont);
+        }
+      }
+    }
+
+    if (maybe.length >= 1) {
+      if (maybe.length !== 1 && mustWarn) {
+        (0, _util.warn)(`XFA - Too many choices to guess the correct font: ${fontName}`);
+      }
+
+      this.cache.set(fontName, maybe[0]);
+      return maybe[0];
+    }
+
+    if (mustWarn && !this.warned.has(fontName)) {
+      this.warned.add(fontName);
+      (0, _util.warn)(`XFA - Cannot find the font: ${fontName}`);
+    }
+
+    return null;
+  }
+
+}
+
+exports.FontFinder = FontFinder;
+
+function selectFont(xfaFont, typeface) {
+  if (xfaFont.posture === "italic") {
+    if (xfaFont.weight === "bold") {
+      return typeface.bolditalic;
+    }
+
+    return typeface.italic;
+  } else if (xfaFont.weight === "bold") {
+    return typeface.bold;
+  }
+
+  return typeface.regular;
+}

+ 297 - 236
lib/core/xfa/html_utils.js

@@ -24,24 +24,29 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.addExtraDivForBorder = addExtraDivForBorder;
 exports.computeBbox = computeBbox;
+exports.createWrapper = createWrapper;
 exports.fixDimensions = fixDimensions;
 exports.fixTextIndent = fixTextIndent;
-exports.getFonts = getFonts;
+exports.isPrintOnly = isPrintOnly;
 exports.layoutClass = layoutClass;
-exports.layoutText = layoutText;
+exports.layoutNode = layoutNode;
 exports.measureToString = measureToString;
+exports.setAccess = setAccess;
+exports.setFontFamily = setFontFamily;
+exports.setMinMaxDimensions = setMinMaxDimensions;
+exports.setPara = setPara;
 exports.toStyle = toStyle;
 
 var _xfa_object = require("./xfa_object.js");
 
 var _utils = require("./utils.js");
 
-var _util = require("../../shared/util.js");
+var _fonts = require("./fonts.js");
+
+var _text = require("./text.js");
 
-const wordNonWordRegex = new RegExp("([\\p{N}\\p{L}\\p{M}]+)|([^\\p{N}\\p{L}\\p{M}]+)", "gu");
-const wordFirstRegex = new RegExp("^[\\p{N}\\p{L}\\p{M}]", "u");
+var _util = require("../../shared/util.js");
 
 function measureToString(m) {
   if (typeof m === "string") {
@@ -53,7 +58,7 @@ function measureToString(m) {
 
 const converters = {
   anchorType(node, style) {
-    const parent = node[_xfa_object.$getParent]();
+    const parent = node[_xfa_object.$getSubformParent]();
 
     if (!parent || parent.layout && parent.layout !== "position") {
       return;
@@ -99,7 +104,7 @@ const converters = {
   },
 
   dimensions(node, style) {
-    const parent = node[_xfa_object.$getParent]();
+    const parent = node[_xfa_object.$getSubformParent]();
 
     let width = node.w;
     const height = node.h;
@@ -126,33 +131,17 @@ const converters = {
       style.width = measureToString(width);
     } else {
       style.width = "auto";
-
-      if (node.maxW > 0) {
-        style.maxWidth = measureToString(node.maxW);
-      }
-
-      if (parent.layout === "position") {
-        style.minWidth = measureToString(node.minW);
-      }
     }
 
     if (height !== "") {
       style.height = measureToString(height);
     } else {
       style.height = "auto";
-
-      if (node.maxH > 0) {
-        style.maxHeight = measureToString(node.maxH);
-      }
-
-      if (parent.layout === "position") {
-        style.minHeight = measureToString(node.minH);
-      }
     }
   },
 
   position(node, style) {
-    const parent = node[_xfa_object.$getParent]();
+    const parent = node[_xfa_object.$getSubformParent]();
 
     if (parent && parent.layout && parent.layout !== "position") {
       return;
@@ -203,129 +192,138 @@ const converters = {
       }
     } else {
       switch (node.hAlign) {
-        case "right":
+        case "left":
+          style.alignSelf = "start";
+          break;
+
         case "center":
-          style.justifyContent = node.hAlign;
+          style.alignSelf = "center";
+          break;
+
+        case "right":
+          style.alignSelf = "end";
           break;
       }
     }
   },
 
-  borderMarginPadding(node, style) {
-    const borderWidths = [0, 0, 0, 0];
-    const borderInsets = [0, 0, 0, 0];
-    const marginNode = node.margin ? [node.margin.topInset, node.margin.rightInset, node.margin.bottomInset, node.margin.leftInset] : [0, 0, 0, 0];
-    let borderMargin;
-
-    if (node.border) {
-      Object.assign(style, node.border[_xfa_object.$toStyle](borderWidths, borderInsets));
-      borderMargin = style.margin;
-      delete style.margin;
+  margin(node, style) {
+    if (node.margin) {
+      style.margin = node.margin[_xfa_object.$toStyle]().margin;
     }
+  }
 
-    if (borderWidths.every(x => x === 0)) {
-      if (marginNode.every(x => x === 0)) {
-        return;
-      }
+};
 
-      Object.assign(style, node.margin[_xfa_object.$toStyle]());
-      style.padding = style.margin;
-      delete style.margin;
-      delete style.outline;
-      delete style.outlineOffset;
-      return;
+function setMinMaxDimensions(node, style) {
+  const parent = node[_xfa_object.$getSubformParent]();
+
+  if (parent.layout === "position") {
+    if (node.minW > 0) {
+      style.minWidth = measureToString(node.minW);
     }
 
-    if (node.margin) {
-      Object.assign(style, node.margin[_xfa_object.$toStyle]());
-      style.padding = style.margin;
-      delete style.margin;
+    if (node.maxW > 0) {
+      style.maxWidth = measureToString(node.maxW);
     }
 
-    if (!style.borderWidth) {
-      return;
+    if (node.minH > 0) {
+      style.minHeight = measureToString(node.minH);
     }
 
-    style.borderData = {
-      borderWidth: style.borderWidth,
-      borderColor: style.borderColor,
-      borderStyle: style.borderStyle,
-      margin: borderMargin
-    };
-    delete style.borderWidth;
-    delete style.borderColor;
-    delete style.borderStyle;
+    if (node.maxH > 0) {
+      style.maxHeight = measureToString(node.maxH);
+    }
   }
+}
 
-};
+function layoutText(text, xfaFont, margin, lineHeight, fontFinder, width) {
+  const measure = new _text.TextMeasure(xfaFont, margin, lineHeight, fontFinder);
 
-function layoutText(text, fontSize, space) {
-  let width = 0;
-  let height = 0;
-  let totalWidth = 0;
-  const lineHeight = fontSize * 1.5;
-  const averageCharSize = fontSize * 0.4;
-  const maxCharOnLine = Math.floor(space.width / averageCharSize);
-  const chunks = text.match(wordNonWordRegex);
-  let treatedChars = 0;
-  let i = 0;
-  let chunk = chunks[0];
-
-  while (chunk) {
-    const w = chunk.length * averageCharSize;
-
-    if (width + w <= space.width) {
-      width += w;
-      treatedChars += chunk.length;
-      chunk = chunks[i++];
-      continue;
-    }
+  if (typeof text === "string") {
+    measure.addString(text);
+  } else {
+    text[_xfa_object.$pushGlyphs](measure);
+  }
 
-    if (!wordFirstRegex.test(chunk) || chunk.length > maxCharOnLine) {
-      const numOfCharOnLine = Math.floor((space.width - width) / averageCharSize);
-      chunk = chunk.slice(numOfCharOnLine);
-      treatedChars += numOfCharOnLine;
+  return measure.compute(width);
+}
 
-      if (height + lineHeight > space.height) {
-        return {
-          width: 0,
-          height: 0,
-          splitPos: treatedChars
-        };
-      }
+function layoutNode(node, availableSpace) {
+  let height = null;
+  let width = null;
+  let isBroken = false;
 
-      totalWidth = Math.max(width, totalWidth);
-      width = 0;
-      height += lineHeight;
-      continue;
+  if ((!node.w || !node.h) && node.value) {
+    let marginH = 0;
+    let marginV = 0;
+
+    if (node.margin) {
+      marginH = node.margin.leftInset + node.margin.rightInset;
+      marginV = node.margin.topInset + node.margin.bottomInset;
     }
 
-    if (height + lineHeight > space.height) {
-      return {
-        width: 0,
-        height: 0,
-        splitPos: treatedChars
-      };
+    let lineHeight = null;
+    let margin = null;
+
+    if (node.para) {
+      margin = Object.create(null);
+      lineHeight = node.para.lineHeight === "" ? null : node.para.lineHeight;
+      margin.top = node.para.spaceAbove === "" ? 0 : node.para.spaceAbove;
+      margin.bottom = node.para.spaceBelow === "" ? 0 : node.para.spaceBelow;
+      margin.left = node.para.marginLeft === "" ? 0 : node.para.marginLeft;
+      margin.right = node.para.marginRight === "" ? 0 : node.para.marginRight;
     }
 
-    totalWidth = Math.max(width, totalWidth);
-    width = w;
-    height += lineHeight;
-    chunk = chunks[i++];
-  }
+    let font = node.font;
 
-  if (totalWidth === 0) {
-    totalWidth = width;
-  }
+    if (!font) {
+      const root = node[_xfa_object.$getTemplateRoot]();
+
+      let parent = node[_xfa_object.$getParent]();
+
+      while (parent !== root) {
+        if (parent.font) {
+          font = parent.font;
+          break;
+        }
+
+        parent = parent[_xfa_object.$getParent]();
+      }
+    }
 
-  if (totalWidth !== 0) {
-    height += lineHeight;
+    const maxWidth = !node.w ? availableSpace.width : node.w;
+    const fontFinder = node[_xfa_object.$globalData].fontFinder;
+
+    if (node.value.exData && node.value.exData[_xfa_object.$content] && node.value.exData.contentType === "text/html") {
+      const res = layoutText(node.value.exData[_xfa_object.$content], font, margin, lineHeight, fontFinder, maxWidth);
+      width = res.width;
+      height = res.height;
+      isBroken = res.isBroken;
+    } else {
+      const text = node.value[_xfa_object.$text]();
+
+      if (text) {
+        const res = layoutText(text, font, margin, lineHeight, fontFinder, maxWidth);
+        width = res.width;
+        height = res.height;
+        isBroken = res.isBroken;
+      }
+    }
+
+    if (width !== null && !node.w) {
+      width += marginH;
+    }
+
+    if (height !== null && !node.h) {
+      height += marginV;
+    }
   }
 
   return {
-    width: totalWidth,
-    height,
-    splitPos: -1
+    w: width,
+    h: height,
+    isBroken
   };
 }
 
@@ -343,7 +341,7 @@ function computeBbox(node, html, availableSpace) {
 
     if (width === "") {
       if (node.maxW === 0) {
-        const parent = node[_xfa_object.$getParent]();
+        const parent = node[_xfa_object.$getSubformParent]();
 
         if (parent.layout === "position" && parent.w !== "") {
           width = 0;
@@ -361,7 +359,7 @@ function computeBbox(node, html, availableSpace) {
 
     if (height === "") {
       if (node.maxH === 0) {
-        const parent = node[_xfa_object.$getParent]();
+        const parent = node[_xfa_object.$getSubformParent]();
 
         if (parent.layout === "position" && parent.h !== "") {
           height = 0;
@@ -382,7 +380,7 @@ function computeBbox(node, html, availableSpace) {
 }
 
 function fixDimensions(node) {
-  const parent = node[_xfa_object.$getParent]();
+  const parent = node[_xfa_object.$getSubformParent]();
 
   if (parent.layout && parent.layout.includes("row")) {
     const extra = parent[_xfa_object.$extra];
@@ -400,32 +398,13 @@ function fixDimensions(node) {
     }
   }
 
-  if (parent.w && node.w) {
-    node.w = Math.min(parent.w, node.w);
-  }
-
-  if (parent.h && node.h) {
-    node.h = Math.min(parent.h, node.h);
-  }
-
   if (parent.layout && parent.layout !== "position") {
     node.x = node.y = 0;
-
-    if (parent.layout === "tb") {
-      if (parent.w !== "" && (node.w === "" || node.w === 0 || node.w > parent.w)) {
-        node.w = parent.w;
-      }
-    }
   }
 
-  if (node.layout === "position") {
-    node.minW = node.minH = 0;
-    node.maxW = node.maxH = Infinity;
-  } else {
-    if (node.layout === "table") {
-      if (node.w === "" && Array.isArray(node.columnWidths)) {
-        node.w = node.columnWidths.reduce((a, x) => a + x, 0);
-      }
+  if (node.layout === "table") {
+    if (node.w === "" && Array.isArray(node.columnWidths)) {
+      node.w = node.columnWidths.reduce((a, x) => a + x, 0);
     }
   }
 }
@@ -468,6 +447,11 @@ function toStyle(node, ...names) {
       continue;
     }
 
+    if (converters.hasOwnProperty(name)) {
+      converters[name](node, style);
+      continue;
+    }
+
     if (value instanceof _xfa_object.XFAObject) {
       const newStyle = value[_xfa_object.$toStyle]();
 
@@ -476,138 +460,215 @@ function toStyle(node, ...names) {
       } else {
         (0, _util.warn)(`(DEBUG) - XFA - style for ${name} not implemented yet`);
       }
-
-      continue;
-    }
-
-    if (converters.hasOwnProperty(name)) {
-      converters[name](node, style);
     }
   }
 
   return style;
 }
 
-function addExtraDivForBorder(html) {
-  const style = html.attributes.style;
-  const data = style.borderData;
-  const children = [];
-  const attributes = {
-    class: "xfaWrapper",
-    style: Object.create(null)
+function createWrapper(node, html) {
+  const {
+    attributes
+  } = html;
+  const {
+    style
+  } = attributes;
+  const wrapper = {
+    name: "div",
+    attributes: {
+      class: ["xfaWrapper"],
+      style: Object.create(null)
+    },
+    children: []
   };
+  attributes.class.push("xfaWrapped");
+
+  if (node.border) {
+    const {
+      widths,
+      insets
+    } = node.border[_xfa_object.$extra];
+    let width, height;
+    let top = insets[0];
+    let left = insets[3];
+    const insetsH = insets[0] + insets[2];
+    const insetsW = insets[1] + insets[3];
+
+    switch (node.border.hand) {
+      case "even":
+        top -= widths[0] / 2;
+        left -= widths[3] / 2;
+        width = `calc(100% + ${(widths[1] + widths[3]) / 2 - insetsW}px)`;
+        height = `calc(100% + ${(widths[0] + widths[2]) / 2 - insetsH}px)`;
+        break;
 
-  for (const key of ["top", "left"]) {
-    if (style[key] !== undefined) {
-      attributes.style[key] = style[key];
+      case "left":
+        top -= widths[0];
+        left -= widths[3];
+        width = `calc(100% + ${widths[1] + widths[3] - insetsW}px)`;
+        height = `calc(100% + ${widths[0] + widths[2] - insetsH}px)`;
+        break;
+
+      case "right":
+        width = insetsW ? `calc(100% - ${insetsW}px)` : "100%";
+        height = insetsH ? `calc(100% - ${insetsH}px)` : "100%";
+        break;
+    }
+
+    const classNames = ["xfaBorder"];
+
+    if (isPrintOnly(node.border)) {
+      classNames.push("xfaPrintOnly");
+    }
+
+    const border = {
+      name: "div",
+      attributes: {
+        class: classNames,
+        style: {
+          top: `${top}px`,
+          left: `${left}px`,
+          width,
+          height
+        }
+      },
+      children: []
+    };
+
+    for (const key of ["border", "borderWidth", "borderColor", "borderRadius", "borderStyle"]) {
+      if (style[key] !== undefined) {
+        border.attributes.style[key] = style[key];
+        delete style[key];
+      }
     }
+
+    wrapper.children.push(border, html);
+  } else {
+    wrapper.children.push(html);
   }
 
-  delete style.top;
-  delete style.left;
+  for (const key of ["background", "backgroundClip", "top", "left", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "transform", "transformOrigin", "visibility"]) {
+    if (style[key] !== undefined) {
+      wrapper.attributes.style[key] = style[key];
+      delete style[key];
+    }
+  }
 
   if (style.position === "absolute") {
-    attributes.style.position = "absolute";
+    wrapper.attributes.style.position = "absolute";
   } else {
-    attributes.style.position = "relative";
+    wrapper.attributes.style.position = "relative";
   }
 
   delete style.position;
 
-  if (style.justifyContent) {
-    attributes.style.justifyContent = style.justifyContent;
-    delete style.justifyContent;
+  if (style.alignSelf) {
+    wrapper.attributes.style.alignSelf = style.alignSelf;
+    delete style.alignSelf;
   }
 
-  if (data) {
-    delete style.borderData;
-    let insets;
+  return wrapper;
+}
 
-    if (data.margin) {
-      insets = data.margin.split(" ");
-      delete data.margin;
-    } else {
-      insets = ["0px", "0px", "0px", "0px"];
-    }
+function fixTextIndent(styles) {
+  const indent = (0, _utils.getMeasurement)(styles.textIndent, "0px");
 
-    let width = "100%";
-    let height = width;
+  if (indent >= 0) {
+    return;
+  }
 
-    if (insets[1] !== "0px" || insets[3] !== "0px") {
-      width = `calc(100% - ${parseInt(insets[1]) + parseInt(insets[3])}px`;
-    }
+  const align = styles.textAlign === "right" ? "right" : "left";
+  const name = "padding" + (align === "left" ? "Left" : "Right");
+  const padding = (0, _utils.getMeasurement)(styles[name], "0px");
+  styles[name] = `${padding - indent}px`;
+}
 
-    if (insets[0] !== "0px" || insets[2] !== "0px") {
-      height = `calc(100% - ${parseInt(insets[0]) + parseInt(insets[2])}px`;
-    }
+function setAccess(node, classNames) {
+  switch (node.access) {
+    case "nonInteractive":
+      classNames.push("xfaNonInteractive");
+      break;
 
-    const borderStyle = {
-      top: insets[0],
-      left: insets[3],
-      width,
-      height
-    };
+    case "readOnly":
+      classNames.push("xfaReadOnly");
+      break;
 
-    for (const [k, v] of Object.entries(data)) {
-      borderStyle[k] = v;
-    }
+    case "protected":
+      classNames.push("xfaDisabled");
+      break;
+  }
+}
 
-    if (style.transform) {
-      borderStyle.transform = style.transform;
-    }
+function isPrintOnly(node) {
+  return node.relevant.length > 0 && !node.relevant[0].excluded && node.relevant[0].viewname === "print";
+}
 
-    const borderDiv = {
-      name: "div",
-      attributes: {
-        class: "xfaBorderDiv",
-        style: borderStyle
+function setPara(node, nodeStyle, value) {
+  if (value.attributes.class && value.attributes.class.includes("xfaRich")) {
+    if (nodeStyle) {
+      if (node.h === "") {
+        nodeStyle.height = "auto";
       }
-    };
-    children.push(borderDiv);
-  }
 
-  children.push(html);
-  return {
-    name: "div",
-    attributes,
-    children
-  };
-}
+      if (node.w === "") {
+        nodeStyle.width = "auto";
+      }
+    }
 
-function fixTextIndent(styles) {
-  const indent = (0, _utils.getMeasurement)(styles.textIndent, "0px");
+    if (node.para) {
+      const valueStyle = value.attributes.style;
+      valueStyle.display = "flex";
+      valueStyle.flexDirection = "column";
 
-  if (indent >= 0) {
-    return;
-  }
+      switch (node.para.vAlign) {
+        case "top":
+          valueStyle.justifyContent = "start";
+          break;
 
-  const align = styles.textAlign || "left";
+        case "bottom":
+          valueStyle.justifyContent = "end";
+          break;
+
+        case "middle":
+          valueStyle.justifyContent = "center";
+          break;
+      }
+
+      const paraStyle = node.para[_xfa_object.$toStyle]();
 
-  if (align === "left" || align === "right") {
-    const name = "margin" + (align === "left" ? "Left" : "Right");
-    const margin = (0, _utils.getMeasurement)(styles[name], "0px");
-    styles[name] = `${margin - indent}pt`;
+      for (const [key, val] of Object.entries(paraStyle)) {
+        if (!(key in valueStyle)) {
+          valueStyle[key] = val;
+        }
+      }
+    }
   }
 }
 
-function getFonts(family) {
-  if (family.startsWith("'")) {
-    family = `"${family.slice(1, family.length - 1)}"`;
-  } else if (family.includes(" ") && !family.startsWith('"')) {
-    family = `"${family}"`;
-  }
+function setFontFamily(xfaFont, fontFinder, style) {
+  const name = (0, _utils.stripQuotes)(xfaFont.typeface);
+  const typeface = fontFinder.find(name);
+  style.fontFamily = `"${name}"`;
 
-  const fonts = [family];
+  if (typeface) {
+    const {
+      fontFamily
+    } = typeface.regular.cssFontInfo;
 
-  switch (family) {
-    case `"Myriad Pro"`:
-      fonts.push(`"Roboto Condensed"`, `"Ubuntu Condensed"`, `"Microsoft Sans Serif"`, `"Apple Symbols"`, "Helvetica", `"sans serif"`);
-      break;
+    if (fontFamily !== name) {
+      style.fontFamily = `"${fontFamily}"`;
+    }
 
-    case "Arial":
-      fonts.push("Helvetica", `"Liberation Sans"`, "Arimo", `"sans serif"`);
-      break;
-  }
+    if (style.lineHeight) {
+      return;
+    }
 
-  return fonts.join(",");
+    const pdfFont = (0, _fonts.selectFont)(xfaFont, typeface);
+
+    if (pdfFont && pdfFont.lineHeight > 0) {
+      style.lineHeight = Math.max(1.2, pdfFont.lineHeight);
+    } else {
+      style.lineHeight = 1.2;
+    }
+  }
 }

+ 221 - 43
lib/core/xfa/layout.js

@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.addHTML = addHTML;
+exports.checkDimensions = checkDimensions;
 exports.flushHTML = flushHTML;
 exports.getAvailableSpace = getAvailableSpace;
 
@@ -32,7 +33,21 @@ var _xfa_object = require("./xfa_object.js");
 
 var _html_utils = require("./html_utils.js");
 
+function createLine(node, children) {
+  return {
+    name: "div",
+    attributes: {
+      class: [node.layout === "lr-tb" ? "xfaLr" : "xfaRl"]
+    },
+    children
+  };
+}
+
 function flushHTML(node) {
+  if (!node[_xfa_object.$extra]) {
+    return null;
+  }
+
   const attributes = node[_xfa_object.$extra].attributes;
   const html = {
     name: "div",
@@ -44,7 +59,11 @@ function flushHTML(node) {
     const htmlFromFailing = node[_xfa_object.$extra].failingNode[_xfa_object.$flushHTML]();
 
     if (htmlFromFailing) {
-      html.children.push(htmlFromFailing);
+      if (node.layout.endsWith("-tb")) {
+        html.children.push(createLine(node, [htmlFromFailing]));
+      } else {
+        html.children.push(htmlFromFailing);
+      }
     }
   }
 
@@ -52,19 +71,17 @@ function flushHTML(node) {
     return null;
   }
 
-  node[_xfa_object.$extra].children = [];
-  delete node[_xfa_object.$extra].line;
   return html;
 }
 
 function addHTML(node, html, bbox) {
   const extra = node[_xfa_object.$extra];
   const availableSpace = extra.availableSpace;
+  const [x, y, w, h] = bbox;
 
   switch (node.layout) {
     case "position":
       {
-        const [x, y, w, h] = bbox;
         extra.width = Math.max(extra.width, x + w);
         extra.height = Math.max(extra.height, y + h);
         extra.children.push(html);
@@ -74,48 +91,37 @@ function addHTML(node, html, bbox) {
     case "lr-tb":
     case "rl-tb":
       if (!extra.line || extra.attempt === 1) {
-        extra.line = {
-          name: "div",
-          attributes: {
-            class: node.layout === "lr-tb" ? "xfaLr" : "xfaRl"
-          },
-          children: []
-        };
+        extra.line = createLine(node, []);
         extra.children.push(extra.line);
+        extra.numberInLine = 0;
       }
 
+      extra.numberInLine += 1;
       extra.line.children.push(html);
 
       if (extra.attempt === 0) {
-        const [,, w, h] = bbox;
         extra.currentWidth += w;
         extra.height = Math.max(extra.height, extra.prevHeight + h);
       } else {
-        const [,, w, h] = bbox;
-        extra.width = Math.max(extra.width, extra.currentWidth);
         extra.currentWidth = w;
         extra.prevHeight = extra.height;
         extra.height += h;
         extra.attempt = 0;
       }
 
+      extra.width = Math.max(extra.width, extra.currentWidth);
       break;
 
     case "rl-row":
     case "row":
       {
         extra.children.push(html);
-        const [,, w, h] = bbox;
         extra.width += w;
         extra.height = Math.max(extra.height, h);
         const height = (0, _html_utils.measureToString)(extra.height);
 
         for (const child of extra.children) {
-          if (child.attributes.class === "xfaWrapper") {
-            child.children[child.children.length - 1].attributes.style.height = height;
-          } else {
-            child.attributes.style.height = height;
-          }
+          child.attributes.style.height = height;
         }
 
         break;
@@ -123,7 +129,6 @@ function addHTML(node, html, bbox) {
 
     case "table":
       {
-        const [,, w, h] = bbox;
         extra.width = Math.min(availableSpace.width, Math.max(extra.width, w));
         extra.height += h;
         extra.children.push(html);
@@ -132,7 +137,6 @@ function addHTML(node, html, bbox) {
 
     case "tb":
       {
-        const [,,, h] = bbox;
         extra.width = availableSpace.width;
         extra.height += h;
         extra.children.push(html);
@@ -143,48 +147,222 @@ function addHTML(node, html, bbox) {
 
 function getAvailableSpace(node) {
   const availableSpace = node[_xfa_object.$extra].availableSpace;
+  const marginV = node.margin ? node.margin.topInset + node.margin.bottomInset : 0;
+  const marginH = node.margin ? node.margin.leftInset + node.margin.rightInset : 0;
 
   switch (node.layout) {
     case "lr-tb":
     case "rl-tb":
-      switch (node[_xfa_object.$extra].attempt) {
-        case 0:
-          return {
-            width: availableSpace.width - node[_xfa_object.$extra].currentWidth,
-            height: availableSpace.height - node[_xfa_object.$extra].prevHeight
-          };
-
-        case 1:
-          return {
-            width: availableSpace.width,
-            height: availableSpace.height - node[_xfa_object.$extra].height
-          };
-
-        default:
-          return {
-            width: Infinity,
-            height: availableSpace.height - node[_xfa_object.$extra].prevHeight
-          };
+      if (node[_xfa_object.$extra].attempt === 0) {
+        return {
+          width: availableSpace.width - marginH - node[_xfa_object.$extra].currentWidth,
+          height: availableSpace.height - marginV - node[_xfa_object.$extra].prevHeight
+        };
       }
 
+      return {
+        width: availableSpace.width - marginH,
+        height: availableSpace.height - marginV - node[_xfa_object.$extra].height
+      };
+
     case "rl-row":
     case "row":
       const width = node[_xfa_object.$extra].columnWidths.slice(node[_xfa_object.$extra].currentColumn).reduce((a, x) => a + x);
 
       return {
         width,
-        height: availableSpace.height
+        height: availableSpace.height - marginH
       };
 
     case "table":
     case "tb":
       return {
-        width: availableSpace.width,
-        height: availableSpace.height - node[_xfa_object.$extra].height
+        width: availableSpace.width - marginH,
+        height: availableSpace.height - marginV - node[_xfa_object.$extra].height
       };
 
     case "position":
     default:
       return availableSpace;
   }
+}
+
+function getTransformedBBox(node) {
+  let w = node.w === "" ? NaN : node.w;
+  let h = node.h === "" ? NaN : node.h;
+  let [centerX, centerY] = [0, 0];
+
+  switch (node.anchorType || "") {
+    case "bottomCenter":
+      [centerX, centerY] = [w / 2, h];
+      break;
+
+    case "bottomLeft":
+      [centerX, centerY] = [0, h];
+      break;
+
+    case "bottomRight":
+      [centerX, centerY] = [w, h];
+      break;
+
+    case "middleCenter":
+      [centerX, centerY] = [w / 2, h / 2];
+      break;
+
+    case "middleLeft":
+      [centerX, centerY] = [0, h / 2];
+      break;
+
+    case "middleRight":
+      [centerX, centerY] = [w, h / 2];
+      break;
+
+    case "topCenter":
+      [centerX, centerY] = [w / 2, 0];
+      break;
+
+    case "topRight":
+      [centerX, centerY] = [w, 0];
+      break;
+  }
+
+  let x, y;
+
+  switch (node.rotate || 0) {
+    case 0:
+      [x, y] = [-centerX, -centerY];
+      break;
+
+    case 90:
+      [x, y] = [-centerY, centerX];
+      [w, h] = [h, -w];
+      break;
+
+    case 180:
+      [x, y] = [centerX, centerY];
+      [w, h] = [-w, -h];
+      break;
+
+    case 270:
+      [x, y] = [centerY, -centerX];
+      [w, h] = [-h, w];
+      break;
+  }
+
+  return [node.x + x + Math.min(0, w), node.y + y + Math.min(0, h), Math.abs(w), Math.abs(h)];
+}
+
+function checkDimensions(node, space) {
+  if (node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].firstUnsplittable === null) {
+    return true;
+  }
+
+  if (node.w === 0 || node.h === 0) {
+    return true;
+  }
+
+  const ERROR = 2;
+
+  const parent = node[_xfa_object.$getSubformParent]();
+
+  const attempt = parent[_xfa_object.$extra] && parent[_xfa_object.$extra].attempt || 0;
+  const [, y, w, h] = getTransformedBBox(node);
+
+  switch (parent.layout) {
+    case "lr-tb":
+    case "rl-tb":
+      if (attempt === 0) {
+        if (!node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].noLayoutFailure) {
+          if (node.h !== "" && Math.round(h - space.height) > ERROR) {
+            return false;
+          }
+
+          if (node.w !== "") {
+            if (Math.round(w - space.width) <= ERROR) {
+              return true;
+            }
+
+            if (parent[_xfa_object.$extra].numberInLine === 0) {
+              return space.height > 0;
+            }
+
+            return false;
+          }
+
+          return space.width > 0;
+        }
+
+        if (node.w !== "") {
+          return Math.round(w - space.width) <= ERROR;
+        }
+
+        return space.width > 0;
+      }
+
+      if (node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].noLayoutFailure) {
+        return true;
+      }
+
+      if (node.h !== "" && Math.round(h - space.height) > ERROR) {
+        return false;
+      }
+
+      if (node.w === "" || Math.round(w - space.width) <= ERROR) {
+        return space.height > 0;
+      }
+
+      if (parent[_xfa_object.$isThereMoreWidth]()) {
+        return false;
+      }
+
+      return space.height > 0;
+
+    case "table":
+    case "tb":
+      if (node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].noLayoutFailure) {
+        return true;
+      }
+
+      if (node.h !== "" && !node[_xfa_object.$isSplittable]()) {
+        return Math.round(h - space.height) <= ERROR;
+      }
+
+      if (node.w === "" || Math.round(w - space.width) <= ERROR) {
+        return space.height > 0;
+      }
+
+      if (parent[_xfa_object.$isThereMoreWidth]()) {
+        return false;
+      }
+
+      return space.height > 0;
+
+    case "position":
+      if (node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].noLayoutFailure) {
+        return true;
+      }
+
+      if (node.h === "" || Math.round(h + y - space.height) <= ERROR) {
+        return true;
+      }
+
+      const area = node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].currentContentArea;
+
+      return h + y > area.h;
+
+    case "rl-row":
+    case "row":
+      if (node[_xfa_object.$getTemplateRoot]()[_xfa_object.$extra].noLayoutFailure) {
+        return true;
+      }
+
+      if (node.h !== "") {
+        return Math.round(h - space.height) <= ERROR;
+      }
+
+      return true;
+
+    default:
+      return true;
+  }
 }

+ 17 - 3
lib/core/xfa/parser.js

@@ -39,6 +39,9 @@ class XFAParser extends _xml_parser.XMLParserBase {
     super();
     this._builder = new _builder.Builder();
     this._stack = [];
+    this._globalData = {
+      usedTypefaces: new Set()
+    };
     this._ids = new Map();
     this._current = this._builder.buildRoot(this._ids);
     this._errorCode = _xml_parser.XMLParserErrorCode.NoError;
@@ -131,20 +134,20 @@ class XFAParser extends _xml_parser.XMLParserBase {
     return [namespace, prefixes, attributeObj];
   }
 
-  _getNameAndPrefix(name) {
+  _getNameAndPrefix(name, nsAgnostic) {
     const i = name.indexOf(":");
 
     if (i === -1) {
       return [name, null];
     }
 
-    return [name.substring(i + 1), name.substring(0, i)];
+    return [name.substring(i + 1), nsAgnostic ? "" : name.substring(0, i)];
   }
 
   onBeginElement(tagName, attributes, isEmpty) {
     const [namespace, prefixes, attributesObj] = this._mkAttributes(attributes, tagName);
 
-    const [name, nsPrefix] = this._getNameAndPrefix(tagName);
+    const [name, nsPrefix] = this._getNameAndPrefix(tagName, this._builder.isNsAgnostic());
 
     const node = this._builder.build({
       nsPrefix,
@@ -154,6 +157,8 @@ class XFAParser extends _xml_parser.XMLParserBase {
       prefixes
     });
 
+    node[_xfa_object.$globalData] = this._globalData;
+
     if (isEmpty) {
       node[_xfa_object.$finalize]();
 
@@ -174,6 +179,15 @@ class XFAParser extends _xml_parser.XMLParserBase {
   onEndElement(name) {
     const node = this._current;
 
+    if (node[_xfa_object.$isCDATAXml]() && typeof node[_xfa_object.$content] === "string") {
+      const parser = new XFAParser();
+      parser._globalData = this._globalData;
+      const root = parser.parse(node[_xfa_object.$content]);
+      node[_xfa_object.$content] = null;
+
+      node[_xfa_object.$onChild](root);
+    }
+
     node[_xfa_object.$finalize]();
 
     this._current = this._stack.pop();

+ 13 - 3
lib/core/xfa/som.js

@@ -53,7 +53,7 @@ function parseIndex(index) {
   return parseInt(index, 10) || 0;
 }
 
-function parseExpression(expr, dotDotAllowed) {
+function parseExpression(expr, dotDotAllowed, noExpr = true) {
   let match = expr.match(namePattern);
 
   if (!match) {
@@ -106,10 +106,20 @@ function parseExpression(expr, dotDotAllowed) {
         break;
 
       case "[":
+        if (noExpr) {
+          (0, _util.warn)("XFA - SOM expression contains a FormCalc subexpression which is not supported for now.");
+          return null;
+        }
+
         operator = operators.dotBracket;
         break;
 
       case "(":
+        if (noExpr) {
+          (0, _util.warn)("XFA - SOM expression contains a JavaScript subexpression which is not supported for now.");
+          return null;
+        }
+
         operator = operators.dotParen;
         break;
 
@@ -256,7 +266,7 @@ function createNodes(root, path) {
     name,
     index
   } of path) {
-    for (let i = 0; i <= index; i++) {
+    for (let i = 0, ii = !isFinite(index) ? 0 : index; i <= ii; i++) {
       node = new _xfa_object.XmlObject(root[_xfa_object.$namespaceId], name);
 
       root[_xfa_object.$appendChild](node);
@@ -341,7 +351,7 @@ function createDataNode(root, container, expr) {
 
       root = child;
     } else {
-      parsed[i].index = children.length - index;
+      parsed[i].index = index - children.length;
       return createNodes(root, parsed.slice(i));
     }
   }

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


+ 272 - 0
lib/core/xfa/text.js

@@ -0,0 +1,272 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.TextMeasure = void 0;
+
+var _fonts = require("./fonts.js");
+
+const WIDTH_FACTOR = 1.01;
+
+class FontInfo {
+  constructor(xfaFont, margin, lineHeight, fontFinder) {
+    this.lineHeight = lineHeight;
+    this.paraMargin = margin || {
+      top: 0,
+      bottom: 0,
+      left: 0,
+      right: 0
+    };
+
+    if (!xfaFont) {
+      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
+      return;
+    }
+
+    this.xfaFont = {
+      typeface: xfaFont.typeface,
+      posture: xfaFont.posture,
+      weight: xfaFont.weight,
+      size: xfaFont.size,
+      letterSpacing: xfaFont.letterSpacing
+    };
+    const typeface = fontFinder.find(xfaFont.typeface);
+
+    if (!typeface) {
+      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
+      return;
+    }
+
+    this.pdfFont = (0, _fonts.selectFont)(xfaFont, typeface);
+
+    if (!this.pdfFont) {
+      [this.pdfFont, this.xfaFont] = this.defaultFont(fontFinder);
+    }
+  }
+
+  defaultFont(fontFinder) {
+    const font = fontFinder.find("Helvetica", false) || fontFinder.find("Myriad Pro", false) || fontFinder.find("Arial", false) || fontFinder.getDefault();
+
+    if (font && font.regular) {
+      const pdfFont = font.regular;
+      const info = pdfFont.cssFontInfo;
+      const xfaFont = {
+        typeface: info.fontFamily,
+        posture: "normal",
+        weight: "normal",
+        size: 10,
+        letterSpacing: 0
+      };
+      return [pdfFont, xfaFont];
+    }
+
+    const xfaFont = {
+      typeface: "Courier",
+      posture: "normal",
+      weight: "normal",
+      size: 10,
+      letterSpacing: 0
+    };
+    return [null, xfaFont];
+  }
+
+}
+
+class FontSelector {
+  constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder) {
+    this.fontFinder = fontFinder;
+    this.stack = [new FontInfo(defaultXfaFont, defaultParaMargin, defaultLineHeight, fontFinder)];
+  }
+
+  pushData(xfaFont, margin, lineHeight) {
+    const lastFont = this.stack[this.stack.length - 1];
+
+    for (const name of ["typeface", "posture", "weight", "size", "letterSpacing"]) {
+      if (!xfaFont[name]) {
+        xfaFont[name] = lastFont.xfaFont[name];
+      }
+    }
+
+    for (const name of ["top", "bottom", "left", "right"]) {
+      if (isNaN(margin[name])) {
+        margin[name] = lastFont.paraMargin[name];
+      }
+    }
+
+    const fontInfo = new FontInfo(xfaFont, margin, lineHeight || lastFont.lineHeight, this.fontFinder);
+
+    if (!fontInfo.pdfFont) {
+      fontInfo.pdfFont = lastFont.pdfFont;
+    }
+
+    this.stack.push(fontInfo);
+  }
+
+  popFont() {
+    this.stack.pop();
+  }
+
+  topFont() {
+    return this.stack[this.stack.length - 1];
+  }
+
+}
+
+class TextMeasure {
+  constructor(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts) {
+    this.glyphs = [];
+    this.fontSelector = new FontSelector(defaultXfaFont, defaultParaMargin, defaultLineHeight, fonts);
+    this.extraHeight = 0;
+  }
+
+  pushData(xfaFont, margin, lineHeight) {
+    this.fontSelector.pushData(xfaFont, margin, lineHeight);
+  }
+
+  popFont(xfaFont) {
+    return this.fontSelector.popFont();
+  }
+
+  addPara() {
+    const lastFont = this.fontSelector.topFont();
+    this.extraHeight += lastFont.paraMargin.top + lastFont.paraMargin.bottom;
+  }
+
+  addString(str) {
+    if (!str) {
+      return;
+    }
+
+    const lastFont = this.fontSelector.topFont();
+    const fontSize = lastFont.xfaFont.size;
+
+    if (lastFont.pdfFont) {
+      const letterSpacing = lastFont.xfaFont.letterSpacing;
+      const pdfFont = lastFont.pdfFont;
+      const lineHeight = lastFont.lineHeight || Math.ceil(Math.max(1.2, pdfFont.lineHeight) * fontSize);
+      const scale = fontSize / 1000;
+
+      for (const line of str.split(/[\u2029\n]/)) {
+        const encodedLine = pdfFont.encodeString(line).join("");
+        const glyphs = pdfFont.charsToGlyphs(encodedLine);
+
+        for (const glyph of glyphs) {
+          this.glyphs.push([glyph.width * scale + letterSpacing, lineHeight, glyph.unicode === " ", false]);
+        }
+
+        this.glyphs.push([0, 0, false, true]);
+      }
+
+      this.glyphs.pop();
+      return;
+    }
+
+    for (const line of str.split(/[\u2029\n]/)) {
+      for (const char of line.split("")) {
+        this.glyphs.push([fontSize, fontSize, char === " ", false]);
+      }
+
+      this.glyphs.push([0, 0, false, true]);
+    }
+
+    this.glyphs.pop();
+  }
+
+  compute(maxWidth) {
+    let lastSpacePos = -1,
+        lastSpaceWidth = 0,
+        width = 0,
+        height = 0,
+        currentLineWidth = 0,
+        currentLineHeight = 0;
+    let isBroken = false;
+
+    for (let i = 0, ii = this.glyphs.length; i < ii; i++) {
+      const [glyphWidth, glyphHeight, isSpace, isEOL] = this.glyphs[i];
+
+      if (isEOL) {
+        width = Math.max(width, currentLineWidth);
+        currentLineWidth = 0;
+        height += currentLineHeight;
+        currentLineHeight = glyphHeight;
+        lastSpacePos = -1;
+        lastSpaceWidth = 0;
+        continue;
+      }
+
+      if (isSpace) {
+        if (currentLineWidth + glyphWidth > maxWidth) {
+          width = Math.max(width, currentLineWidth);
+          currentLineWidth = 0;
+          height += currentLineHeight;
+          currentLineHeight = glyphHeight;
+          lastSpacePos = -1;
+          lastSpaceWidth = 0;
+          isBroken = true;
+        } else {
+          currentLineHeight = Math.max(glyphHeight, currentLineHeight);
+          lastSpaceWidth = currentLineWidth;
+          currentLineWidth += glyphWidth;
+          lastSpacePos = i;
+        }
+
+        continue;
+      }
+
+      if (currentLineWidth + glyphWidth > maxWidth) {
+        height += currentLineHeight;
+        currentLineHeight = glyphHeight;
+
+        if (lastSpacePos !== -1) {
+          i = lastSpacePos;
+          width = Math.max(width, lastSpaceWidth);
+          currentLineWidth = 0;
+          lastSpacePos = -1;
+          lastSpaceWidth = 0;
+        } else {
+          width = Math.max(width, currentLineWidth);
+          currentLineWidth = glyphWidth;
+        }
+
+        isBroken = true;
+        continue;
+      }
+
+      currentLineWidth += glyphWidth;
+      currentLineHeight = Math.max(glyphHeight, currentLineHeight);
+    }
+
+    width = Math.max(width, currentLineWidth);
+    height += currentLineHeight + this.extraHeight;
+    return {
+      width: WIDTH_FACTOR * width,
+      height,
+      isBroken
+    };
+  }
+
+}
+
+exports.TextMeasure = TextMeasure;

+ 32 - 5
lib/core/xfa/utils.js

@@ -33,7 +33,11 @@ exports.getMeasurement = getMeasurement;
 exports.getRatio = getRatio;
 exports.getRelevant = getRelevant;
 exports.getStringOption = getStringOption;
+exports.stripQuotes = stripQuotes;
 exports.HTMLResult = void 0;
+
+var _util = require("../../shared/util.js");
+
 const dimConverters = {
   pt: x => x,
   cm: x => x / 2.54 * 72,
@@ -43,6 +47,14 @@ const dimConverters = {
 };
 const measurementPattern = /([+-]?[0-9]+\.?[0-9]*)(.*)/;
 
+function stripQuotes(str) {
+  if (str.startsWith("'") || str.startsWith('"')) {
+    return str.slice(1, str.length - 1);
+  }
+
+  return str;
+}
+
 function getInteger({
   data,
   defaultValue,
@@ -243,18 +255,33 @@ function getBBox(data) {
 }
 
 class HTMLResult {
-  constructor(success, html, bbox) {
+  static get FAILURE() {
+    return (0, _util.shadow)(this, "FAILURE", new HTMLResult(false, null, null, null));
+  }
+
+  static get EMPTY() {
+    return (0, _util.shadow)(this, "EMPTY", new HTMLResult(true, null, null, null));
+  }
+
+  constructor(success, html, bbox, breakNode) {
     this.success = success;
     this.html = html;
     this.bbox = bbox;
+    this.breakNode = breakNode;
+  }
+
+  isBreak() {
+    return !!this.breakNode;
+  }
+
+  static breakNode(node) {
+    return new HTMLResult(false, null, null, node);
   }
 
   static success(html, bbox = null) {
-    return new HTMLResult(true, html, bbox);
+    return new HTMLResult(true, html, bbox, null);
   }
 
 }
 
-exports.HTMLResult = HTMLResult;
-HTMLResult.FAILURE = new HTMLResult(false, null, null);
-HTMLResult.EMPTY = new HTMLResult(true, null, null);
+exports.HTMLResult = HTMLResult;

+ 222 - 54
lib/core/xfa/xfa_object.js

@@ -24,26 +24,30 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.XmlObject = exports.XFAObjectArray = exports.XFAObject = exports.XFAAttribute = exports.StringObject = exports.OptionObject = exports.Option10 = exports.Option01 = exports.IntegerObject = exports.ContentObject = exports.$uid = exports.$toStyle = exports.$toHTML = exports.$text = exports.$setValue = exports.$setSetAttributes = exports.$setId = exports.$searchNode = exports.$resolvePrototypes = exports.$removeChild = exports.$onText = exports.$onChildCheck = exports.$onChild = exports.$nsAttributes = exports.$nodeName = exports.$namespaceId = exports.$isTransparent = exports.$isDescendent = exports.$isDataValue = exports.$insertAt = exports.$indexOf = exports.$ids = exports.$hasSettableValue = exports.$hasItem = exports.$global = exports.$getRealChildrenByNameIt = exports.$getParent = exports.$getNextPage = exports.$getDataValue = exports.$getChildrenByNameIt = exports.$getChildrenByName = exports.$getChildrenByClass = exports.$getChildren = exports.$getAvailableSpace = exports.$getAttributeIt = exports.$flushHTML = exports.$finalize = exports.$extra = exports.$dump = exports.$data = exports.$content = exports.$consumed = exports.$clone = exports.$cleanup = exports.$clean = exports.$childrenToHTML = exports.$break = exports.$appendChild = exports.$addHTML = exports.$acceptWhitespace = void 0;
+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.$pushGlyphs = 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 _util = require("../../shared/util.js");
 
+var _core_utils = require("../core_utils.js");
+
 var _namespaces = require("./namespaces.js");
 
+var _som = require("./som.js");
+
 const $acceptWhitespace = Symbol();
 exports.$acceptWhitespace = $acceptWhitespace;
 const $addHTML = Symbol();
 exports.$addHTML = $addHTML;
 const $appendChild = Symbol();
 exports.$appendChild = $appendChild;
-const $break = Symbol();
-exports.$break = $break;
 const $childrenToHTML = Symbol();
 exports.$childrenToHTML = $childrenToHTML;
 const $clean = Symbol();
 exports.$clean = $clean;
+const $cleanPage = Symbol();
+exports.$cleanPage = $cleanPage;
 const $cleanup = Symbol();
 exports.$cleanup = $cleanup;
 const $clone = Symbol();
@@ -64,6 +68,8 @@ const $flushHTML = Symbol();
 exports.$flushHTML = $flushHTML;
 const $getAttributeIt = Symbol();
 exports.$getAttributeIt = $getAttributeIt;
+const $getAttributes = Symbol();
+exports.$getAttributes = $getAttributes;
 const $getAvailableSpace = Symbol();
 exports.$getAvailableSpace = $getAvailableSpace;
 const $getChildrenByClass = Symbol();
@@ -74,18 +80,24 @@ const $getChildrenByNameIt = Symbol();
 exports.$getChildrenByNameIt = $getChildrenByNameIt;
 const $getDataValue = Symbol();
 exports.$getDataValue = $getDataValue;
+const $getExtra = Symbol();
+exports.$getExtra = $getExtra;
 const $getRealChildrenByNameIt = Symbol();
 exports.$getRealChildrenByNameIt = $getRealChildrenByNameIt;
 const $getChildren = Symbol();
 exports.$getChildren = $getChildren;
+const $getContainedChildren = Symbol();
+exports.$getContainedChildren = $getContainedChildren;
 const $getNextPage = Symbol();
 exports.$getNextPage = $getNextPage;
+const $getSubformParent = Symbol();
+exports.$getSubformParent = $getSubformParent;
 const $getParent = Symbol();
 exports.$getParent = $getParent;
-const $global = Symbol();
-exports.$global = $global;
-const $hasItem = Symbol();
-exports.$hasItem = $hasItem;
+const $getTemplateRoot = Symbol();
+exports.$getTemplateRoot = $getTemplateRoot;
+const $globalData = Symbol();
+exports.$globalData = $globalData;
 const $hasSettableValue = Symbol();
 exports.$hasSettableValue = $hasSettableValue;
 const $ids = Symbol();
@@ -94,12 +106,24 @@ const $indexOf = Symbol();
 exports.$indexOf = $indexOf;
 const $insertAt = Symbol();
 exports.$insertAt = $insertAt;
+const $isCDATAXml = Symbol();
+exports.$isCDATAXml = $isCDATAXml;
+const $isBindable = Symbol();
+exports.$isBindable = $isBindable;
 const $isDataValue = Symbol();
 exports.$isDataValue = $isDataValue;
 const $isDescendent = Symbol();
 exports.$isDescendent = $isDescendent;
+const $isNsAgnostic = Symbol();
+exports.$isNsAgnostic = $isNsAgnostic;
+const $isSplittable = Symbol();
+exports.$isSplittable = $isSplittable;
+const $isThereMoreWidth = Symbol();
+exports.$isThereMoreWidth = $isThereMoreWidth;
 const $isTransparent = Symbol();
 exports.$isTransparent = $isTransparent;
+const $isUsable = Symbol();
+exports.$isUsable = $isUsable;
 const $lastAttribute = Symbol();
 const $namespaceId = Symbol("namespaceId");
 exports.$namespaceId = $namespaceId;
@@ -113,8 +137,12 @@ const $onChildCheck = Symbol();
 exports.$onChildCheck = $onChildCheck;
 const $onText = Symbol();
 exports.$onText = $onText;
+const $pushGlyphs = Symbol();
+exports.$pushGlyphs = $pushGlyphs;
 const $removeChild = Symbol();
 exports.$removeChild = $removeChild;
+const $root = Symbol("root");
+exports.$root = $root;
 const $resolvePrototypes = Symbol();
 exports.$resolvePrototypes = $resolvePrototypes;
 const $searchNode = Symbol();
@@ -125,10 +153,14 @@ const $setSetAttributes = Symbol();
 exports.$setSetAttributes = $setSetAttributes;
 const $setValue = Symbol();
 exports.$setValue = $setValue;
+const $tabIndex = Symbol();
+exports.$tabIndex = $tabIndex;
 const $text = Symbol();
 exports.$text = $text;
 const $toHTML = Symbol();
 exports.$toHTML = $toHTML;
+const $toString = Symbol();
+exports.$toString = $toString;
 const $toStyle = Symbol();
 exports.$toStyle = $toStyle;
 const $uid = Symbol("uid");
@@ -162,11 +194,14 @@ const _options = Symbol();
 
 const _parent = Symbol("parent");
 
+const _resolvePrototypesHelper = Symbol();
+
 const _setAttributes = Symbol();
 
 const _validator = Symbol();
 
 let uid = 0;
+const NS_DATASETS = _namespaces.NamespaceIds.datasets.id;
 
 class XFAObject {
   constructor(nsId, name, hasChildren = false) {
@@ -176,6 +211,7 @@ class XFAObject {
     this[_parent] = null;
     this[_children] = [];
     this[$uid] = `${name}${uid++}`;
+    this[$globalData] = null;
   }
 
   [$onChild](child) {
@@ -217,16 +253,40 @@ class XFAObject {
     return this.hasOwnProperty(child[$nodeName]) && child[$namespaceId] === this[$namespaceId];
   }
 
+  [$isNsAgnostic]() {
+    return false;
+  }
+
   [$acceptWhitespace]() {
     return false;
   }
 
+  [$isCDATAXml]() {
+    return false;
+  }
+
+  [$isBindable]() {
+    return false;
+  }
+
   [$setId](ids) {
     if (this.id && this[$namespaceId] === _namespaces.NamespaceIds.template.id) {
       ids.set(this.id, this);
     }
   }
 
+  [$getTemplateRoot]() {
+    return this[$globalData].template;
+  }
+
+  [$isSplittable]() {
+    return false;
+  }
+
+  [$isThereMoreWidth]() {
+    return false;
+  }
+
   [$appendChild](child) {
     child[_parent] = this;
 
@@ -258,10 +318,6 @@ class XFAObject {
     }
   }
 
-  [$hasItem]() {
-    return false;
-  }
-
   [$indexOf](child) {
     return this[_children].indexOf(child);
   }
@@ -273,7 +329,7 @@ class XFAObject {
   }
 
   [$isTransparent]() {
-    return this.name === "";
+    return !this.name;
   }
 
   [$lastAttribute]() {
@@ -324,6 +380,10 @@ class XFAObject {
     return this[_parent];
   }
 
+  [$getSubformParent]() {
+    return this[$getParent]();
+  }
+
   [$getChildren](name = null) {
     if (!name) {
       return this[_children];
@@ -368,8 +428,14 @@ class XFAObject {
     return _utils.HTMLResult.EMPTY;
   }
 
-  *[_filteredChildrenGenerator](filter, include) {
+  *[$getContainedChildren]() {
     for (const node of this[$getChildren]()) {
+      yield node;
+    }
+  }
+
+  *[_filteredChildrenGenerator](filter, include) {
+    for (const node of this[$getContainedChildren]()) {
       if (!filter || include === filter.has(node[$nodeName])) {
         const availableSpace = this[$getAvailableSpace]();
         const res = node[$toHTML](availableSpace);
@@ -404,7 +470,7 @@ class XFAObject {
       const res = this[$extra].failingNode[$toHTML](availableSpace);
 
       if (!res.success) {
-        return false;
+        return res;
       }
 
       if (res.html) {
@@ -424,7 +490,7 @@ class XFAObject {
       const res = gen.value;
 
       if (!res.success) {
-        return false;
+        return res;
       }
 
       if (res.html) {
@@ -433,13 +499,11 @@ class XFAObject {
     }
 
     this[$extra].generator = null;
-    return true;
+    return _utils.HTMLResult.EMPTY;
   }
 
   [$setSetAttributes](attributes) {
-    if (attributes.use || attributes.id) {
-      this[_setAttributes] = new Set(Object.keys(attributes));
-    }
+    this[_setAttributes] = new Set(Object.keys(attributes));
   }
 
   [_getUnsetAttributes](protoAttributes) {
@@ -450,57 +514,91 @@ class XFAObject {
 
   [$resolvePrototypes](ids, ancestors = new Set()) {
     for (const child of this[_children]) {
-      const proto = child[_getPrototype](ids, ancestors);
+      child[_resolvePrototypesHelper](ids, ancestors);
+    }
+  }
 
-      if (proto) {
-        child[_applyPrototype](proto, ids, ancestors);
-      } else {
-        child[$resolvePrototypes](ids, ancestors);
-      }
+  [_resolvePrototypesHelper](ids, ancestors) {
+    const proto = this[_getPrototype](ids, ancestors);
+
+    if (proto) {
+      this[_applyPrototype](proto, ids, ancestors);
+    } else {
+      this[$resolvePrototypes](ids, ancestors);
     }
   }
 
   [_getPrototype](ids, ancestors) {
     const {
-      use
+      use,
+      usehref
     } = this;
 
-    if (use && use.startsWith("#")) {
-      const id = use.slice(1);
-      const proto = ids.get(id);
-      this.use = "";
+    if (!use && !usehref) {
+      return null;
+    }
 
-      if (!proto) {
-        (0, _util.warn)(`XFA - Invalid prototype id: ${id}.`);
-        return null;
+    let proto = null;
+    let somExpression = null;
+    let id = null;
+    let ref = use;
+
+    if (usehref) {
+      ref = usehref;
+
+      if (usehref.startsWith("#som(") && usehref.endsWith(")")) {
+        somExpression = usehref.slice("#som(".length, usehref.length - 1);
+      } else if (usehref.startsWith(".#som(") && usehref.endsWith(")")) {
+        somExpression = usehref.slice(".#som(".length, usehref.length - 1);
+      } else if (usehref.startsWith("#")) {
+        id = usehref.slice(1);
+      } else if (usehref.startsWith(".#")) {
+        id = usehref.slice(2);
       }
+    } else if (use.startsWith("#")) {
+      id = use.slice(1);
+    } else {
+      somExpression = use;
+    }
 
-      if (proto[$nodeName] !== this[$nodeName]) {
-        (0, _util.warn)(`XFA - Incompatible prototype: ${proto[$nodeName]} !== ${this[$nodeName]}.`);
-        return null;
-      }
+    this.use = this.usehref = "";
+
+    if (id) {
+      proto = ids.get(id);
+    } else {
+      proto = (0, _som.searchNode)(ids.get($root), this, somExpression, true, false);
 
-      if (ancestors.has(proto)) {
-        (0, _util.warn)(`XFA - Cycle detected in prototypes use.`);
-        return null;
+      if (proto) {
+        proto = proto[0];
       }
+    }
 
-      ancestors.add(proto);
+    if (!proto) {
+      (0, _util.warn)(`XFA - Invalid prototype reference: ${ref}.`);
+      return null;
+    }
 
-      const protoProto = proto[_getPrototype](ids, ancestors);
+    if (proto[$nodeName] !== this[$nodeName]) {
+      (0, _util.warn)(`XFA - Incompatible prototype: ${proto[$nodeName]} !== ${this[$nodeName]}.`);
+      return null;
+    }
 
-      if (!protoProto) {
-        ancestors.delete(proto);
-        return proto;
-      }
+    if (ancestors.has(proto)) {
+      (0, _util.warn)(`XFA - Cycle detected in prototypes use.`);
+      return null;
+    }
 
-      proto[_applyPrototype](protoProto, ids, ancestors);
+    ancestors.add(proto);
+
+    const protoProto = proto[_getPrototype](ids, ancestors);
 
-      ancestors.delete(proto);
-      return proto;
+    if (protoProto) {
+      proto[_applyPrototype](protoProto, ids, ancestors);
     }
 
-    return null;
+    proto[$resolvePrototypes](ids, ancestors);
+    ancestors.delete(proto);
+    return proto;
   }
 
   [_applyPrototype](proto, ids, ancestors) {
@@ -534,7 +632,7 @@ class XFAObject {
 
       if (value instanceof XFAObjectArray) {
         for (const child of value[_children]) {
-          child[$resolvePrototypes](ids, ancestors);
+          child[_resolvePrototypesHelper](ids, ancestors);
         }
 
         for (let i = value[_children].length, ii = protoValue[_children].length; i < ii; i++) {
@@ -545,7 +643,7 @@ class XFAObject {
 
             this[_children].push(child);
 
-            child[$resolvePrototypes](ids, newAncestors);
+            child[_resolvePrototypesHelper](ids, ancestors);
           } else {
             break;
           }
@@ -556,6 +654,11 @@ class XFAObject {
 
       if (value !== null) {
         value[$resolvePrototypes](ids, ancestors);
+
+        if (protoValue) {
+          value[_applyPrototype](protoValue, ids, ancestors);
+        }
+
         continue;
       }
 
@@ -566,7 +669,7 @@ class XFAObject {
 
         this[_children].push(child);
 
-        child[$resolvePrototypes](ids, newAncestors);
+        child[_resolvePrototypesHelper](ids, ancestors);
       }
     }
   }
@@ -576,7 +679,7 @@ class XFAObject {
       return obj.map(x => XFAObject[_cloneAttribute](x));
     }
 
-    if (obj instanceof Object) {
+    if (typeof obj === "object" && obj !== null) {
       return Object.assign({}, obj);
     }
 
@@ -594,6 +697,7 @@ class XFAObject {
       }
     }
 
+    clone[$uid] = `${clone[$nodeName]}${uid++}`;
     clone[_children] = [];
 
     for (const name of Object.getOwnPropertyNames(this)) {
@@ -725,6 +829,7 @@ class XFAAttribute {
     this[$nodeName] = name;
     this[$content] = value;
     this[$consumed] = false;
+    this[$uid] = `attribute${uid++}`;
   }
 
   [$getParent]() {
@@ -735,6 +840,15 @@ class XFAAttribute {
     return true;
   }
 
+  [$getDataValue]() {
+    return this[$content].trim();
+  }
+
+  [$setValue](value) {
+    value = value.value || "";
+    this[$content] = value.toString();
+  }
+
   [$text]() {
     return this[$content];
   }
@@ -777,6 +891,51 @@ class XmlObject extends XFAObject {
     this[$consumed] = false;
   }
 
+  [$toString](buf) {
+    const tagName = this[$nodeName];
+
+    if (tagName === "#text") {
+      buf.push((0, _core_utils.encodeToXmlString)(this[$content]));
+      return;
+    }
+
+    const prefix = this[$namespaceId] === NS_DATASETS ? "xfa:" : "";
+    buf.push(`<${prefix}${tagName}`);
+
+    for (const [name, value] of this[_attributes].entries()) {
+      buf.push(` ${name}="${(0, _core_utils.encodeToXmlString)(value[$content])}"`);
+    }
+
+    if (this[_dataValue] !== null) {
+      if (this[_dataValue]) {
+        buf.push(` xfa:dataNode="dataValue"`);
+      } else {
+        buf.push(` xfa:dataNode="dataGroup"`);
+      }
+    }
+
+    if (!this[$content] && this[_children].length === 0) {
+      buf.push("/>");
+      return;
+    }
+
+    buf.push(">");
+
+    if (this[$content]) {
+      if (typeof this[$content] === "string") {
+        buf.push((0, _core_utils.encodeToXmlString)(this[$content]));
+      } else {
+        this[$content][$toString](buf);
+      }
+    } else {
+      for (const child of this[_children]) {
+        child[$toString](buf);
+      }
+    }
+
+    buf.push(`</${prefix}${tagName}>`);
+  }
+
   [$onChild](child) {
     if (this[$content]) {
       const node = new XmlObject(this[$namespaceId], "#text");
@@ -821,6 +980,10 @@ class XmlObject extends XFAObject {
     return this[_children].filter(c => c[$nodeName] === name);
   }
 
+  [$getAttributes]() {
+    return this[_attributes];
+  }
+
   [$getChildrenByClass](name) {
     const value = this[_attributes].get(name);
 
@@ -897,6 +1060,11 @@ class XmlObject extends XFAObject {
     return this[$content].trim();
   }
 
+  [$setValue](value) {
+    value = value.value || "";
+    this[$content] = value.toString();
+  }
+
   [$dump]() {
     const dumped = Object.create(null);
 

+ 173 - 12
lib/core/xfa/xhtml.js

@@ -36,17 +36,22 @@ var _utils = require("./utils.js");
 
 const XHTML_NS_ID = _namespaces.NamespaceIds.xhtml.id;
 const VALID_STYLES = new Set(["color", "font", "font-family", "font-size", "font-stretch", "font-style", "font-weight", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "letter-spacing", "line-height", "orphans", "page-break-after", "page-break-before", "page-break-inside", "tab-interval", "tab-stop", "text-align", "text-decoration", "text-indent", "vertical-align", "widows", "kerning-mode", "xfa-font-horizontal-scale", "xfa-font-vertical-scale", "xfa-spacerun", "xfa-tab-stops"]);
-const StyleMapping = new Map([["page-break-after", "breakAfter"], ["page-break-before", "breakBefore"], ["page-break-inside", "breakInside"], ["kerning-mode", value => value === "none" ? "none" : "normal"], ["xfa-font-horizontal-scale", value => `scaleX(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-font-vertical-scale", value => `scaleY(${Math.max(0, Math.min(parseInt(value) / 100)).toFixed(2)})`], ["xfa-spacerun", ""], ["xfa-tab-stops", ""], ["font-size", value => (0, _html_utils.measureToString)(1 * (0, _utils.getMeasurement)(value))], ["letter-spacing", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["line-height", value => (0, _html_utils.measureToString)(0.99 * (0, _utils.getMeasurement)(value))], ["margin", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-bottom", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-left", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-right", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["margin-top", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["text-indent", value => (0, _html_utils.measureToString)((0, _utils.getMeasurement)(value))], ["font-family", value => (0, _html_utils.getFonts)(value)]]);
+const 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);
+  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]]);
 const spacesRegExp = /\s+/g;
 const crlfRegExp = /[\r\n]+/g;
 
-function mapStyle(styleStr) {
+function mapStyle(styleStr, fontFinder) {
   const style = Object.create(null);
 
   if (!styleStr) {
     return style;
   }
 
+  const original = Object.create(null);
+
   for (const [key, value] of styleStr.split(";").map(s => s.split(":", 2))) {
     const mapping = StyleMapping.get(key);
 
@@ -60,7 +65,7 @@ function mapStyle(styleStr) {
       if (typeof mapping === "string") {
         newValue = mapping;
       } else {
-        newValue = mapping(value);
+        newValue = mapping(value, original);
       }
     }
 
@@ -75,16 +80,31 @@ function mapStyle(styleStr) {
     }
   }
 
+  if (style.fontFamily) {
+    (0, _html_utils.setFontFamily)({
+      typeface: style.fontFamily,
+      weight: style.fontWeight || "normal",
+      posture: style.fontStyle || "normal",
+      size: original.fontSize || 0
+    }, fontFinder, style);
+  }
+
   (0, _html_utils.fixTextIndent)(style);
   return style;
 }
 
-function checkStyle(style) {
-  if (!style) {
+function checkStyle(node) {
+  if (!node.style) {
     return "";
   }
 
-  return style.trim().split(/\s*;\s*/).filter(s => !!s).map(s => s.split(/\s*:\s*/, 2)).filter(([key]) => VALID_STYLES.has(key)).map(kv => kv.join(":")).join(";");
+  return node.style.trim().split(/\s*;\s*/).filter(s => !!s).map(s => s.split(/\s*:\s*/, 2)).filter(([key, value]) => {
+    if (key === "font-family") {
+      node[_xfa_object.$globalData].usedTypefaces.add(value);
+    }
+
+    return VALID_STYLES.has(key);
+  }).map(kv => kv.join(":")).join(";");
 }
 
 const NoWhites = new Set(["body", "html"]);
@@ -92,7 +112,13 @@ const NoWhites = new Set(["body", "html"]);
 class XhtmlObject extends _xfa_object.XmlObject {
   constructor(attributes, name) {
     super(XHTML_NS_ID, name);
-    this.style = checkStyle(attributes.style);
+    this.style = attributes.style || "";
+  }
+
+  [_xfa_object.$clean](builder) {
+    super[_xfa_object.$clean](builder);
+
+    this.style = checkStyle(this);
   }
 
   [_xfa_object.$acceptWhitespace]() {
@@ -111,6 +137,109 @@ class XhtmlObject extends _xfa_object.XmlObject {
     }
   }
 
+  [_xfa_object.$pushGlyphs](measure, mustPop = true) {
+    const xfaFont = Object.create(null);
+    const margin = {
+      top: NaN,
+      bottom: NaN,
+      left: NaN,
+      right: NaN
+    };
+    let lineHeight = null;
+
+    for (const [key, value] of this.style.split(";").map(s => s.split(":", 2))) {
+      switch (key) {
+        case "font-family":
+          xfaFont.typeface = (0, _utils.stripQuotes)(value);
+          break;
+
+        case "font-size":
+          xfaFont.size = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "font-weight":
+          xfaFont.weight = value;
+          break;
+
+        case "font-style":
+          xfaFont.posture = value;
+          break;
+
+        case "letter-spacing":
+          xfaFont.letterSpacing = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "margin":
+          const values = value.split(/ \t/).map(x => (0, _utils.getMeasurement)(x));
+
+          switch (values.length) {
+            case 1:
+              margin.top = margin.bottom = margin.left = margin.right = values[0];
+              break;
+
+            case 2:
+              margin.top = margin.bottom = values[0];
+              margin.left = margin.right = values[1];
+              break;
+
+            case 3:
+              margin.top = values[0];
+              margin.bottom = values[2];
+              margin.left = margin.right = values[1];
+              break;
+
+            case 4:
+              margin.top = values[0];
+              margin.left = values[1];
+              margin.bottom = values[2];
+              margin.right = values[3];
+              break;
+          }
+
+          break;
+
+        case "margin-top":
+          margin.top = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "margin-bottom":
+          margin.bottom = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "margin-left":
+          margin.left = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "margin-right":
+          margin.right = (0, _utils.getMeasurement)(value);
+          break;
+
+        case "line-height":
+          lineHeight = (0, _utils.getMeasurement)(value);
+          break;
+      }
+    }
+
+    measure.pushData(xfaFont, margin, lineHeight);
+
+    if (this[_xfa_object.$content]) {
+      measure.addString(this[_xfa_object.$content]);
+    } else {
+      for (const child of this[_xfa_object.$getChildren]()) {
+        if (child[_xfa_object.$nodeName] === "#text") {
+          measure.addString(child[_xfa_object.$content]);
+          continue;
+        }
+
+        child[_xfa_object.$pushGlyphs](measure);
+      }
+    }
+
+    if (mustPop) {
+      measure.popFont();
+    }
+  }
+
   [_xfa_object.$toHTML](availableSpace) {
     const children = [];
     this[_xfa_object.$extra] = {
@@ -127,7 +256,7 @@ class XhtmlObject extends _xfa_object.XmlObject {
       name: this[_xfa_object.$nodeName],
       attributes: {
         href: this.href,
-        style: mapStyle(this.style)
+        style: mapStyle(this.style, this[_xfa_object.$globalData].fontFinder)
       },
       children,
       value: this[_xfa_object.$content] || ""
@@ -149,6 +278,16 @@ class B extends XhtmlObject {
     super(attributes, "b");
   }
 
+  [_xfa_object.$pushGlyphs](measure) {
+    measure.pushFont({
+      weight: "bold"
+    });
+
+    super[_xfa_object.$pushGlyphs](measure);
+
+    measure.popFont();
+  }
+
 }
 
 class Body extends XhtmlObject {
@@ -168,7 +307,7 @@ class Body extends XhtmlObject {
     }
 
     html.name = "div";
-    html.attributes.class = "xfaRich";
+    html.attributes.class = ["xfaRich"];
     return res;
   }
 
@@ -183,6 +322,10 @@ class Br extends XhtmlObject {
     return "\n";
   }
 
+  [_xfa_object.$pushGlyphs](measure) {
+    measure.addString("\n");
+  }
+
   [_xfa_object.$toHTML](availableSpace) {
     return _utils.HTMLResult.success({
       name: "br"
@@ -208,7 +351,7 @@ class Html extends XhtmlObject {
       return _utils.HTMLResult.success({
         name: "div",
         attributes: {
-          class: "xfaRich",
+          class: ["xfaRich"],
           style: {}
         },
         value: this[_xfa_object.$content] || ""
@@ -218,7 +361,7 @@ class Html extends XhtmlObject {
     if (children.length === 1) {
       const child = children[0];
 
-      if (child.attributes && child.attributes.class === "xfaRich") {
+      if (child.attributes && child.attributes.class.includes("xfaRich")) {
         return _utils.HTMLResult.success(child);
       }
     }
@@ -226,7 +369,7 @@ class Html extends XhtmlObject {
     return _utils.HTMLResult.success({
       name: "div",
       attributes: {
-        class: "xfaRich",
+        class: ["xfaRich"],
         style: {}
       },
       children
@@ -240,6 +383,16 @@ class I extends XhtmlObject {
     super(attributes, "i");
   }
 
+  [_xfa_object.$pushGlyphs](measure) {
+    measure.pushFont({
+      posture: "italic"
+    });
+
+    super[_xfa_object.$pushGlyphs](measure);
+
+    measure.popFont();
+  }
+
 }
 
 class Li extends XhtmlObject {
@@ -261,6 +414,14 @@ class P extends XhtmlObject {
     super(attributes, "p");
   }
 
+  [_xfa_object.$pushGlyphs](measure) {
+    super[_xfa_object.$pushGlyphs](measure, false);
+
+    measure.addString("\n");
+    measure.addPara();
+    measure.popFont();
+  }
+
   [_xfa_object.$text]() {
     return super[_xfa_object.$text]() + "\n";
   }

+ 182 - 0
lib/core/xfa_fonts.js

@@ -0,0 +1,182 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.getXfaFontName = getXfaFontName;
+exports.getXfaFontWidths = getXfaFontWidths;
+
+var _calibri_factors = require("./calibri_factors.js");
+
+var _helvetica_factors = require("./helvetica_factors.js");
+
+var _liberationsans_widths = require("./liberationsans_widths.js");
+
+var _myriadpro_factors = require("./myriadpro_factors.js");
+
+var _segoeui_factors = require("./segoeui_factors.js");
+
+var _core_utils = require("./core_utils.js");
+
+var _fonts_utils = require("./fonts_utils.js");
+
+const getXFAFontMap = (0, _core_utils.getLookupTableFactory)(function (t) {
+  t["MyriadPro-Regular"] = t["PdfJS-Fallback-Regular"] = {
+    name: "LiberationSans-Regular",
+    factors: _myriadpro_factors.MyriadProRegularFactors,
+    baseWidths: _liberationsans_widths.LiberationSansRegularWidths,
+    lineHeight: _myriadpro_factors.MyriadProRegularLineHeight
+  };
+  t["MyriadPro-Bold"] = t["PdfJS-Fallback-Bold"] = {
+    name: "LiberationSans-Bold",
+    factors: _myriadpro_factors.MyriadProBoldFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldWidths,
+    lineHeight: _myriadpro_factors.MyriadProBoldLineHeight
+  };
+  t["MyriadPro-It"] = t["MyriadPro-Italic"] = t["PdfJS-Fallback-Italic"] = {
+    name: "LiberationSans-Italic",
+    factors: _myriadpro_factors.MyriadProItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansItalicWidths,
+    lineHeight: _myriadpro_factors.MyriadProItalicLineHeight
+  };
+  t["MyriadPro-BoldIt"] = t["MyriadPro-BoldItalic"] = t["PdfJS-Fallback-BoldItalic"] = {
+    name: "LiberationSans-BoldItalic",
+    factors: _myriadpro_factors.MyriadProBoldItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldItalicWidths,
+    lineHeight: _myriadpro_factors.MyriadProBoldItalicLineHeight
+  };
+  t.ArialMT = t.Arial = t["Arial-Regular"] = {
+    name: "LiberationSans-Regular",
+    baseWidths: _liberationsans_widths.LiberationSansRegularWidths
+  };
+  t["Arial-BoldMT"] = t["Arial-Bold"] = {
+    name: "LiberationSans-Bold",
+    baseWidths: _liberationsans_widths.LiberationSansBoldWidths
+  };
+  t["Arial-ItalicMT"] = t["Arial-Italic"] = {
+    name: "LiberationSans-Italic",
+    baseWidths: _liberationsans_widths.LiberationSansItalicWidths
+  };
+  t["Arial-BoldItalicMT"] = t["Arial-BoldItalic"] = {
+    name: "LiberationSans-BoldItalic",
+    baseWidths: _liberationsans_widths.LiberationSansBoldItalicWidths
+  };
+  t["Calibri-Regular"] = {
+    name: "LiberationSans-Regular",
+    factors: _calibri_factors.CalibriRegularFactors,
+    baseWidths: _liberationsans_widths.LiberationSansRegularWidths,
+    lineHeight: _calibri_factors.CalibriRegularLineHeight
+  };
+  t["Calibri-Bold"] = {
+    name: "LiberationSans-Bold",
+    factors: _calibri_factors.CalibriBoldFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldWidths,
+    lineHeight: _calibri_factors.CalibriBoldLineHeight
+  };
+  t["Calibri-Italic"] = {
+    name: "LiberationSans-Italic",
+    factors: _calibri_factors.CalibriItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansItalicWidths,
+    lineHeight: _calibri_factors.CalibriItalicLineHeight
+  };
+  t["Calibri-BoldItalic"] = {
+    name: "LiberationSans-BoldItalic",
+    factors: _calibri_factors.CalibriBoldItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldItalicWidths,
+    lineHeight: _calibri_factors.CalibriBoldItalicLineHeight
+  };
+  t["Segoeui-Regular"] = {
+    name: "LiberationSans-Regular",
+    factors: _segoeui_factors.SegoeuiRegularFactors,
+    baseWidths: _liberationsans_widths.LiberationSansRegularWidths,
+    lineHeight: _segoeui_factors.SegoeuiRegularLineHeight
+  };
+  t["Segoeui-Bold"] = {
+    name: "LiberationSans-Bold",
+    factors: _segoeui_factors.SegoeuiBoldFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldWidths,
+    lineHeight: _segoeui_factors.SegoeuiBoldLineHeight
+  };
+  t["Segoeui-Italic"] = {
+    name: "LiberationSans-Italic",
+    factors: _segoeui_factors.SegoeuiItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansItalicWidths,
+    lineHeight: _segoeui_factors.SegoeuiItalicLineHeight
+  };
+  t["Segoeui-BoldItalic"] = {
+    name: "LiberationSans-BoldItalic",
+    factors: _segoeui_factors.SegoeuiBoldItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldItalicWidths,
+    lineHeight: _segoeui_factors.SegoeuiBoldItalicLineHeight
+  };
+  t["Helvetica-Regular"] = t.Helvetica = {
+    name: "LiberationSans-Regular",
+    factors: _helvetica_factors.HelveticaRegularFactors,
+    baseWidths: _liberationsans_widths.LiberationSansRegularWidths,
+    lineHeight: _helvetica_factors.HelveticaRegularLineHeight
+  };
+  t["Helvetica-Bold"] = {
+    name: "LiberationSans-Bold",
+    factors: _helvetica_factors.HelveticaBoldFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldWidths,
+    lineHeight: _helvetica_factors.HelveticaBoldLineHeight
+  };
+  t["Helvetica-Italic"] = {
+    name: "LiberationSans-Italic",
+    factors: _helvetica_factors.HelveticaItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansItalicWidths,
+    lineHeight: _helvetica_factors.HelveticaItalicLineHeight
+  };
+  t["Helvetica-BoldItalic"] = {
+    name: "LiberationSans-BoldItalic",
+    factors: _helvetica_factors.HelveticaBoldItalicFactors,
+    baseWidths: _liberationsans_widths.LiberationSansBoldItalicWidths,
+    lineHeight: _helvetica_factors.HelveticaBoldItalicLineHeight
+  };
+});
+
+function getXfaFontName(name) {
+  const fontName = (0, _fonts_utils.normalizeFontName)(name);
+  const fontMap = getXFAFontMap();
+  return fontMap[fontName];
+}
+
+function getXfaFontWidths(name) {
+  const info = getXfaFontName(name);
+
+  if (!info) {
+    return null;
+  }
+
+  const {
+    baseWidths,
+    factors
+  } = info;
+
+  if (!factors) {
+    return baseWidths;
+  }
+
+  return baseWidths.map((w, i) => w * factors[i]);
+}

+ 22 - 4
lib/core/xref.js

@@ -420,8 +420,29 @@ class XRef {
       } else if (m = objRegExp.exec(token)) {
         const num = m[1] | 0,
               gen = m[2] | 0;
+        let contentLength,
+            startPos = position + token.length,
+            updateEntries = false;
+
+        if (!this.entries[num]) {
+          updateEntries = true;
+        } else if (this.entries[num].gen === gen) {
+          try {
+            const parser = new _parser.Parser({
+              lexer: new _parser.Lexer(stream.makeSubStream(startPos))
+            });
+            parser.getObj();
+            updateEntries = true;
+          } catch (ex) {
+            if (ex instanceof _core_utils.ParserEOFException) {
+              (0, _util.warn)(`indexObjects -- checking object (${token}): "${ex}".`);
+            } else {
+              updateEntries = true;
+            }
+          }
+        }
 
-        if (!this.entries[num] || this.entries[num].gen === gen) {
+        if (updateEntries) {
           this.entries[num] = {
             offset: position - stream.start,
             gen,
@@ -429,9 +450,6 @@ class XRef {
           };
         }
 
-        let contentLength,
-            startPos = position + token.length;
-
         while (startPos < buffer.length) {
           const endPos = startPos + skipUntil(buffer, startPos, objBytes) + 4;
           contentLength = endPos - position;

+ 2 - 2
lib/display/annotation_layer.js

@@ -167,8 +167,8 @@ class AnnotationElement {
       container.style.borderWidth = `${data.borderStyle.width}px`;
 
       if (data.borderStyle.style !== _util.AnnotationBorderStyleType.UNDERLINE) {
-        width = width - 2 * data.borderStyle.width;
-        height = height - 2 * data.borderStyle.width;
+        width -= 2 * data.borderStyle.width;
+        height -= 2 * data.borderStyle.width;
       }
 
       const horizontalRadius = data.borderStyle.horizontalCornerRadius;

+ 88 - 68
lib/display/api.js

@@ -26,7 +26,7 @@ Object.defineProperty(exports, "__esModule", {
 });
 exports.getDocument = getDocument;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
-exports.version = exports.PDFWorker = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.DefaultCMapReaderFactory = exports.DefaultCanvasFactory = exports.build = void 0;
+exports.version = exports.PDFWorker = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.DefaultStandardFontDataFactory = exports.DefaultCMapReaderFactory = exports.DefaultCanvasFactory = exports.build = void 0;
 
 var _util = require("../shared/util.js");
 
@@ -38,8 +38,6 @@ var _node_utils = require("./node_utils.js");
 
 var _annotation_storage = require("./annotation_storage.js");
 
-var _api_compatibility = require("./api_compatibility.js");
-
 var _canvas = require("./canvas.js");
 
 var _worker_options = require("./worker_options.js");
@@ -60,6 +58,8 @@ const DefaultCanvasFactory = _is_node.isNodeJS ? _node_utils.NodeCanvasFactory :
 exports.DefaultCanvasFactory = DefaultCanvasFactory;
 const DefaultCMapReaderFactory = _is_node.isNodeJS ? _node_utils.NodeCMapReaderFactory : _display_utils.DOMCMapReaderFactory;
 exports.DefaultCMapReaderFactory = DefaultCMapReaderFactory;
+const DefaultStandardFontDataFactory = _is_node.isNodeJS ? _node_utils.NodeStandardFontDataFactory : _display_utils.DOMStandardFontDataFactory;
+exports.DefaultStandardFontDataFactory = DefaultStandardFontDataFactory;
 let createPDFNetworkStream;
 
 function setPDFNetworkStreamFactory(pdfNetworkStreamFactory) {
@@ -148,6 +148,7 @@ function getDocument(src) {
 
   params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
   params.CMapReaderFactory = params.CMapReaderFactory || DefaultCMapReaderFactory;
+  params.StandardFontDataFactory = params.StandardFontDataFactory || DefaultStandardFontDataFactory;
   params.ignoreErrors = params.stopAtErrors !== true;
   params.fontExtraProperties = params.fontExtraProperties === true;
   params.pdfBug = params.pdfBug === true;
@@ -161,12 +162,20 @@ function getDocument(src) {
     params.maxImageSize = -1;
   }
 
+  if (typeof params.useWorkerFetch !== "boolean") {
+    params.useWorkerFetch = params.CMapReaderFactory === _display_utils.DOMCMapReaderFactory && params.StandardFontDataFactory === _display_utils.DOMStandardFontDataFactory;
+  }
+
   if (typeof params.isEvalSupported !== "boolean") {
     params.isEvalSupported = true;
   }
 
   if (typeof params.disableFontFace !== "boolean") {
-    params.disableFontFace = _api_compatibility.apiCompatibilityParams.disableFontFace || false;
+    params.disableFontFace = _is_node.isNodeJS;
+  }
+
+  if (typeof params.useSystemFonts !== "boolean") {
+    params.useSystemFonts = !_is_node.isNodeJS && !params.disableFontFace;
   }
 
   if (typeof params.ownerDocument === "undefined") {
@@ -259,7 +268,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
 
   return worker.messageHandler.sendWithPromise("GetDocRequest", {
     docId,
-    apiVersion: '2.9.359',
+    apiVersion: '2.10.377',
     source: {
       data: source.data,
       url: source.url,
@@ -275,7 +284,10 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
     ignoreErrors: source.ignoreErrors,
     isEvalSupported: source.isEvalSupported,
     fontExtraProperties: source.fontExtraProperties,
-    enableXfa: source.enableXfa
+    enableXfa: source.enableXfa,
+    useSystemFonts: source.useSystemFonts,
+    cMapUrl: source.useWorkerFetch ? source.cMapUrl : null,
+    standardFontDataUrl: source.useWorkerFetch ? source.standardFontDataUrl : null
   }).then(function (workerId) {
     if (worker.destroyed) {
       throw new Error("Worker was destroyed");
@@ -400,6 +412,13 @@ class PDFDocumentProxy {
   constructor(pdfInfo, transport) {
     this._pdfInfo = pdfInfo;
     this._transport = transport;
+    Object.defineProperty(this, "fingerprint", {
+      get() {
+        (0, _display_utils.deprecated)("`PDFDocumentProxy.fingerprint`, " + "please use `PDFDocumentProxy.fingerprints` instead.");
+        return this.fingerprints[0];
+      }
+
+    });
   }
 
   get annotationStorage() {
@@ -410,12 +429,16 @@ class PDFDocumentProxy {
     return this._pdfInfo.numPages;
   }
 
-  get fingerprint() {
-    return this._pdfInfo.fingerprint;
+  get fingerprints() {
+    return this._pdfInfo.fingerprints;
   }
 
   get isPureXfa() {
-    return this._pdfInfo.isPureXfa;
+    return !!this._transport._htmlForXfa;
+  }
+
+  get allXfaHtml() {
+    return this._transport._htmlForXfa;
   }
 
   getPage(pageNumber) {
@@ -515,10 +538,6 @@ class PDFDocumentProxy {
   }
 
   saveDocument() {
-    if (arguments.length > 0) {
-      (0, _display_utils.deprecated)("saveDocument no longer accepts any options.");
-    }
-
     if (this._transport.annotationStorage.size <= 0) {
       (0, _display_utils.deprecated)("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead.");
     }
@@ -598,9 +617,11 @@ class PDFPageProxy {
   getAnnotations({
     intent = null
   } = {}) {
-    if (!this._annotationsPromise || this._annotationsIntent !== intent) {
-      this._annotationsPromise = this._transport.getAnnotations(this._pageIndex, intent);
-      this._annotationsIntent = intent;
+    const renderingIntent = intent === "display" || intent === "print" ? intent : null;
+
+    if (!this._annotationsPromise || this._annotationsIntent !== renderingIntent) {
+      this._annotationsPromise = this._transport.getAnnotations(this._pageIndex, renderingIntent);
+      this._annotationsIntent = renderingIntent;
     }
 
     return this._annotationsPromise;
@@ -610,8 +631,8 @@ class PDFPageProxy {
     return this._jsActionsPromise || (this._jsActionsPromise = this._transport.getPageJSActions(this._pageIndex));
   }
 
-  getXfa() {
-    return this._xfaPromise || (this._xfaPromise = this._transport.getPageXfa(this._pageIndex));
+  async getXfa() {
+    return this._transport._htmlForXfa?.children[this._pageIndex] || null;
   }
 
   render({
@@ -628,11 +649,6 @@ class PDFPageProxy {
   }) {
     var _intentState;
 
-    if (arguments[0]?.annotationStorage !== undefined) {
-      (0, _display_utils.deprecated)("render no longer accepts an `annotationStorage` option, " + "please use the `includeAnnotationStorage`-boolean instead.");
-      includeAnnotationStorage || (includeAnnotationStorage = !!arguments[0].annotationStorage);
-    }
-
     if (this._stats) {
       this._stats.time("Overall");
     }
@@ -747,7 +763,9 @@ class PDFPageProxy {
     return renderTask;
   }
 
-  getOperatorList() {
+  getOperatorList({
+    intent = "display"
+  } = {}) {
     function operatorListChanged() {
       if (intentState.operatorList.lastChunk) {
         intentState.opListReadCapability.resolve(intentState.operatorList);
@@ -755,7 +773,7 @@ class PDFPageProxy {
       }
     }
 
-    const renderingIntent = "oplist";
+    const renderingIntent = `oplist-${intent === "print" ? "print" : "display"}`;
 
     let intentState = this._intentStates.get(renderingIntent);
 
@@ -858,7 +876,7 @@ class PDFPageProxy {
         force: true
       });
 
-      if (intent === "oplist") {
+      if (intent.startsWith("oplist-")) {
         continue;
       }
 
@@ -871,7 +889,6 @@ class PDFPageProxy {
     this.objs.clear();
     this._annotationsPromise = null;
     this._jsActionsPromise = null;
-    this._xfaPromise = null;
     this._structTreePromise = null;
     this.pendingCleanup = false;
     return Promise.all(waitOn);
@@ -901,7 +918,6 @@ class PDFPageProxy {
     this.objs.clear();
     this._annotationsPromise = null;
     this._jsActionsPromise = null;
-    this._xfaPromise = null;
     this._structTreePromise = null;
 
     if (resetStats && this._stats) {
@@ -1067,6 +1083,10 @@ class LoopbackPort {
 
   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()}`);
+      }
+
       if (typeof value !== "object" || value === null) {
         return value;
       }
@@ -1110,10 +1130,6 @@ class LoopbackPort {
         return result;
       }
 
-      if (value instanceof URL) {
-        throw new Error(`LoopbackPort.postMessage - cannot clone: ${value}`);
-      }
-
       result = Array.isArray(value) ? [] : Object.create(null);
       cloned.set(value, result);
 
@@ -1129,11 +1145,7 @@ class LoopbackPort {
           continue;
         }
 
-        if (typeof desc.value === "function") {
-          if (value.hasOwnProperty?.(i)) {
-            throw new Error(`LoopbackPort.postMessage - cannot clone: ${value[i]}`);
-          }
-
+        if (typeof desc.value === "function" && !value.hasOwnProperty?.(i)) {
           continue;
         }
 
@@ -1478,13 +1490,21 @@ class WorkerTransport {
     this.fontLoader = new _font_loader.FontLoader({
       docId: loadingTask.docId,
       onUnsupportedFeature: this._onUnsupportedFeature.bind(this),
-      ownerDocument: params.ownerDocument
+      ownerDocument: params.ownerDocument,
+      styleElement: params.styleElement
     });
     this._params = params;
-    this.CMapReaderFactory = new params.CMapReaderFactory({
-      baseUrl: params.cMapUrl,
-      isCompressed: params.cMapPacked
-    });
+
+    if (!params.useWorkerFetch) {
+      this.CMapReaderFactory = new params.CMapReaderFactory({
+        baseUrl: params.cMapUrl,
+        isCompressed: params.cMapPacked
+      });
+      this.StandardFontDataFactory = new params.StandardFontDataFactory({
+        baseUrl: params.standardFontDataUrl
+      });
+    }
+
     this.destroyed = false;
     this.destroyCapability = null;
     this._passwordCapability = null;
@@ -1663,6 +1683,8 @@ class WorkerTransport {
       pdfInfo
     }) => {
       this._numPages = pdfInfo.numPages;
+      this._htmlForXfa = pdfInfo.htmlForXfa;
+      delete pdfInfo.htmlForXfa;
 
       loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this));
     });
@@ -1823,6 +1845,10 @@ class WorkerTransport {
 
           break;
 
+        case "Pattern":
+          pageProxy.objs.resolve(id, imageData);
+          break;
+
         default:
           throw new Error(`Got unknown object type ${type}`);
       }
@@ -1842,27 +1868,27 @@ class WorkerTransport {
       }
     });
     messageHandler.on("UnsupportedFeature", this._onUnsupportedFeature.bind(this));
-    messageHandler.on("FetchBuiltInCMap", (data, sink) => {
+    messageHandler.on("FetchBuiltInCMap", data => {
       if (this.destroyed) {
-        sink.error(new Error("Worker was destroyed"));
-        return;
+        return Promise.reject(new Error("Worker was destroyed."));
       }
 
-      let fetched = false;
+      if (!this.CMapReaderFactory) {
+        return Promise.reject(new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter."));
+      }
 
-      sink.onPull = () => {
-        if (fetched) {
-          sink.close();
-          return;
-        }
+      return this.CMapReaderFactory.fetch(data);
+    });
+    messageHandler.on("FetchStandardFontData", data => {
+      if (this.destroyed) {
+        return Promise.reject(new Error("Worker was destroyed."));
+      }
 
-        fetched = true;
-        this.CMapReaderFactory.fetch(data).then(function (builtInCMap) {
-          sink.enqueue(builtInCMap, 1, [builtInCMap.cMapData.buffer]);
-        }).catch(function (reason) {
-          sink.error(reason);
-        });
-      };
+      if (!this.StandardFontDataFactory) {
+        return Promise.reject(new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter."));
+      }
+
+      return this.StandardFontDataFactory.fetch(data);
     });
   }
 
@@ -1925,6 +1951,7 @@ class WorkerTransport {
 
   saveDocument() {
     return this.messageHandler.sendWithPromise("SaveDocument", {
+      isPureXfa: !!this._htmlForXfa,
       numPages: this._numPages,
       annotationStorage: this.annotationStorage.serializable,
       filename: this._fullReader?.filename ?? null
@@ -1997,12 +2024,6 @@ class WorkerTransport {
     });
   }
 
-  getPageXfa(pageIndex) {
-    return this.messageHandler.sendWithPromise("GetPageXfa", {
-      pageIndex
-    });
-  }
-
   getStructTree(pageIndex) {
     return this.messageHandler.sendWithPromise("GetStructTree", {
       pageIndex
@@ -2075,8 +2096,7 @@ class WorkerTransport {
   get loadingParams() {
     const params = this._params;
     return (0, _util.shadow)(this, "loadingParams", {
-      disableAutoFetch: params.disableAutoFetch,
-      disableFontFace: params.disableFontFace
+      disableAutoFetch: params.disableAutoFetch
     });
   }
 
@@ -2322,7 +2342,7 @@ const InternalRenderTask = function InternalRenderTaskClosure() {
   return InternalRenderTask;
 }();
 
-const version = '2.9.359';
+const version = '2.10.377';
 exports.version = version;
-const build = 'e667c8cbc';
+const build = '156762c48';
 exports.build = build;

+ 194 - 0
lib/display/base_factory.js

@@ -0,0 +1,194 @@
+/**
+ * @licstart The following is the entire license notice for the
+ * Javascript code in this page
+ *
+ * Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @licend The above is the entire license notice for the
+ * Javascript code in this page
+ */
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.BaseSVGFactory = exports.BaseStandardFontDataFactory = exports.BaseCMapReaderFactory = exports.BaseCanvasFactory = void 0;
+
+var _util = require("../shared/util.js");
+
+class BaseCanvasFactory {
+  constructor() {
+    if (this.constructor === BaseCanvasFactory) {
+      (0, _util.unreachable)("Cannot initialize BaseCanvasFactory.");
+    }
+  }
+
+  create(width, height) {
+    if (width <= 0 || height <= 0) {
+      throw new Error("Invalid canvas size");
+    }
+
+    const canvas = this._createCanvas(width, height);
+
+    return {
+      canvas,
+      context: canvas.getContext("2d")
+    };
+  }
+
+  reset(canvasAndContext, width, height) {
+    if (!canvasAndContext.canvas) {
+      throw new Error("Canvas is not specified");
+    }
+
+    if (width <= 0 || height <= 0) {
+      throw new Error("Invalid canvas size");
+    }
+
+    canvasAndContext.canvas.width = width;
+    canvasAndContext.canvas.height = height;
+  }
+
+  destroy(canvasAndContext) {
+    if (!canvasAndContext.canvas) {
+      throw new Error("Canvas is not specified");
+    }
+
+    canvasAndContext.canvas.width = 0;
+    canvasAndContext.canvas.height = 0;
+    canvasAndContext.canvas = null;
+    canvasAndContext.context = null;
+  }
+
+  _createCanvas(width, height) {
+    (0, _util.unreachable)("Abstract method `_createCanvas` called.");
+  }
+
+}
+
+exports.BaseCanvasFactory = BaseCanvasFactory;
+
+class BaseCMapReaderFactory {
+  constructor({
+    baseUrl = null,
+    isCompressed = false
+  }) {
+    if (this.constructor === BaseCMapReaderFactory) {
+      (0, _util.unreachable)("Cannot initialize BaseCMapReaderFactory.");
+    }
+
+    this.baseUrl = baseUrl;
+    this.isCompressed = isCompressed;
+  }
+
+  async fetch({
+    name
+  }) {
+    if (!this.baseUrl) {
+      throw new Error('The CMap "baseUrl" parameter must be specified, ensure that ' + 'the "cMapUrl" and "cMapPacked" API parameters are provided.');
+    }
+
+    if (!name) {
+      throw new Error("CMap name must be specified.");
+    }
+
+    const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
+    const compressionType = this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE;
+    return this._fetchData(url, compressionType).catch(reason => {
+      throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`);
+    });
+  }
+
+  _fetchData(url, compressionType) {
+    (0, _util.unreachable)("Abstract method `_fetchData` called.");
+  }
+
+}
+
+exports.BaseCMapReaderFactory = BaseCMapReaderFactory;
+
+class BaseStandardFontDataFactory {
+  constructor({
+    baseUrl = null
+  }) {
+    if (this.constructor === BaseStandardFontDataFactory) {
+      (0, _util.unreachable)("Cannot initialize BaseStandardFontDataFactory.");
+    }
+
+    this.baseUrl = baseUrl;
+  }
+
+  async fetch({
+    filename
+  }) {
+    if (!this.baseUrl) {
+      throw new Error('The standard font "baseUrl" parameter must be specified, ensure that ' + 'the "standardFontDataUrl" API parameter is provided.');
+    }
+
+    if (!filename) {
+      throw new Error("Font filename must be specified.");
+    }
+
+    const url = `${this.baseUrl}${filename}`;
+    return this._fetchData(url).catch(reason => {
+      throw new Error(`Unable to load font data at: ${url}`);
+    });
+  }
+
+  _fetchData(url) {
+    (0, _util.unreachable)("Abstract method `_fetchData` called.");
+  }
+
+}
+
+exports.BaseStandardFontDataFactory = BaseStandardFontDataFactory;
+
+class BaseSVGFactory {
+  constructor() {
+    if (this.constructor === BaseSVGFactory) {
+      (0, _util.unreachable)("Cannot initialize BaseSVGFactory.");
+    }
+  }
+
+  create(width, height) {
+    if (width <= 0 || height <= 0) {
+      throw new Error("Invalid SVG dimensions");
+    }
+
+    const svg = this._createSVG("svg:svg");
+
+    svg.setAttribute("version", "1.1");
+    svg.setAttribute("width", `${width}px`);
+    svg.setAttribute("height", `${height}px`);
+    svg.setAttribute("preserveAspectRatio", "none");
+    svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
+    return svg;
+  }
+
+  createElement(type) {
+    if (typeof type !== "string") {
+      throw new Error("Invalid SVG element type");
+    }
+
+    return this._createSVG(type);
+  }
+
+  _createSVG(type) {
+    (0, _util.unreachable)("Abstract method `_createSVG` called.");
+  }
+
+}
+
+exports.BaseSVGFactory = BaseSVGFactory;

+ 142 - 77
lib/display/canvas.js

@@ -112,10 +112,10 @@ function addContextCurrentTransform(ctx) {
 
   ctx.scale = function ctxScale(x, y) {
     const m = this._transformMatrix;
-    m[0] = m[0] * x;
-    m[1] = m[1] * x;
-    m[2] = m[2] * y;
-    m[3] = m[3] * y;
+    m[0] *= x;
+    m[1] *= x;
+    m[2] *= y;
+    m[3] *= y;
 
     this._originalScale(x, y);
   };
@@ -763,6 +763,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       this.markedContentStack = [];
       this.optionalContentConfig = optionalContentConfig;
       this.cachedCanvases = new CachedCanvases(this.canvasFactory);
+      this.cachedPatterns = new Map();
 
       if (canvasCtx) {
         addContextCurrentTransform(canvasCtx);
@@ -881,12 +882,100 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       }
 
       this.cachedCanvases.clear();
+      this.cachedPatterns.clear();
 
       if (this.imageLayer) {
         this.imageLayer.endLayout();
       }
     }
 
+    _scaleImage(img, inverseTransform) {
+      const width = img.width;
+      const height = img.height;
+      let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1);
+      let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1);
+      let paintWidth = width,
+          paintHeight = height;
+      let tmpCanvasId = "prescale1";
+      let tmpCanvas, tmpCtx;
+
+      while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
+        let newWidth = paintWidth,
+            newHeight = paintHeight;
+
+        if (widthScale > 2 && paintWidth > 1) {
+          newWidth = Math.ceil(paintWidth / 2);
+          widthScale /= paintWidth / newWidth;
+        }
+
+        if (heightScale > 2 && paintHeight > 1) {
+          newHeight = Math.ceil(paintHeight / 2);
+          heightScale /= paintHeight / newHeight;
+        }
+
+        tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
+        tmpCtx = tmpCanvas.context;
+        tmpCtx.clearRect(0, 0, newWidth, newHeight);
+        tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
+        img = tmpCanvas.canvas;
+        paintWidth = newWidth;
+        paintHeight = newHeight;
+        tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
+      }
+
+      return {
+        img,
+        paintWidth,
+        paintHeight
+      };
+    }
+
+    _createMaskCanvas(img) {
+      const ctx = this.ctx;
+      const width = img.width,
+            height = img.height;
+      const fillColor = this.current.fillColor;
+      const isPatternFill = this.current.patternFill;
+      const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
+      const maskCtx = maskCanvas.context;
+      putBinaryImageMask(maskCtx, img);
+      const objToCanvas = ctx.mozCurrentTransform;
+
+      let maskToCanvas = _util.Util.transform(objToCanvas, [1 / width, 0, 0, -1 / height, 0, 0]);
+
+      maskToCanvas = _util.Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
+
+      const cord1 = _util.Util.applyTransform([0, 0], maskToCanvas);
+
+      const cord2 = _util.Util.applyTransform([width, height], maskToCanvas);
+
+      const rect = _util.Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);
+
+      const drawnWidth = Math.ceil(rect[2] - rect[0]);
+      const drawnHeight = Math.ceil(rect[3] - rect[1]);
+      const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight, true);
+      const fillCtx = fillCanvas.context;
+      const offsetX = Math.min(cord1[0], cord2[0]);
+      const offsetY = Math.min(cord1[1], cord2[1]);
+      fillCtx.translate(-offsetX, -offsetY);
+      fillCtx.transform.apply(fillCtx, maskToCanvas);
+
+      const scaled = this._scaleImage(maskCanvas.canvas, fillCtx.mozCurrentTransformInverse);
+
+      fillCtx.drawImage(scaled.img, 0, 0, scaled.img.width, scaled.img.height, 0, 0, width, height);
+      fillCtx.globalCompositeOperation = "source-in";
+
+      const inverse = _util.Util.transform(fillCtx.mozCurrentTransformInverse, [1, 0, 0, 1, -offsetX, -offsetY]);
+
+      fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, false) : fillColor;
+      fillCtx.fillRect(0, 0, width, height);
+      return {
+        canvas: fillCanvas.canvas,
+        offsetX: Math.round(offsetX),
+        offsetY: Math.round(offsetY)
+      };
+    }
+
     setLineWidth(width) {
       this.current.lineWidth = width;
       this.ctx.lineWidth = width;
@@ -1170,7 +1259,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
         if (typeof strokeColor === "object" && strokeColor?.getPattern) {
           const lineWidth = this.getSinglePixelWidth();
           ctx.save();
-          ctx.strokeStyle = strokeColor.getPattern(ctx, this);
+          ctx.strokeStyle = strokeColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
           ctx.lineWidth = Math.max(lineWidth, this.current.lineWidth);
           ctx.stroke();
           ctx.restore();
@@ -1211,7 +1300,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
 
       if (isPatternFill) {
         ctx.save();
-        ctx.fillStyle = fillColor.getPattern(ctx, this);
+        ctx.fillStyle = fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
         needRestore = true;
       }
 
@@ -1520,7 +1609,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
 
       if (current.patternFill) {
         ctx.save();
-        const pattern = current.fillColor.getPattern(ctx, this);
+        const pattern = current.fillColor.getPattern(ctx, this, ctx.mozCurrentTransformInverse);
         patternTransform = ctx.mozCurrentTransform;
         ctx.restore();
         ctx.fillStyle = pattern;
@@ -1724,7 +1813,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
         };
         pattern = new _pattern_helper.TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform);
       } else {
-        pattern = (0, _pattern_helper.getShadingPattern)(IR);
+        pattern = this._getPattern(IR[1]);
       }
 
       return pattern;
@@ -1754,15 +1843,27 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       this.current.patternFill = false;
     }
 
-    shadingFill(patternIR) {
+    _getPattern(objId) {
+      if (this.cachedPatterns.has(objId)) {
+        return this.cachedPatterns.get(objId);
+      }
+
+      const pattern = (0, _pattern_helper.getShadingPattern)(this.objs.get(objId));
+      this.cachedPatterns.set(objId, pattern);
+      return pattern;
+    }
+
+    shadingFill(objId) {
       if (!this.contentVisible) {
         return;
       }
 
       const ctx = this.ctx;
       this.save();
-      const pattern = (0, _pattern_helper.getShadingPattern)(patternIR);
-      ctx.fillStyle = pattern.getPattern(ctx, this, true);
+
+      const pattern = this._getPattern(objId);
+
+      ctx.fillStyle = pattern.getPattern(ctx, this, ctx.mozCurrentTransformInverse, true);
       const inv = ctx.mozCurrentTransformInverse;
 
       if (inv) {
@@ -1952,7 +2053,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       this.restore();
     }
 
-    beginAnnotation(rect, transform, matrix) {
+    beginAnnotation(id, rect, transform, matrix) {
       this.save();
       resetCtxToDefault(this.ctx);
       this.current = new CanvasExtraState();
@@ -1981,8 +2082,6 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       const ctx = this.ctx;
       const width = img.width,
             height = img.height;
-      const fillColor = this.current.fillColor;
-      const isPatternFill = this.current.patternFill;
       const glyph = this.processingType3;
 
       if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) {
@@ -2002,15 +2101,13 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
         return;
       }
 
-      const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
-      const maskCtx = maskCanvas.context;
-      maskCtx.save();
-      putBinaryImageMask(maskCtx, img);
-      maskCtx.globalCompositeOperation = "source-in";
-      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
-      maskCtx.fillRect(0, 0, width, height);
-      maskCtx.restore();
-      this.paintInlineImageXObject(maskCanvas.canvas);
+      const mask = this._createMaskCanvas(img);
+
+      const maskCanvas = mask.canvas;
+      ctx.save();
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
+      ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
+      ctx.restore();
     }
 
     paintImageMaskXObjectRepeat(imgData, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
@@ -2018,27 +2115,24 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
         return;
       }
 
-      const width = imgData.width;
-      const height = imgData.height;
-      const fillColor = this.current.fillColor;
-      const isPatternFill = this.current.patternFill;
-      const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
-      const maskCtx = maskCanvas.context;
-      maskCtx.save();
-      putBinaryImageMask(maskCtx, imgData);
-      maskCtx.globalCompositeOperation = "source-in";
-      maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
-      maskCtx.fillRect(0, 0, width, height);
-      maskCtx.restore();
       const ctx = this.ctx;
+      ctx.save();
+      const currentTransform = ctx.mozCurrentTransform;
+      ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
+
+      const mask = this._createMaskCanvas(imgData);
+
+      ctx.setTransform(1, 0, 0, 1, 0, 0);
 
       for (let i = 0, ii = positions.length; i < ii; i += 2) {
-        ctx.save();
-        ctx.transform(scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]);
-        ctx.scale(1, -1);
-        ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
-        ctx.restore();
+        const trans = _util.Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);
+
+        const [x, y] = _util.Util.applyTransform([0, 0], trans);
+
+        ctx.drawImage(mask.canvas, x, y);
       }
+
+      ctx.restore();
     }
 
     paintImageMaskXObjectGroup(images) {
@@ -2059,7 +2153,7 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
         maskCtx.save();
         putBinaryImageMask(maskCtx, image);
         maskCtx.globalCompositeOperation = "source-in";
-        maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this) : fillColor;
+        maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, ctx.mozCurrentTransformInverse, false) : fillColor;
         maskCtx.fillRect(0, 0, width, height);
         maskCtx.restore();
         ctx.save();
@@ -2124,49 +2218,20 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
       const ctx = this.ctx;
       this.save();
       ctx.scale(1 / width, -1 / height);
-      const currentTransform = ctx.mozCurrentTransformInverse;
-      let widthScale = Math.max(Math.hypot(currentTransform[0], currentTransform[1]), 1);
-      let heightScale = Math.max(Math.hypot(currentTransform[2], currentTransform[3]), 1);
-      let imgToPaint, tmpCanvas, tmpCtx;
+      let imgToPaint;
 
       if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) {
         imgToPaint = imgData;
       } else {
-        tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
-        tmpCtx = tmpCanvas.context;
+        const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
+        const tmpCtx = tmpCanvas.context;
         putBinaryImageData(tmpCtx, imgData, this.current.transferMaps);
         imgToPaint = tmpCanvas.canvas;
       }
 
-      let paintWidth = width,
-          paintHeight = height;
-      let tmpCanvasId = "prescale1";
-
-      while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
-        let newWidth = paintWidth,
-            newHeight = paintHeight;
-
-        if (widthScale > 2 && paintWidth > 1) {
-          newWidth = Math.ceil(paintWidth / 2);
-          widthScale /= paintWidth / newWidth;
-        }
-
-        if (heightScale > 2 && paintHeight > 1) {
-          newHeight = Math.ceil(paintHeight / 2);
-          heightScale /= paintHeight / newHeight;
-        }
-
-        tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
-        tmpCtx = tmpCanvas.context;
-        tmpCtx.clearRect(0, 0, newWidth, newHeight);
-        tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
-        imgToPaint = tmpCanvas.canvas;
-        paintWidth = newWidth;
-        paintHeight = newHeight;
-        tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
-      }
+      const scaled = this._scaleImage(imgToPaint, ctx.mozCurrentTransformInverse);
 
-      ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, -height, width, height);
+      ctx.drawImage(scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);
 
       if (this.imageLayer) {
         const position = this.getCanvasPosition(0, -height);
@@ -2174,8 +2239,8 @@ const CanvasGraphics = function CanvasGraphicsClosure() {
           imgData,
           left: position[0],
           top: position[1],
-          width: width / currentTransform[0],
-          height: height / currentTransform[3]
+          width: width / ctx.mozCurrentTransformInverse[0],
+          height: height / ctx.mozCurrentTransformInverse[3]
         });
       }
 

+ 69 - 149
lib/display/display_utils.js

@@ -28,59 +28,22 @@ exports.addLinkAttributes = addLinkAttributes;
 exports.deprecated = deprecated;
 exports.getFilenameFromUrl = getFilenameFromUrl;
 exports.getPdfFilenameFromUrl = getPdfFilenameFromUrl;
+exports.getXfaPageViewport = getXfaPageViewport;
 exports.isDataScheme = isDataScheme;
-exports.isFetchSupported = isFetchSupported;
 exports.isPdfFile = isPdfFile;
 exports.isValidFetchUrl = isValidFetchUrl;
 exports.loadScript = loadScript;
-exports.StatTimer = exports.RenderingCancelledException = exports.PDFDateString = exports.PageViewport = exports.LinkTarget = exports.DOMSVGFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.BaseCMapReaderFactory = exports.BaseCanvasFactory = void 0;
+exports.StatTimer = exports.RenderingCancelledException = exports.PDFDateString = exports.PageViewport = exports.LinkTarget = exports.DOMSVGFactory = exports.DOMStandardFontDataFactory = exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = void 0;
 
 var _util = require("../shared/util.js");
 
+var _base_factory = require("./base_factory.js");
+
 const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
 exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
 const SVG_NS = "http://www.w3.org/2000/svg";
 
-class BaseCanvasFactory {
-  constructor() {
-    if (this.constructor === BaseCanvasFactory) {
-      (0, _util.unreachable)("Cannot initialize BaseCanvasFactory.");
-    }
-  }
-
-  create(width, height) {
-    (0, _util.unreachable)("Abstract method `create` called.");
-  }
-
-  reset(canvasAndContext, width, height) {
-    if (!canvasAndContext.canvas) {
-      throw new Error("Canvas is not specified");
-    }
-
-    if (width <= 0 || height <= 0) {
-      throw new Error("Invalid canvas size");
-    }
-
-    canvasAndContext.canvas.width = width;
-    canvasAndContext.canvas.height = height;
-  }
-
-  destroy(canvasAndContext) {
-    if (!canvasAndContext.canvas) {
-      throw new Error("Canvas is not specified");
-    }
-
-    canvasAndContext.canvas.width = 0;
-    canvasAndContext.canvas.height = 0;
-    canvasAndContext.canvas = null;
-    canvasAndContext.context = null;
-  }
-
-}
-
-exports.BaseCanvasFactory = BaseCanvasFactory;
-
-class DOMCanvasFactory extends BaseCanvasFactory {
+class DOMCanvasFactory extends _base_factory.BaseCanvasFactory {
   constructor({
     ownerDocument = globalThis.document
   } = {}) {
@@ -88,123 +51,71 @@ class DOMCanvasFactory extends BaseCanvasFactory {
     this._document = ownerDocument;
   }
 
-  create(width, height) {
-    if (width <= 0 || height <= 0) {
-      throw new Error("Invalid canvas size");
-    }
-
+  _createCanvas(width, height) {
     const canvas = this._document.createElement("canvas");
 
-    const context = canvas.getContext("2d");
     canvas.width = width;
     canvas.height = height;
-    return {
-      canvas,
-      context
-    };
+    return canvas;
   }
 
 }
 
 exports.DOMCanvasFactory = DOMCanvasFactory;
 
-class BaseCMapReaderFactory {
-  constructor({
-    baseUrl = null,
-    isCompressed = false
-  }) {
-    if (this.constructor === BaseCMapReaderFactory) {
-      (0, _util.unreachable)("Cannot initialize BaseCMapReaderFactory.");
+async function fetchData(url, asTypedArray = false) {
+  if (isValidFetchUrl(url, document.baseURI)) {
+    const response = await fetch(url);
+
+    if (!response.ok) {
+      throw new Error(response.statusText);
     }
 
-    this.baseUrl = baseUrl;
-    this.isCompressed = isCompressed;
+    return asTypedArray ? new Uint8Array(await response.arrayBuffer()) : (0, _util.stringToBytes)(await response.text());
   }
 
-  async fetch({
-    name
-  }) {
-    if (!this.baseUrl) {
-      throw new Error('The CMap "baseUrl" parameter must be specified, ensure that ' + 'the "cMapUrl" and "cMapPacked" API parameters are provided.');
-    }
+  return new Promise((resolve, reject) => {
+    const request = new XMLHttpRequest();
+    request.open("GET", url, true);
 
-    if (!name) {
-      throw new Error("CMap name must be specified.");
+    if (asTypedArray) {
+      request.responseType = "arraybuffer";
     }
 
-    const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
-    const compressionType = this.isCompressed ? _util.CMapCompressionType.BINARY : _util.CMapCompressionType.NONE;
-    return this._fetchData(url, compressionType).catch(reason => {
-      throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`);
-    });
-  }
-
-  _fetchData(url, compressionType) {
-    (0, _util.unreachable)("Abstract method `_fetchData` called.");
-  }
-
-}
+    request.onreadystatechange = () => {
+      if (request.readyState !== XMLHttpRequest.DONE) {
+        return;
+      }
 
-exports.BaseCMapReaderFactory = BaseCMapReaderFactory;
+      if (request.status === 200 || request.status === 0) {
+        let data;
 
-class DOMCMapReaderFactory extends BaseCMapReaderFactory {
-  _fetchData(url, compressionType) {
-    if (isFetchSupported() && isValidFetchUrl(url, document.baseURI)) {
-      return fetch(url).then(async response => {
-        if (!response.ok) {
-          throw new Error(response.statusText);
+        if (asTypedArray && request.response) {
+          data = new Uint8Array(request.response);
+        } else if (!asTypedArray && request.responseText) {
+          data = (0, _util.stringToBytes)(request.responseText);
         }
 
-        let cMapData;
-
-        if (this.isCompressed) {
-          cMapData = new Uint8Array(await response.arrayBuffer());
-        } else {
-          cMapData = (0, _util.stringToBytes)(await response.text());
+        if (data) {
+          resolve(data);
+          return;
         }
-
-        return {
-          cMapData,
-          compressionType
-        };
-      });
-    }
-
-    return new Promise((resolve, reject) => {
-      const request = new XMLHttpRequest();
-      request.open("GET", url, true);
-
-      if (this.isCompressed) {
-        request.responseType = "arraybuffer";
       }
 
-      request.onreadystatechange = () => {
-        if (request.readyState !== XMLHttpRequest.DONE) {
-          return;
-        }
+      reject(new Error(request.statusText));
+    };
 
-        if (request.status === 200 || request.status === 0) {
-          let cMapData;
-
-          if (this.isCompressed && request.response) {
-            cMapData = new Uint8Array(request.response);
-          } else if (!this.isCompressed && request.responseText) {
-            cMapData = (0, _util.stringToBytes)(request.responseText);
-          }
-
-          if (cMapData) {
-            resolve({
-              cMapData,
-              compressionType
-            });
-            return;
-          }
-        }
+    request.send(null);
+  });
+}
 
-        reject(new Error(request.statusText));
+class DOMCMapReaderFactory extends _base_factory.BaseCMapReaderFactory {
+  _fetchData(url, compressionType) {
+    return fetchData(url, this.isCompressed).then(data => {
+      return {
+        cMapData: data,
+        compressionType
       };
-
-      request.send(null);
     });
   }
 
@@ -212,20 +123,17 @@ class DOMCMapReaderFactory extends BaseCMapReaderFactory {
 
 exports.DOMCMapReaderFactory = DOMCMapReaderFactory;
 
-class DOMSVGFactory {
-  create(width, height) {
-    (0, _util.assert)(width > 0 && height > 0, "Invalid SVG dimensions");
-    const svg = document.createElementNS(SVG_NS, "svg:svg");
-    svg.setAttribute("version", "1.1");
-    svg.setAttribute("width", width + "px");
-    svg.setAttribute("height", height + "px");
-    svg.setAttribute("preserveAspectRatio", "none");
-    svg.setAttribute("viewBox", "0 0 " + width + " " + height);
-    return svg;
+class DOMStandardFontDataFactory extends _base_factory.BaseStandardFontDataFactory {
+  _fetchData(url) {
+    return fetchData(url, true);
   }
 
-  createElement(type) {
-    (0, _util.assert)(typeof type === "string", "Invalid SVG element type");
+}
+
+exports.DOMStandardFontDataFactory = DOMStandardFontDataFactory;
+
+class DOMSVGFactory extends _base_factory.BaseSVGFactory {
+  _createSVG(type) {
     return document.createElementNS(SVG_NS, type);
   }
 
@@ -517,10 +425,6 @@ class StatTimer {
 
 exports.StatTimer = StatTimer;
 
-function isFetchSupported() {
-  return typeof fetch !== "undefined" && typeof Response !== "undefined" && "body" in Response.prototype && typeof ReadableStream !== "undefined";
-}
-
 function isValidFetchUrl(url, baseUrl) {
   try {
     const {
@@ -605,4 +509,20 @@ class PDFDateString {
 
 }
 
-exports.PDFDateString = PDFDateString;
+exports.PDFDateString = PDFDateString;
+
+function getXfaPageViewport(xfaPage, {
+  scale = 1,
+  rotation = 0
+}) {
+  const {
+    width,
+    height
+  } = xfaPage.attributes.style;
+  const viewBox = [0, 0, parseInt(width), parseInt(height)];
+  return new PageViewport({
+    viewBox,
+    scale,
+    rotation
+  });
+}

+ 4 - 2
lib/display/font_loader.js

@@ -32,7 +32,8 @@ class BaseFontLoader {
   constructor({
     docId,
     onUnsupportedFeature,
-    ownerDocument = globalThis.document
+    ownerDocument = globalThis.document,
+    styleElement = null
   }) {
     if (this.constructor === BaseFontLoader) {
       (0, _util.unreachable)("Cannot initialize BaseFontLoader.");
@@ -129,7 +130,8 @@ class BaseFontLoader {
   }
 
   get isFontLoadingAPISupported() {
-    return (0, _util.shadow)(this, "isFontLoadingAPISupported", !!this._document?.fonts);
+    const hasFonts = !!this._document?.fonts;
+    return (0, _util.shadow)(this, "isFontLoadingAPISupported", hasFonts);
   }
 
   get isSyncFontLoadingSupported() {

+ 38 - 27
lib/display/node_utils.js

@@ -24,9 +24,9 @@
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.NodeCMapReaderFactory = exports.NodeCanvasFactory = void 0;
+exports.NodeStandardFontDataFactory = exports.NodeCMapReaderFactory = exports.NodeCanvasFactory = void 0;
 
-var _display_utils = require("./display_utils.js");
+var _base_factory = require("./base_factory.js");
 
 var _is_node = require("../shared/is_node.js");
 
@@ -46,42 +46,53 @@ let NodeCMapReaderFactory = class {
 
 };
 exports.NodeCMapReaderFactory = NodeCMapReaderFactory;
+let NodeStandardFontDataFactory = class {
+  constructor() {
+    (0, _util.unreachable)("Not implemented: NodeStandardFontDataFactory");
+  }
+
+};
+exports.NodeStandardFontDataFactory = NodeStandardFontDataFactory;
 
 if (_is_node.isNodeJS) {
-  exports.NodeCanvasFactory = NodeCanvasFactory = class extends _display_utils.BaseCanvasFactory {
-    create(width, height) {
-      if (width <= 0 || height <= 0) {
-        throw new Error("Invalid canvas size");
-      }
+  const fetchData = function (url) {
+    return new Promise((resolve, reject) => {
+      const fs = require("fs");
+
+      fs.readFile(url, (error, data) => {
+        if (error || !data) {
+          reject(new Error(error));
+          return;
+        }
 
+        resolve(new Uint8Array(data));
+      });
+    });
+  };
+
+  exports.NodeCanvasFactory = NodeCanvasFactory = class extends _base_factory.BaseCanvasFactory {
+    _createCanvas(width, height) {
       const Canvas = require("canvas");
 
-      const canvas = Canvas.createCanvas(width, height);
-      return {
-        canvas,
-        context: canvas.getContext("2d")
-      };
+      return Canvas.createCanvas(width, height);
     }
 
   };
-  exports.NodeCMapReaderFactory = NodeCMapReaderFactory = class extends _display_utils.BaseCMapReaderFactory {
+  exports.NodeCMapReaderFactory = NodeCMapReaderFactory = class extends _base_factory.BaseCMapReaderFactory {
     _fetchData(url, compressionType) {
-      return new Promise((resolve, reject) => {
-        const fs = require("fs");
-
-        fs.readFile(url, (error, data) => {
-          if (error || !data) {
-            reject(new Error(error));
-            return;
-          }
-
-          resolve({
-            cMapData: new Uint8Array(data),
-            compressionType
-          });
-        });
+      return fetchData(url).then(data => {
+        return {
+          cMapData: data,
+          compressionType
+        };
       });
     }
 
   };
+  exports.NodeStandardFontDataFactory = NodeStandardFontDataFactory = class extends _base_factory.BaseStandardFontDataFactory {
+    _fetchData(url) {
+      return fetchData(url);
+    }
+
+  };
 }

+ 85 - 56
lib/display/pattern_helper.js

@@ -29,20 +29,6 @@ exports.TilingPattern = void 0;
 
 var _util = require("../shared/util.js");
 
-let svgElement;
-
-function createMatrix(matrix) {
-  if (typeof DOMMatrix !== "undefined") {
-    return new DOMMatrix(matrix);
-  }
-
-  if (!svgElement) {
-    svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
-  }
-
-  return svgElement.createSVGMatrix(matrix);
-}
-
 function applyBoundingBox(ctx, bbox) {
   if (!bbox || typeof Path2D === "undefined") {
     return;
@@ -79,42 +65,65 @@ class RadialAxialShadingPattern extends BaseShadingPattern {
     this._r0 = IR[6];
     this._r1 = IR[7];
     this._matrix = IR[8];
+    this._patternCache = null;
   }
 
-  getPattern(ctx, owner, shadingFill) {
-    const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", ctx.canvas.width, 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);
-
-    if (!shadingFill) {
-      tmpCtx.setTransform.apply(tmpCtx, owner.baseTransform);
-
-      if (this._matrix) {
-        tmpCtx.transform.apply(tmpCtx, this._matrix);
-      }
-    } else {
-      tmpCtx.setTransform.apply(tmpCtx, ctx.mozCurrentTransform);
-    }
-
-    applyBoundingBox(tmpCtx, this._bbox);
+  _createGradient(ctx) {
     let grad;
 
     if (this._type === "axial") {
-      grad = tmpCtx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);
+      grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);
     } else if (this._type === "radial") {
-      grad = tmpCtx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);
+      grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);
     }
 
     for (const colorStop of this._colorStops) {
       grad.addColorStop(colorStop[0], colorStop[1]);
     }
 
-    tmpCtx.fillStyle = grad;
-    tmpCtx.fill();
-    const pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
-    pattern.setTransform(createMatrix(ctx.mozCurrentTransformInverse));
+    return grad;
+  }
+
+  getPattern(ctx, owner, inverse, shadingFill = false) {
+    let pattern;
+
+    if (this._patternCache) {
+      pattern = this._patternCache;
+    } else {
+      if (!shadingFill) {
+        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);
+        }
+
+        applyBoundingBox(tmpCtx, this._bbox);
+        tmpCtx.fillStyle = this._createGradient(tmpCtx);
+        tmpCtx.fill();
+        pattern = ctx.createPattern(tmpCanvas.canvas, "repeat");
+      } else {
+        applyBoundingBox(ctx, this._bbox);
+        pattern = this._createGradient(ctx);
+      }
+
+      this._patternCache = pattern;
+    }
+
+    if (!shadingFill) {
+      const domMatrix = new DOMMatrix(inverse);
+
+      try {
+        pattern.setTransform(domMatrix);
+      } catch (ex) {
+        (0, _util.warn)(`RadialAxialShadingPattern.getPattern: "${ex?.message}".`);
+      }
+    }
+
     return pattern;
   }
 
@@ -185,8 +194,6 @@ function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
 
       if (y < y1) {
         k = 0;
-      } else if (y1 === y2) {
-        k = 1;
       } else {
         k = (y1 - y) / (y1 - y2);
       }
@@ -345,7 +352,7 @@ class MeshShadingPattern extends BaseShadingPattern {
     };
   }
 
-  getPattern(ctx, owner, shadingFill) {
+  getPattern(ctx, owner, inverse, shadingFill = false) {
     applyBoundingBox(ctx, this._bbox);
     let scale;
 
@@ -451,15 +458,33 @@ class TilingPattern {
     const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
     graphics.groupLevel = owner.groupLevel;
     this.setFillAndStrokeStyleToContext(graphics, paintType, color);
+    let adjustedX0 = x0;
+    let adjustedY0 = y0;
+    let adjustedX1 = x1;
+    let adjustedY1 = y1;
+
+    if (x0 < 0) {
+      adjustedX0 = 0;
+      adjustedX1 += Math.abs(x0);
+    }
+
+    if (y0 < 0) {
+      adjustedY0 = 0;
+      adjustedY1 += Math.abs(y0);
+    }
+
+    tmpCtx.translate(-(dimx.scale * adjustedX0), -(dimy.scale * adjustedY0));
     graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
-    this.clipBbox(graphics, bbox, x0, y0, x1, y1);
+    this.clipBbox(graphics, adjustedX0, adjustedY0, adjustedX1, adjustedY1);
     graphics.baseTransform = graphics.ctx.mozCurrentTransform.slice();
     graphics.executeOperatorList(operatorList);
     graphics.endDrawing();
     return {
       canvas: tmpCanvas.canvas,
       scaleX: dimx.scale,
-      scaleY: dimy.scale
+      scaleY: dimy.scale,
+      offsetX: adjustedX0,
+      offsetY: adjustedY0
     };
   }
 
@@ -480,14 +505,12 @@ class TilingPattern {
     };
   }
 
-  clipBbox(graphics, bbox, x0, y0, x1, y1) {
-    if (Array.isArray(bbox) && bbox.length === 4) {
-      const bboxWidth = x1 - x0;
-      const bboxHeight = y1 - y0;
-      graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
-      graphics.clip();
-      graphics.endPath();
-    }
+  clipBbox(graphics, x0, y0, x1, y1) {
+    const bboxWidth = x1 - x0;
+    const bboxHeight = y1 - y0;
+    graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
+    graphics.clip();
+    graphics.endPath();
   }
 
   setFillAndStrokeStyleToContext(graphics, paintType, color) {
@@ -517,9 +540,8 @@ class TilingPattern {
     }
   }
 
-  getPattern(ctx, owner, shadingFill) {
-    ctx = this.ctx;
-    let matrix = ctx.mozCurrentTransformInverse;
+  getPattern(ctx, owner, inverse, shadingFill = false) {
+    let matrix = inverse;
 
     if (!shadingFill) {
       matrix = _util.Util.transform(matrix, owner.baseTransform);
@@ -530,10 +552,17 @@ class TilingPattern {
     }
 
     const temporaryPatternCanvas = this.createPatternCanvas(owner);
-    let domMatrix = createMatrix(matrix);
+    let domMatrix = new DOMMatrix(matrix);
+    domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
     domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY);
     const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
-    pattern.setTransform(domMatrix);
+
+    try {
+      pattern.setTransform(domMatrix);
+    } catch (ex) {
+      (0, _util.warn)(`TilingPattern.getPattern: "${ex?.message}".`);
+    }
+
     return pattern;
   }
 

+ 1 - 3
lib/display/svg.js

@@ -1028,9 +1028,7 @@ exports.SVGGraphics = SVGGraphics;
       const paintType = args[7];
       const tilingId = `shading${shadingCount++}`;
 
-      const [tx0, ty0] = _util.Util.applyTransform([x0, y0], matrix);
-
-      const [tx1, ty1] = _util.Util.applyTransform([x1, y1], matrix);
+      const [tx0, ty0, tx1, ty1] = _util.Util.normalizeRect([..._util.Util.applyTransform([x0, y0], matrix), ..._util.Util.applyTransform([x1, y1], matrix)]);
 
       const [xscale, yscale] = _util.Util.singularValueDecompose2dScale(matrix);
 

+ 58 - 34
lib/display/xfa_layer.js

@@ -27,48 +27,54 @@ Object.defineProperty(exports, "__esModule", {
 exports.XfaLayer = void 0;
 
 class XfaLayer {
-  static setupStorage(html, fieldId, element, storage) {
-    const storedData = storage.getValue(fieldId, {
+  static setupStorage(html, id, element, storage, intent) {
+    const storedData = storage.getValue(id, {
       value: null
     });
 
     switch (element.name) {
       case "textarea":
-        html.textContent = storedData.value !== null ? storedData.value : "";
+        if (storedData.value !== null) {
+          html.textContent = storedData.value;
+        }
+
+        if (intent === "print") {
+          break;
+        }
+
         html.addEventListener("input", event => {
-          storage.setValue(fieldId, {
+          storage.setValue(id, {
             value: event.target.value
           });
         });
         break;
 
       case "input":
-        if (storedData.value !== null) {
-          html.setAttribute("value", storedData.value);
-        }
+        if (element.attributes.type === "radio" || element.attributes.type === "checkbox") {
+          if (storedData.value === element.attributes.xfaOn) {
+            html.setAttribute("checked", true);
+          }
 
-        if (element.attributes.type === "radio") {
-          html.addEventListener("change", event => {
-            const {
-              target
-            } = event;
-
-            for (const radio of document.getElementsByName(target.name)) {
-              if (radio !== target) {
-                const id = radio.id;
-                storage.setValue(id.split("-")[0], {
-                  value: false
-                });
-              }
-            }
+          if (intent === "print") {
+            break;
+          }
 
-            storage.setValue(fieldId, {
-              value: target.checked
+          html.addEventListener("change", event => {
+            storage.setValue(id, {
+              value: event.target.getAttribute("xfaOn")
             });
           });
         } else {
+          if (storedData.value !== null) {
+            html.setAttribute("value", storedData.value);
+          }
+
+          if (intent === "print") {
+            break;
+          }
+
           html.addEventListener("input", event => {
-            storage.setValue(fieldId, {
+            storage.setValue(id, {
               value: event.target.value
             });
           });
@@ -87,8 +93,8 @@ class XfaLayer {
 
         html.addEventListener("input", event => {
           const options = event.target.options;
-          const value = options.selectedIndex === -1 ? null : options[options.selectedIndex].value;
-          storage.setValue(fieldId, {
+          const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value;
+          storage.setValue(id, {
             value
           });
         });
@@ -96,19 +102,25 @@ class XfaLayer {
     }
   }
 
-  static setAttributes(html, element, storage) {
+  static setAttributes(html, element, storage, intent) {
     const {
       attributes
     } = element;
 
+    if (attributes.type === "radio") {
+      attributes.name = `${attributes.name}-${intent}`;
+    }
+
     for (const [key, value] of Object.entries(attributes)) {
-      if (value === null || value === undefined || key === "fieldId") {
+      if (value === null || value === undefined || key === "dataId") {
         continue;
       }
 
       if (key !== "style") {
         if (key === "textContent") {
           html.textContent = value;
+        } else if (key === "class") {
+          html.setAttribute(key, value.join(" "));
         } else {
           html.setAttribute(key, value);
         }
@@ -117,14 +129,15 @@ class XfaLayer {
       }
     }
 
-    if (storage && attributes.fieldId !== undefined) {
-      this.setupStorage(html, attributes.fieldId, element, storage);
+    if (storage && attributes.dataId) {
+      this.setupStorage(html, attributes.dataId, element, storage);
     }
   }
 
   static render(parameters) {
     const storage = parameters.annotationStorage;
     const root = parameters.xfa;
+    const intent = parameters.intent || "display";
     const rootHtml = document.createElement(root.name);
 
     if (root.attributes) {
@@ -134,8 +147,8 @@ class XfaLayer {
     const stack = [[root, -1, rootHtml]];
     const rootDiv = parameters.div;
     rootDiv.appendChild(rootHtml);
-    const coeffs = parameters.viewport.transform.join(",");
-    rootDiv.style.transform = `matrix(${coeffs})`;
+    const transform = `matrix(${parameters.viewport.transform.join(",")})`;
+    rootDiv.style.transform = transform;
     rootDiv.setAttribute("class", "xfaLayer xfaFont");
 
     while (stack.length > 0) {
@@ -161,11 +174,18 @@ class XfaLayer {
         continue;
       }
 
-      const childHtml = document.createElement(name);
+      let childHtml;
+
+      if (child?.attributes?.xmlns) {
+        childHtml = document.createElementNS(child.attributes.xmlns, name);
+      } else {
+        childHtml = document.createElement(name);
+      }
+
       html.appendChild(childHtml);
 
       if (child.attributes) {
-        this.setAttributes(childHtml, child, storage);
+        this.setAttributes(childHtml, child, storage, intent);
       }
 
       if (child.children && child.children.length > 0) {
@@ -174,6 +194,10 @@ class XfaLayer {
         childHtml.appendChild(document.createTextNode(child.value));
       }
     }
+
+    for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) {
+      el.setAttribute("readOnly", true);
+    }
   }
 
   static update(parameters) {

+ 1 - 3
lib/examples/node/domstubs.js

@@ -117,9 +117,7 @@ DOMElement.prototype = {
     return null;
   },
   setAttribute: function DOMElement_setAttribute(name, value) {
-    value = value || "";
-    value = xmlEncode(value);
-    this.attributes[name] = value;
+    this.attributes[name] = value || "";
   },
   setAttributeNS: function DOMElement_setAttributeNS(NS, name, value) {
     this.setAttribute(name, value);

+ 21 - 23
lib/pdf.js

@@ -42,6 +42,12 @@ Object.defineProperty(exports, "getPdfFilenameFromUrl", {
     return _display_utils.getPdfFilenameFromUrl;
   }
 });
+Object.defineProperty(exports, "getXfaPageViewport", {
+  enumerable: true,
+  get: function () {
+    return _display_utils.getXfaPageViewport;
+  }
+});
 Object.defineProperty(exports, "isPdfFile", {
   enumerable: true,
   get: function () {
@@ -204,12 +210,6 @@ Object.defineProperty(exports, "AnnotationLayer", {
     return _annotation_layer.AnnotationLayer;
   }
 });
-Object.defineProperty(exports, "apiCompatibilityParams", {
-  enumerable: true,
-  get: function () {
-    return _api_compatibility.apiCompatibilityParams;
-  }
-});
 Object.defineProperty(exports, "GlobalWorkerOptions", {
   enumerable: true,
   get: function () {
@@ -243,40 +243,38 @@ var _util = require("./shared/util.js");
 
 var _annotation_layer = require("./display/annotation_layer.js");
 
-var _api_compatibility = require("./display/api_compatibility.js");
-
 var _worker_options = require("./display/worker_options.js");
 
+var _is_node = require("./shared/is_node.js");
+
 var _text_layer = require("./display/text_layer.js");
 
 var _svg = require("./display/svg.js");
 
 var _xfa_layer = require("./display/xfa_layer.js");
 
-const pdfjsVersion = '2.9.359';
-const pdfjsBuild = 'e667c8cbc';
+const pdfjsVersion = '2.10.377';
+const pdfjsBuild = '156762c48';
 {
-  const {
-    isNodeJS
-  } = require("./shared/is_node.js");
-
-  if (isNodeJS) {
-    const PDFNodeStream = require("./display/node_stream.js").PDFNodeStream;
+  if (_is_node.isNodeJS) {
+    const {
+      PDFNodeStream
+    } = require("./display/node_stream.js");
 
     (0, _api.setPDFNetworkStreamFactory)(params => {
       return new PDFNodeStream(params);
     });
   } else {
-    const PDFNetworkStream = require("./display/network.js").PDFNetworkStream;
-
-    let PDFFetchStream;
+    const {
+      PDFNetworkStream
+    } = require("./display/network.js");
 
-    if ((0, _display_utils.isFetchSupported)()) {
-      PDFFetchStream = require("./display/fetch_stream.js").PDFFetchStream;
-    }
+    const {
+      PDFFetchStream
+    } = require("./display/fetch_stream.js");
 
     (0, _api.setPDFNetworkStreamFactory)(params => {
-      if (PDFFetchStream && (0, _display_utils.isValidFetchUrl)(params.url)) {
+      if ((0, _display_utils.isValidFetchUrl)(params.url)) {
         return new PDFFetchStream(params);
       }
 

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 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");
 
-const pdfjsVersion = '2.9.359';
-const pdfjsBuild = 'e667c8cbc';
+const pdfjsVersion = '2.10.377';
+const pdfjsBuild = '156762c48';

+ 1 - 0
lib/shared/util.js

@@ -234,6 +234,7 @@ exports.StreamType = StreamType;
 const FontType = {
   UNKNOWN: "UNKNOWN",
   TYPE1: "TYPE1",
+  TYPE1STANDARD: "TYPE1STANDARD",
   TYPE1C: "TYPE1C",
   CIDFONTTYPE0: "CIDFONTTYPE0",
   CIDFONTTYPE0C: "CIDFONTTYPE0C",

+ 41 - 19
lib/test/unit/annotation_spec.js

@@ -27,12 +27,12 @@ var _util = require("../../shared/util.js");
 
 var _test_utils = require("./test_utils.js");
 
+var _api = require("../../display/api.js");
+
 var _primitives = require("../../core/primitives.js");
 
 var _parser = require("../../core/parser.js");
 
-var _api = require("../../display/api.js");
-
 var _evaluator = require("../../core/evaluator.js");
 
 var _stream = require("../../core/stream.js");
@@ -72,6 +72,10 @@ describe("annotation", function () {
 
   }
 
+  const fontDataReader = new _api.DefaultStandardFontDataFactory({
+    baseUrl: _test_utils.STANDARD_FONT_DATA_URL
+  });
+
   function HandlerMock() {
     this.inputs = [];
   }
@@ -82,6 +86,14 @@ describe("annotation", function () {
         name,
         data
       });
+    },
+
+    sendWithPromise(name, data) {
+      if (name !== "FetchStandardFontData") {
+        return Promise.reject(new Error(`Unsupported mock ${name}.`));
+      }
+
+      return fontDataReader.fetch(data);
     }
 
   };
@@ -108,7 +120,8 @@ describe("annotation", function () {
       pageIndex: 0,
       idFactory: (0, _test_utils.createIdFactory)(0),
       fontCache: new _primitives.RefSetCache(),
-      builtInCMapCache
+      builtInCMapCache,
+      standardFontDataCache: new Map()
     });
   });
   afterAll(function () {
@@ -1432,6 +1445,7 @@ describe("annotation", function () {
       const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
     });
     it("should render auto-sized text for printing", async function () {
@@ -1739,8 +1753,8 @@ describe("annotation", function () {
       buttonWidgetDict = null;
     });
     it("should handle checkboxes with export value", async function () {
-      buttonWidgetDict.set("V", _primitives.Name.get("1"));
-      buttonWidgetDict.set("DV", _primitives.Name.get("2"));
+      buttonWidgetDict.set("V", _primitives.Name.get("Checked"));
+      buttonWidgetDict.set("DV", _primitives.Name.get("Off"));
       const appearanceStatesDict = new _primitives.Dict();
       const normalAppearanceDict = new _primitives.Dict();
       normalAppearanceDict.set("Off", 0);
@@ -1759,14 +1773,14 @@ describe("annotation", function () {
       } = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       expect(data.annotationType).toEqual(_util.AnnotationType.WIDGET);
       expect(data.checkBox).toEqual(true);
-      expect(data.fieldValue).toEqual("1");
-      expect(data.defaultFieldValue).toEqual("2");
+      expect(data.fieldValue).toEqual("Checked");
+      expect(data.defaultFieldValue).toEqual("Off");
       expect(data.radioButton).toEqual(false);
       expect(data.exportValue).toEqual("Checked");
     });
     it("should handle checkboxes without export value", async function () {
-      buttonWidgetDict.set("V", _primitives.Name.get("1"));
-      buttonWidgetDict.set("DV", _primitives.Name.get("2"));
+      buttonWidgetDict.set("V", _primitives.Name.get("Checked"));
+      buttonWidgetDict.set("DV", _primitives.Name.get("Off"));
 
       const buttonWidgetRef = _primitives.Ref.get(124, 0);
 
@@ -1779,13 +1793,13 @@ describe("annotation", function () {
       } = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       expect(data.annotationType).toEqual(_util.AnnotationType.WIDGET);
       expect(data.checkBox).toEqual(true);
-      expect(data.fieldValue).toEqual("1");
-      expect(data.defaultFieldValue).toEqual("2");
+      expect(data.fieldValue).toEqual("Checked");
+      expect(data.defaultFieldValue).toEqual("Off");
       expect(data.radioButton).toEqual(false);
     });
     it("should handle checkboxes without /Off appearance", async function () {
-      buttonWidgetDict.set("V", _primitives.Name.get("1"));
-      buttonWidgetDict.set("DV", _primitives.Name.get("2"));
+      buttonWidgetDict.set("V", _primitives.Name.get("Checked"));
+      buttonWidgetDict.set("DV", _primitives.Name.get("Off"));
       const appearanceStatesDict = new _primitives.Dict();
       const normalAppearanceDict = new _primitives.Dict();
       normalAppearanceDict.set("Checked", 1);
@@ -1803,8 +1817,8 @@ describe("annotation", function () {
       } = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       expect(data.annotationType).toEqual(_util.AnnotationType.WIDGET);
       expect(data.checkBox).toEqual(true);
-      expect(data.fieldValue).toEqual("1");
-      expect(data.defaultFieldValue).toEqual("2");
+      expect(data.fieldValue).toEqual("Checked");
+      expect(data.defaultFieldValue).toEqual("Off");
       expect(data.radioButton).toEqual(false);
       expect(data.exportValue).toEqual("Checked");
     });
@@ -1832,18 +1846,19 @@ describe("annotation", function () {
         data: buttonWidgetDict
       }]);
       const task = new _worker.WorkerTask("test print");
-      partialEvaluator.options = {
+      const checkboxEvaluator = partialEvaluator.clone({
         ignoreErrors: true
-      };
+      });
       const annotation = await _annotation.AnnotationFactory.create(xref, buttonWidgetRef, pdfManagerMock, idFactoryMock);
       const annotationStorage = new Map();
       annotationStorage.set(annotation.data.id, {
         value: true
       });
-      const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
+      const operatorList = await annotation.getOperatorList(checkboxEvaluator, task, false, annotationStorage);
       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.argsArray[3][0][0].fontChar).toEqual("✔");
+      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[3][0][0].unicode).toEqual("4");
     });
     it("should render checkboxes for printing", async function () {
       const appearanceStatesDict = new _primitives.Dict();
@@ -1877,6 +1892,7 @@ describe("annotation", function () {
       let operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       annotationStorage.set(annotation.data.id, {
         value: false
@@ -1884,6 +1900,7 @@ describe("annotation", function () {
       operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     it("should render checkboxes for printing twice", async function () {
@@ -1921,6 +1938,7 @@ describe("annotation", function () {
         const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
         expect(operatorList.argsArray.length).toEqual(3);
         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[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       }
     });
@@ -1954,6 +1972,7 @@ describe("annotation", function () {
       const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
     });
     it("should save checkboxes", async function () {
@@ -2096,6 +2115,7 @@ describe("annotation", function () {
       let operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([26, 51, 76]));
       annotationStorage.set(annotation.data.id, {
         value: false
@@ -2103,6 +2123,7 @@ describe("annotation", function () {
       operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     it("should render radio buttons for printing using normal appearance", async function () {
@@ -2136,6 +2157,7 @@ describe("annotation", function () {
       const operatorList = await annotation.getOperatorList(partialEvaluator, task, false, annotationStorage);
       expect(operatorList.argsArray.length).toEqual(3);
       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[1]).toEqual(new Uint8ClampedArray([76, 51, 26]));
     });
     it("should save radio buttons", async function () {

+ 32 - 13
lib/test/unit/api_spec.js

@@ -386,8 +386,14 @@ describe("api", function () {
     it("gets number of pages", function () {
       expect(pdfDocument.numPages).toEqual(3);
     });
-    it("gets fingerprint", function () {
-      expect(pdfDocument.fingerprint).toEqual("ea8b35919d6279a369e835bde778611b");
+    it("gets fingerprints", function () {
+      expect(pdfDocument.fingerprints).toEqual(["ea8b35919d6279a369e835bde778611b", null]);
+    });
+    it("gets fingerprints, from modified document", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("annotation-tx.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      expect(pdfDoc.fingerprints).toEqual(["3ebd77c320274649a68f10dbf3b9f882", "e7087346aa4b4ae0911c1f1643b57345"]);
+      await loadingTask.destroy();
     });
     it("gets page", async function () {
       const data = await pdfDocument.getPage(1);
@@ -898,11 +904,11 @@ describe("api", function () {
       const loadingTask1 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue4436r.pdf"));
       const loadingTask2 = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("issue4575.pdf"));
       const data = await Promise.all([loadingTask1.promise, loadingTask2.promise]);
-      const fingerprint1 = data[0].fingerprint;
-      const fingerprint2 = data[1].fingerprint;
-      expect(fingerprint1).not.toEqual(fingerprint2);
-      expect(fingerprint1).toEqual("2f695a83d6e7553c24fc08b7ac69712d");
-      expect(fingerprint2).toEqual("04c7126b34a46b6d4d6e7a1eff7edcb6");
+      const fingerprints1 = data[0].fingerprints;
+      const fingerprints2 = data[1].fingerprints;
+      expect(fingerprints1).not.toEqual(fingerprints2);
+      expect(fingerprints1).toEqual(["2f695a83d6e7553c24fc08b7ac69712d", null]);
+      expect(fingerprints2).toEqual(["04c7126b34a46b6d4d6e7a1eff7edcb6", null]);
       await Promise.all([loadingTask1.destroy(), loadingTask2.destroy()]);
     });
     describe("Cross-origin", function () {
@@ -1147,17 +1153,18 @@ describe("api", function () {
         styles
       } = await pdfPage.getTextContent();
       expect(items.length).toEqual(1);
-      expect(Object.keys(styles)).toEqual(["Times"]);
+      const fontName = items[0].fontName;
+      expect(Object.keys(styles)).toEqual([fontName]);
       expect(items[0]).toEqual({
         dir: "ltr",
-        fontName: "Times",
+        fontName,
         height: 18,
         str: "Issue 8276",
         transform: [18, 0, 0, 18, 441.81, 708.4499999999999],
         width: 77.49,
         hasEOL: false
       });
-      expect(styles.Times).toEqual({
+      expect(styles[fontName]).toEqual({
         fontFamily: "serif",
         ascent: NaN,
         descent: NaN,
@@ -1221,8 +1228,8 @@ describe("api", function () {
     });
     it("gets operator list", async function () {
       const operatorList = await page.getOperatorList();
-      expect(!!operatorList.fnArray).toEqual(true);
-      expect(!!operatorList.argsArray).toEqual(true);
+      expect(operatorList.fnArray.length).toBeGreaterThan(100);
+      expect(operatorList.argsArray.length).toBeGreaterThan(100);
       expect(operatorList.lastChunk).toEqual(true);
     });
     it("gets operatorList with JPEG image (issue 4888)", async function () {
@@ -1268,6 +1275,18 @@ describe("api", function () {
       });
       await Promise.all([result1, result2]);
     });
+    it("gets operator list, containing Annotation-operatorLists", async function () {
+      const loadingTask = (0, _api.getDocument)((0, _test_utils.buildGetDocumentParams)("annotation-line.pdf"));
+      const pdfDoc = await loadingTask.promise;
+      const pdfPage = await pdfDoc.getPage(1);
+      const operatorList = await pdfPage.getOperatorList();
+      expect(operatorList.fnArray.length).toBeGreaterThan(20);
+      expect(operatorList.argsArray.length).toBeGreaterThan(20);
+      expect(operatorList.lastChunk).toEqual(true);
+      expect(operatorList.fnArray.includes(_util.OPS.beginAnnotation)).toEqual(true);
+      expect(operatorList.fnArray.includes(_util.OPS.endAnnotation)).toEqual(true);
+      await loadingTask.destroy();
+    });
     it("gets document stats after parsing page", async function () {
       const stats = await page.getOperatorList().then(function () {
         return pdfDocument.getStats();
@@ -1275,7 +1294,7 @@ describe("api", function () {
       const expectedStreamTypes = {};
       expectedStreamTypes[_util.StreamType.FLATE] = true;
       const expectedFontTypes = {};
-      expectedFontTypes[_util.FontType.TYPE1] = true;
+      expectedFontTypes[_util.FontType.TYPE1STANDARD] = true;
       expectedFontTypes[_util.FontType.CIDFONTTYPE2] = true;
       expect(stats).toEqual({
         streamTypes: expectedStreamTypes,

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

@@ -245,7 +245,7 @@ describe("evaluator", function () {
         expect(false).toEqual(true);
       } catch (reason) {
         expect(reason instanceof _util.FormatError).toEqual(true);
-        expect(reason.message).toEqual("XObject must be referred to by name.");
+        expect(reason.message).toEqual("XObject should be a stream");
       }
     });
     it("should skip paintXObject if subtype is PS", async function () {

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

@@ -34,7 +34,7 @@ var _api = require("pdfjs/display/api.js");
 var _testreporter = require("./testreporter.js");
 
 async function initializePDFJS(callback) {
-  await Promise.all(["pdfjs-test/unit/annotation_spec.js", "pdfjs-test/unit/annotation_storage_spec.js", "pdfjs-test/unit/api_spec.js", "pdfjs-test/unit/bidi_spec.js", "pdfjs-test/unit/cff_parser_spec.js", "pdfjs-test/unit/cmap_spec.js", "pdfjs-test/unit/colorspace_spec.js", "pdfjs-test/unit/core_utils_spec.js", "pdfjs-test/unit/crypto_spec.js", "pdfjs-test/unit/custom_spec.js", "pdfjs-test/unit/default_appearance_spec.js", "pdfjs-test/unit/display_svg_spec.js", "pdfjs-test/unit/display_utils_spec.js", "pdfjs-test/unit/document_spec.js", "pdfjs-test/unit/encodings_spec.js", "pdfjs-test/unit/evaluator_spec.js", "pdfjs-test/unit/function_spec.js", "pdfjs-test/unit/fetch_stream_spec.js", "pdfjs-test/unit/message_handler_spec.js", "pdfjs-test/unit/metadata_spec.js", "pdfjs-test/unit/murmurhash3_spec.js", "pdfjs-test/unit/network_spec.js", "pdfjs-test/unit/network_utils_spec.js", "pdfjs-test/unit/parser_spec.js", "pdfjs-test/unit/pdf_find_controller_spec.js", "pdfjs-test/unit/pdf_find_utils_spec.js", "pdfjs-test/unit/pdf_history_spec.js", "pdfjs-test/unit/primitives_spec.js", "pdfjs-test/unit/scripting_spec.js", "pdfjs-test/unit/stream_spec.js", "pdfjs-test/unit/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_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/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) {
     return import(moduleName);
   }));
 

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