Merge pull request #11912 from Snuffleupagus/GlobalImageCache
Attempt to cache repeated images at the document, rather than the page, level (issue 11878)
This commit is contained in:
		
						commit
						4a3a24b002
					
				@ -74,6 +74,7 @@ class Page {
 | 
				
			|||||||
    ref,
 | 
					    ref,
 | 
				
			||||||
    fontCache,
 | 
					    fontCache,
 | 
				
			||||||
    builtInCMapCache,
 | 
					    builtInCMapCache,
 | 
				
			||||||
 | 
					    globalImageCache,
 | 
				
			||||||
    pdfFunctionFactory,
 | 
					    pdfFunctionFactory,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
    this.pdfManager = pdfManager;
 | 
					    this.pdfManager = pdfManager;
 | 
				
			||||||
@ -83,6 +84,7 @@ class Page {
 | 
				
			|||||||
    this.ref = ref;
 | 
					    this.ref = ref;
 | 
				
			||||||
    this.fontCache = fontCache;
 | 
					    this.fontCache = fontCache;
 | 
				
			||||||
    this.builtInCMapCache = builtInCMapCache;
 | 
					    this.builtInCMapCache = builtInCMapCache;
 | 
				
			||||||
 | 
					    this.globalImageCache = globalImageCache;
 | 
				
			||||||
    this.pdfFunctionFactory = pdfFunctionFactory;
 | 
					    this.pdfFunctionFactory = pdfFunctionFactory;
 | 
				
			||||||
    this.evaluatorOptions = pdfManager.evaluatorOptions;
 | 
					    this.evaluatorOptions = pdfManager.evaluatorOptions;
 | 
				
			||||||
    this.resourcesPromise = null;
 | 
					    this.resourcesPromise = null;
 | 
				
			||||||
@ -261,6 +263,7 @@ class Page {
 | 
				
			|||||||
      idFactory: this.idFactory,
 | 
					      idFactory: this.idFactory,
 | 
				
			||||||
      fontCache: this.fontCache,
 | 
					      fontCache: this.fontCache,
 | 
				
			||||||
      builtInCMapCache: this.builtInCMapCache,
 | 
					      builtInCMapCache: this.builtInCMapCache,
 | 
				
			||||||
 | 
					      globalImageCache: this.globalImageCache,
 | 
				
			||||||
      options: this.evaluatorOptions,
 | 
					      options: this.evaluatorOptions,
 | 
				
			||||||
      pdfFunctionFactory: this.pdfFunctionFactory,
 | 
					      pdfFunctionFactory: this.pdfFunctionFactory,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -354,6 +357,7 @@ class Page {
 | 
				
			|||||||
        idFactory: this.idFactory,
 | 
					        idFactory: this.idFactory,
 | 
				
			||||||
        fontCache: this.fontCache,
 | 
					        fontCache: this.fontCache,
 | 
				
			||||||
        builtInCMapCache: this.builtInCMapCache,
 | 
					        builtInCMapCache: this.builtInCMapCache,
 | 
				
			||||||
 | 
					        globalImageCache: this.globalImageCache,
 | 
				
			||||||
        options: this.evaluatorOptions,
 | 
					        options: this.evaluatorOptions,
 | 
				
			||||||
        pdfFunctionFactory: this.pdfFunctionFactory,
 | 
					        pdfFunctionFactory: this.pdfFunctionFactory,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@ -816,6 +820,7 @@ class PDFDocument {
 | 
				
			|||||||
        ref,
 | 
					        ref,
 | 
				
			||||||
        fontCache: catalog.fontCache,
 | 
					        fontCache: catalog.fontCache,
 | 
				
			||||||
        builtInCMapCache: catalog.builtInCMapCache,
 | 
					        builtInCMapCache: catalog.builtInCMapCache,
 | 
				
			||||||
 | 
					        globalImageCache: catalog.globalImageCache,
 | 
				
			||||||
        pdfFunctionFactory: this.pdfFunctionFactory,
 | 
					        pdfFunctionFactory: this.pdfFunctionFactory,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
 | 
				
			|||||||
@ -105,6 +105,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
    idFactory,
 | 
					    idFactory,
 | 
				
			||||||
    fontCache,
 | 
					    fontCache,
 | 
				
			||||||
    builtInCMapCache,
 | 
					    builtInCMapCache,
 | 
				
			||||||
 | 
					    globalImageCache,
 | 
				
			||||||
    options = null,
 | 
					    options = null,
 | 
				
			||||||
    pdfFunctionFactory,
 | 
					    pdfFunctionFactory,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
@ -114,6 +115,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
    this.idFactory = idFactory;
 | 
					    this.idFactory = idFactory;
 | 
				
			||||||
    this.fontCache = fontCache;
 | 
					    this.fontCache = fontCache;
 | 
				
			||||||
    this.builtInCMapCache = builtInCMapCache;
 | 
					    this.builtInCMapCache = builtInCMapCache;
 | 
				
			||||||
 | 
					    this.globalImageCache = globalImageCache;
 | 
				
			||||||
    this.options = options || DefaultPartialEvaluatorOptions;
 | 
					    this.options = options || DefaultPartialEvaluatorOptions;
 | 
				
			||||||
    this.pdfFunctionFactory = pdfFunctionFactory;
 | 
					    this.pdfFunctionFactory = pdfFunctionFactory;
 | 
				
			||||||
    this.parsingType3Font = false;
 | 
					    this.parsingType3Font = false;
 | 
				
			||||||
@ -451,6 +453,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
      forceDisableNativeImageDecoder = false,
 | 
					      forceDisableNativeImageDecoder = false,
 | 
				
			||||||
    }) {
 | 
					    }) {
 | 
				
			||||||
      var dict = image.dict;
 | 
					      var dict = image.dict;
 | 
				
			||||||
 | 
					      const imageRef = dict.objId;
 | 
				
			||||||
      var w = dict.get("Width", "W");
 | 
					      var w = dict.get("Width", "W");
 | 
				
			||||||
      var h = dict.get("Height", "H");
 | 
					      var h = dict.get("Height", "H");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -528,12 +531,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
        return undefined;
 | 
					        return undefined;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const nativeImageDecoderSupport = forceDisableNativeImageDecoder
 | 
					      let nativeImageDecoderSupport = forceDisableNativeImageDecoder
 | 
				
			||||||
        ? NativeImageDecoding.NONE
 | 
					        ? NativeImageDecoding.NONE
 | 
				
			||||||
        : this.options.nativeImageDecoderSupport;
 | 
					        : this.options.nativeImageDecoderSupport;
 | 
				
			||||||
      // If there is no imageMask, create the PDFImage and a lot
 | 
					      // If there is no imageMask, create the PDFImage and a lot
 | 
				
			||||||
      // of image processing can be done here.
 | 
					      // of image processing can be done here.
 | 
				
			||||||
      let objId = `img_${this.idFactory.createObjId()}`;
 | 
					      let objId = `img_${this.idFactory.createObjId()}`,
 | 
				
			||||||
 | 
					        cacheGlobally = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.parsingType3Font) {
 | 
					      if (this.parsingType3Font) {
 | 
				
			||||||
        assert(
 | 
					        assert(
 | 
				
			||||||
@ -542,6 +546,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        objId = `${this.idFactory.getDocId()}_type3res_${objId}`;
 | 
					        objId = `${this.idFactory.getDocId()}_type3res_${objId}`;
 | 
				
			||||||
 | 
					      } else if (imageRef) {
 | 
				
			||||||
 | 
					        cacheGlobally = this.globalImageCache.shouldCache(
 | 
				
			||||||
 | 
					          imageRef,
 | 
				
			||||||
 | 
					          this.pageIndex
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cacheGlobally) {
 | 
				
			||||||
 | 
					          // Ensure that the image is *completely* decoded on the worker-thread,
 | 
				
			||||||
 | 
					          // in order to simplify the caching/rendering code on the main-thread.
 | 
				
			||||||
 | 
					          nativeImageDecoderSupport = NativeImageDecoding.NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          objId = `${this.idFactory.getDocId()}_${objId}`;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
@ -566,7 +583,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
            image.getIR(this.options.forceDataSchema),
 | 
					            image.getIR(this.options.forceDataSchema),
 | 
				
			||||||
          ])
 | 
					          ])
 | 
				
			||||||
          .then(
 | 
					          .then(
 | 
				
			||||||
            function () {
 | 
					            () => {
 | 
				
			||||||
              // Only add the dependency once we know that the native JPEG
 | 
					              // Only add the dependency once we know that the native JPEG
 | 
				
			||||||
              // decoding succeeded, to ensure that rendering will always
 | 
					              // decoding succeeded, to ensure that rendering will always
 | 
				
			||||||
              // complete.
 | 
					              // complete.
 | 
				
			||||||
@ -579,6 +596,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
                  fn: OPS.paintJpegXObject,
 | 
					                  fn: OPS.paintJpegXObject,
 | 
				
			||||||
                  args,
 | 
					                  args,
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (imageRef) {
 | 
				
			||||||
 | 
					                  this.globalImageCache.addPageIndex(imageRef, this.pageIndex);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            reason => {
 | 
					            reason => {
 | 
				
			||||||
@ -639,6 +660,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
              [objId, "FontType3Res", imgData],
 | 
					              [objId, "FontType3Res", imgData],
 | 
				
			||||||
              [imgData.data.buffer]
 | 
					              [imgData.data.buffer]
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					          } else if (cacheGlobally) {
 | 
				
			||||||
 | 
					            this.handler.send(
 | 
				
			||||||
 | 
					              "commonobj",
 | 
				
			||||||
 | 
					              [objId, "Image", imgData],
 | 
				
			||||||
 | 
					              [imgData.data.buffer]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.handler.send(
 | 
					          this.handler.send(
 | 
				
			||||||
            "obj",
 | 
					            "obj",
 | 
				
			||||||
@ -656,6 +684,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
              "FontType3Res",
 | 
					              "FontType3Res",
 | 
				
			||||||
              null,
 | 
					              null,
 | 
				
			||||||
            ]);
 | 
					            ]);
 | 
				
			||||||
 | 
					          } else if (cacheGlobally) {
 | 
				
			||||||
 | 
					            this.handler.send("commonobj", [objId, "Image", null]);
 | 
				
			||||||
 | 
					            return undefined;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.handler.send("obj", [objId, this.pageIndex, "Image", null]);
 | 
					          this.handler.send("obj", [objId, this.pageIndex, "Image", null]);
 | 
				
			||||||
          return undefined;
 | 
					          return undefined;
 | 
				
			||||||
@ -674,6 +705,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
          fn: OPS.paintImageXObject,
 | 
					          fn: OPS.paintImageXObject,
 | 
				
			||||||
          args,
 | 
					          args,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (imageRef) {
 | 
				
			||||||
 | 
					          this.globalImageCache.addPageIndex(imageRef, this.pageIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (cacheGlobally) {
 | 
				
			||||||
 | 
					            this.globalImageCache.setData(imageRef, {
 | 
				
			||||||
 | 
					              objId,
 | 
				
			||||||
 | 
					              fn: OPS.paintImageXObject,
 | 
				
			||||||
 | 
					              args,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return undefined;
 | 
					      return undefined;
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -1322,7 +1365,23 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 | 
				
			|||||||
                    );
 | 
					                    );
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  const xobj = xobjs.get(name);
 | 
					                  let xobj = xobjs.getRaw(name);
 | 
				
			||||||
 | 
					                  if (xobj instanceof Ref) {
 | 
				
			||||||
 | 
					                    const globalImage = self.globalImageCache.getData(
 | 
				
			||||||
 | 
					                      xobj,
 | 
				
			||||||
 | 
					                      self.pageIndex
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (globalImage) {
 | 
				
			||||||
 | 
					                      operatorList.addDependency(globalImage.objId);
 | 
				
			||||||
 | 
					                      operatorList.addOp(globalImage.fn, globalImage.args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                      resolveXObject();
 | 
				
			||||||
 | 
					                      return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    xobj = xref.fetch(xobj);
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                  if (!xobj) {
 | 
					                  if (!xobj) {
 | 
				
			||||||
                    operatorList.addOp(fn, args);
 | 
					                    operatorList.addOp(fn, args);
 | 
				
			||||||
                    resolveXObject();
 | 
					                    resolveXObject();
 | 
				
			||||||
 | 
				
			|||||||
@ -14,8 +14,10 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
/* eslint no-var: error */
 | 
					/* eslint no-var: error */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { assert, info, shadow } from "../shared/util.js";
 | 
				
			||||||
import { ColorSpace } from "./colorspace.js";
 | 
					import { ColorSpace } from "./colorspace.js";
 | 
				
			||||||
import { JpegStream } from "./jpeg_stream.js";
 | 
					import { JpegStream } from "./jpeg_stream.js";
 | 
				
			||||||
 | 
					import { RefSetCache } from "./primitives.js";
 | 
				
			||||||
import { Stream } from "./stream.js";
 | 
					import { Stream } from "./stream.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NativeImageDecoder {
 | 
					class NativeImageDecoder {
 | 
				
			||||||
@ -111,4 +113,96 @@ class NativeImageDecoder {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { NativeImageDecoder };
 | 
					class GlobalImageCache {
 | 
				
			||||||
 | 
					  static get NUM_PAGES_THRESHOLD() {
 | 
				
			||||||
 | 
					    return shadow(this, "NUM_PAGES_THRESHOLD", 2);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static get MAX_IMAGES_TO_CACHE() {
 | 
				
			||||||
 | 
					    return shadow(this, "MAX_IMAGES_TO_CACHE", 10);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      typeof PDFJSDev === "undefined" ||
 | 
				
			||||||
 | 
					      PDFJSDev.test("!PRODUCTION || TESTING")
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      assert(
 | 
				
			||||||
 | 
					        GlobalImageCache.NUM_PAGES_THRESHOLD > 1,
 | 
				
			||||||
 | 
					        "GlobalImageCache - invalid NUM_PAGES_THRESHOLD constant."
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this._refCache = new RefSetCache();
 | 
				
			||||||
 | 
					    this._imageCache = new RefSetCache();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  shouldCache(ref, pageIndex) {
 | 
				
			||||||
 | 
					    const pageIndexSet = this._refCache.get(ref);
 | 
				
			||||||
 | 
					    const numPages = pageIndexSet
 | 
				
			||||||
 | 
					      ? pageIndexSet.size + (pageIndexSet.has(pageIndex) ? 0 : 1)
 | 
				
			||||||
 | 
					      : 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (numPages < GlobalImageCache.NUM_PAGES_THRESHOLD) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      !this._imageCache.has(ref) &&
 | 
				
			||||||
 | 
					      this._imageCache.size >= GlobalImageCache.MAX_IMAGES_TO_CACHE
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addPageIndex(ref, pageIndex) {
 | 
				
			||||||
 | 
					    let pageIndexSet = this._refCache.get(ref);
 | 
				
			||||||
 | 
					    if (!pageIndexSet) {
 | 
				
			||||||
 | 
					      pageIndexSet = new Set();
 | 
				
			||||||
 | 
					      this._refCache.put(ref, pageIndexSet);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pageIndexSet.add(pageIndex);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getData(ref, pageIndex) {
 | 
				
			||||||
 | 
					    if (!this._refCache.has(ref)) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const pageIndexSet = this._refCache.get(ref);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pageIndexSet.size < GlobalImageCache.NUM_PAGES_THRESHOLD) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!this._imageCache.has(ref)) {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Ensure that we keep track of all pages containing the image reference.
 | 
				
			||||||
 | 
					    pageIndexSet.add(pageIndex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return this._imageCache.get(ref);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setData(ref, data) {
 | 
				
			||||||
 | 
					    if (!this._refCache.has(ref)) {
 | 
				
			||||||
 | 
					      throw new Error(
 | 
				
			||||||
 | 
					        'GlobalImageCache.setData - expected "addPageIndex" to have been called.'
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this._imageCache.has(ref)) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this._imageCache.size >= GlobalImageCache.MAX_IMAGES_TO_CACHE) {
 | 
				
			||||||
 | 
					      info(
 | 
				
			||||||
 | 
					        "GlobalImageCache.setData - ignoring image above MAX_IMAGES_TO_CACHE."
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this._imageCache.put(ref, data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clear() {
 | 
				
			||||||
 | 
					    this._refCache.clear();
 | 
				
			||||||
 | 
					    this._imageCache.clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { NativeImageDecoder, GlobalImageCache };
 | 
				
			||||||
 | 
				
			|||||||
@ -54,6 +54,7 @@ import {
 | 
				
			|||||||
} from "./core_utils.js";
 | 
					} from "./core_utils.js";
 | 
				
			||||||
import { CipherTransformFactory } from "./crypto.js";
 | 
					import { CipherTransformFactory } from "./crypto.js";
 | 
				
			||||||
import { ColorSpace } from "./colorspace.js";
 | 
					import { ColorSpace } from "./colorspace.js";
 | 
				
			||||||
 | 
					import { GlobalImageCache } from "./image_utils.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fetchDestination(dest) {
 | 
					function fetchDestination(dest) {
 | 
				
			||||||
  return isDict(dest) ? dest.get("D") : dest;
 | 
					  return isDict(dest) ? dest.get("D") : dest;
 | 
				
			||||||
@ -71,6 +72,7 @@ class Catalog {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    this.fontCache = new RefSetCache();
 | 
					    this.fontCache = new RefSetCache();
 | 
				
			||||||
    this.builtInCMapCache = new Map();
 | 
					    this.builtInCMapCache = new Map();
 | 
				
			||||||
 | 
					    this.globalImageCache = new GlobalImageCache();
 | 
				
			||||||
    this.pageKidsCountCache = new RefSetCache();
 | 
					    this.pageKidsCountCache = new RefSetCache();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -716,6 +718,7 @@ class Catalog {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  cleanup() {
 | 
					  cleanup() {
 | 
				
			||||||
    clearPrimitiveCaches();
 | 
					    clearPrimitiveCaches();
 | 
				
			||||||
 | 
					    this.globalImageCache.clear();
 | 
				
			||||||
    this.pageKidsCountCache.clear();
 | 
					    this.pageKidsCountCache.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const promises = [];
 | 
					    const promises = [];
 | 
				
			||||||
 | 
				
			|||||||
@ -251,6 +251,10 @@ var RefSetCache = (function RefSetCacheClosure() {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RefSetCache.prototype = {
 | 
					  RefSetCache.prototype = {
 | 
				
			||||||
 | 
					    get size() {
 | 
				
			||||||
 | 
					      return Object.keys(this.dict).length;
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    get: function RefSetCache_get(ref) {
 | 
					    get: function RefSetCache_get(ref) {
 | 
				
			||||||
      return this.dict[ref.toString()];
 | 
					      return this.dict[ref.toString()];
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
				
			|||||||
@ -2288,6 +2288,7 @@ class WorkerTransport {
 | 
				
			|||||||
          break;
 | 
					          break;
 | 
				
			||||||
        case "FontPath":
 | 
					        case "FontPath":
 | 
				
			||||||
        case "FontType3Res":
 | 
					        case "FontType3Res":
 | 
				
			||||||
 | 
					        case "Image":
 | 
				
			||||||
          this.commonObjs.resolve(id, exportedData);
 | 
					          this.commonObjs.resolve(id, exportedData);
 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
 | 
				
			|||||||
@ -2114,7 +2114,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
 | 
					    paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
 | 
				
			||||||
      const domImage = this.processingType3
 | 
					      const domImage = objId.startsWith("g_")
 | 
				
			||||||
        ? this.commonObjs.get(objId)
 | 
					        ? this.commonObjs.get(objId)
 | 
				
			||||||
        : this.objs.get(objId);
 | 
					        : this.objs.get(objId);
 | 
				
			||||||
      if (!domImage) {
 | 
					      if (!domImage) {
 | 
				
			||||||
@ -2277,7 +2277,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
 | 
					    paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
 | 
				
			||||||
      const imgData = this.processingType3
 | 
					      const imgData = objId.startsWith("g_")
 | 
				
			||||||
        ? this.commonObjs.get(objId)
 | 
					        ? this.commonObjs.get(objId)
 | 
				
			||||||
        : this.objs.get(objId);
 | 
					        : this.objs.get(objId);
 | 
				
			||||||
      if (!imgData) {
 | 
					      if (!imgData) {
 | 
				
			||||||
@ -2294,7 +2294,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
 | 
				
			|||||||
      scaleY,
 | 
					      scaleY,
 | 
				
			||||||
      positions
 | 
					      positions
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
      const imgData = this.processingType3
 | 
					      const imgData = objId.startsWith("g_")
 | 
				
			||||||
        ? this.commonObjs.get(objId)
 | 
					        ? this.commonObjs.get(objId)
 | 
				
			||||||
        : this.objs.get(objId);
 | 
					        : this.objs.get(objId);
 | 
				
			||||||
      if (!imgData) {
 | 
					      if (!imgData) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							@ -90,6 +90,7 @@
 | 
				
			|||||||
!issue11362.pdf
 | 
					!issue11362.pdf
 | 
				
			||||||
!issue11578_reduced.pdf
 | 
					!issue11578_reduced.pdf
 | 
				
			||||||
!issue11651.pdf
 | 
					!issue11651.pdf
 | 
				
			||||||
 | 
					!issue11878.pdf
 | 
				
			||||||
!bad-PageLabels.pdf
 | 
					!bad-PageLabels.pdf
 | 
				
			||||||
!decodeACSuccessive.pdf
 | 
					!decodeACSuccessive.pdf
 | 
				
			||||||
!filled-background.pdf
 | 
					!filled-background.pdf
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/issue11878.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/issue11878.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -23,6 +23,7 @@ import {
 | 
				
			|||||||
import {
 | 
					import {
 | 
				
			||||||
  createPromiseCapability,
 | 
					  createPromiseCapability,
 | 
				
			||||||
  FontType,
 | 
					  FontType,
 | 
				
			||||||
 | 
					  ImageKind,
 | 
				
			||||||
  InvalidPDFException,
 | 
					  InvalidPDFException,
 | 
				
			||||||
  MissingPDFException,
 | 
					  MissingPDFException,
 | 
				
			||||||
  OPS,
 | 
					  OPS,
 | 
				
			||||||
@ -44,6 +45,7 @@ import {
 | 
				
			|||||||
  PDFWorker,
 | 
					  PDFWorker,
 | 
				
			||||||
} from "../../src/display/api.js";
 | 
					} from "../../src/display/api.js";
 | 
				
			||||||
import { AutoPrintRegExp } from "../../web/ui_utils.js";
 | 
					import { AutoPrintRegExp } from "../../web/ui_utils.js";
 | 
				
			||||||
 | 
					import { GlobalImageCache } from "../../src/core/image_utils.js";
 | 
				
			||||||
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
 | 
					import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
 | 
				
			||||||
import { isNodeJS } from "../../src/shared/is_node.js";
 | 
					import { isNodeJS } from "../../src/shared/is_node.js";
 | 
				
			||||||
import { Metadata } from "../../src/display/metadata.js";
 | 
					import { Metadata } from "../../src/display/metadata.js";
 | 
				
			||||||
@ -1928,6 +1930,80 @@ describe("api", function () {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
        .catch(done.fail);
 | 
					        .catch(done.fail);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it("caches image resources at the document/page level as expected (issue 11878)", async function (done) {
 | 
				
			||||||
 | 
					      const { NUM_PAGES_THRESHOLD } = GlobalImageCache,
 | 
				
			||||||
 | 
					        EXPECTED_WIDTH = 2550,
 | 
				
			||||||
 | 
					        EXPECTED_HEIGHT = 3300;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const loadingTask = getDocument(buildGetDocumentParams("issue11878.pdf"));
 | 
				
			||||||
 | 
					      let firstImgData = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const pdfDoc = await loadingTask.promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = 1; i <= pdfDoc.numPages; i++) {
 | 
				
			||||||
 | 
					          const pdfPage = await pdfDoc.getPage(i);
 | 
				
			||||||
 | 
					          const opList = await pdfPage.getOperatorList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const { commonObjs, objs } = pdfPage;
 | 
				
			||||||
 | 
					          const imgIndex = opList.fnArray.indexOf(OPS.paintImageXObject);
 | 
				
			||||||
 | 
					          const [objId, width, height] = opList.argsArray[imgIndex];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (i < NUM_PAGES_THRESHOLD) {
 | 
				
			||||||
 | 
					            expect(objId).toEqual(`img_p${i - 1}_1`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(objs.has(objId)).toEqual(true);
 | 
				
			||||||
 | 
					            expect(commonObjs.has(objId)).toEqual(false);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            expect(objId).toEqual(
 | 
				
			||||||
 | 
					              `g_${loadingTask.docId}_img_p${NUM_PAGES_THRESHOLD - 1}_1`
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(objs.has(objId)).toEqual(false);
 | 
				
			||||||
 | 
					            expect(commonObjs.has(objId)).toEqual(true);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          expect(width).toEqual(EXPECTED_WIDTH);
 | 
				
			||||||
 | 
					          expect(height).toEqual(EXPECTED_HEIGHT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // Ensure that the actual image data is identical for all pages.
 | 
				
			||||||
 | 
					          if (i === 1) {
 | 
				
			||||||
 | 
					            firstImgData = objs.get(objId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(firstImgData.width).toEqual(EXPECTED_WIDTH);
 | 
				
			||||||
 | 
					            expect(firstImgData.height).toEqual(EXPECTED_HEIGHT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(firstImgData.kind).toEqual(ImageKind.RGB_24BPP);
 | 
				
			||||||
 | 
					            expect(firstImgData.data instanceof Uint8ClampedArray).toEqual(
 | 
				
			||||||
 | 
					              true
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            expect(firstImgData.data.length).toEqual(25245000);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            const objsPool = i >= NUM_PAGES_THRESHOLD ? commonObjs : objs;
 | 
				
			||||||
 | 
					            const currentImgData = objsPool.get(objId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(currentImgData.width).toEqual(firstImgData.width);
 | 
				
			||||||
 | 
					            expect(currentImgData.height).toEqual(firstImgData.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(currentImgData.kind).toEqual(firstImgData.kind);
 | 
				
			||||||
 | 
					            expect(currentImgData.data instanceof Uint8ClampedArray).toEqual(
 | 
				
			||||||
 | 
					              true
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            expect(
 | 
				
			||||||
 | 
					              currentImgData.data.every((value, index) => {
 | 
				
			||||||
 | 
					                return value === firstImgData.data[index];
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            ).toEqual(true);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await loadingTask.destroy();
 | 
				
			||||||
 | 
					        firstImgData = null;
 | 
				
			||||||
 | 
					        done();
 | 
				
			||||||
 | 
					      } catch (ex) {
 | 
				
			||||||
 | 
					        done.fail(ex);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
  describe("Multiple `getDocument` instances", function () {
 | 
					  describe("Multiple `getDocument` instances", function () {
 | 
				
			||||||
    // Regression test for https://github.com/mozilla/pdf.js/issues/6205
 | 
					    // Regression test for https://github.com/mozilla/pdf.js/issues/6205
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user