diff --git a/src/core/annotation.js b/src/core/annotation.js index 23d383e93..23c244029 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -83,14 +83,23 @@ class AnnotationFactory { // Only necessary to prevent the `Catalog.attachments`-getter, used // with "GoToE" actions, from throwing and thus breaking parsing: pdfManager.ensureCatalog("attachments"), + pdfManager.ensureCatalog("globalColorSpaceCache"), ]).then( - ([acroForm, xfaDatasets, structTreeRoot, baseUrl, attachments]) => ({ + ([ + acroForm, + xfaDatasets, + structTreeRoot, + baseUrl, + attachments, + globalColorSpaceCache, + ]) => ({ pdfManager, acroForm: acroForm instanceof Dict ? acroForm : Dict.empty, xfaDatasets, structTreeRoot, baseUrl, attachments, + globalColorSpaceCache, }), reason => { warn(`createGlobals: "${reason}".`); @@ -3880,7 +3889,7 @@ class FreeTextAnnotation extends MarkupAnnotation { // We want to be able to add mouse listeners to the annotation. this.data.noHTML = false; - const { evaluatorOptions, xref } = params; + const { annotationGlobals, evaluatorOptions, xref } = params; this.data.annotationType = AnnotationType.FREETEXT; this.setDefaultAppearance(params); this._hasAppearance = !!this.appearance; @@ -3889,7 +3898,8 @@ class FreeTextAnnotation extends MarkupAnnotation { const { fontColor, fontSize } = parseAppearanceStream( this.appearance, evaluatorOptions, - xref + xref, + annotationGlobals.globalColorSpaceCache ); this.data.defaultAppearanceData.fontColor = fontColor; this.data.defaultAppearanceData.fontSize = fontSize || 10; diff --git a/src/core/catalog.js b/src/core/catalog.js index 84624b863..0fb4eb115 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -44,12 +44,12 @@ import { RefSet, RefSetCache, } from "./primitives.js"; +import { GlobalColorSpaceCache, GlobalImageCache } from "./image_utils.js"; import { NameTree, NumberTree } from "./name_number_tree.js"; import { BaseStream } from "./base_stream.js"; import { clearGlobalCaches } from "./cleanup_helper.js"; import { ColorSpace } from "./colorspace.js"; import { FileSpec } from "./file_spec.js"; -import { GlobalImageCache } from "./image_utils.js"; import { MetadataParser } from "./metadata_parser.js"; import { StructTreeRoot } from "./struct_tree.js"; @@ -140,6 +140,7 @@ class Catalog { this.fontCache = new RefSetCache(); this.builtInCMapCache = new Map(); this.standardFontDataCache = new Map(); + this.globalColorSpaceCache = new GlobalColorSpaceCache(); this.globalImageCache = new GlobalImageCache(); this.pageKidsCountCache = new RefSetCache(); this.pageIndexCache = new RefSetCache(); @@ -1171,6 +1172,7 @@ class Catalog { async cleanup(manuallyTriggered = false) { clearGlobalCaches(); + this.globalColorSpaceCache.clear(); this.globalImageCache.clear(/* onlyData = */ manuallyTriggered); this.pageKidsCountCache.clear(); this.pageIndexCache.clear(); diff --git a/src/core/colorspace.js b/src/core/colorspace.js index 9f5ce00b7..6bd879b61 100644 --- a/src/core/colorspace.js +++ b/src/core/colorspace.js @@ -306,19 +306,20 @@ class ColorSpace { return shadow(this, "usesZeroToOneRange", true); } - /** - * @private - */ - static _cache(cacheKey, xref, localColorSpaceCache, parsedColorSpace) { - if (!localColorSpaceCache) { + static #cache( + cacheKey, + xref, + globalColorSpaceCache, + localColorSpaceCache, + parsedCS + ) { + if (!globalColorSpaceCache || !localColorSpaceCache) { throw new Error( - 'ColorSpace._cache - expected "localColorSpaceCache" argument.' + 'ColorSpace.#cache - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.' ); } - if (!parsedColorSpace) { - throw new Error( - 'ColorSpace._cache - expected "parsedColorSpace" argument.' - ); + if (!parsedCS) { + throw new Error('ColorSpace.#cache - expected "parsedCS" argument.'); } let csName, csRef; if (cacheKey instanceof Ref) { @@ -331,20 +332,31 @@ class ColorSpace { csName = cacheKey.name; } if (csName || csRef) { - localColorSpaceCache.set(csName, csRef, parsedColorSpace); + localColorSpaceCache.set(csName, csRef, parsedCS); + + if (csRef) { + globalColorSpaceCache.set(/* name = */ null, csRef, parsedCS); + } } } - static getCached(cacheKey, xref, localColorSpaceCache) { - if (!localColorSpaceCache) { + static getCached( + cacheKey, + xref, + globalColorSpaceCache, + localColorSpaceCache + ) { + if (!globalColorSpaceCache || !localColorSpaceCache) { throw new Error( - 'ColorSpace.getCached - expected "localColorSpaceCache" argument.' + 'ColorSpace.getCached - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.' ); } if (cacheKey instanceof Ref) { - const localColorSpace = localColorSpaceCache.getByRef(cacheKey); - if (localColorSpace) { - return localColorSpace; + const cachedCS = + globalColorSpaceCache.getByRef(cacheKey) || + localColorSpaceCache.getByRef(cacheKey); + if (cachedCS) { + return cachedCS; } try { @@ -357,10 +369,7 @@ class ColorSpace { } } if (cacheKey instanceof Name) { - const localColorSpace = localColorSpaceCache.getByName(cacheKey.name); - if (localColorSpace) { - return localColorSpace; - } + return localColorSpaceCache.getByName(cacheKey.name) || null; } return null; } @@ -370,26 +379,28 @@ class ColorSpace { xref, resources = null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert( - !this.getCached(cs, xref, localColorSpaceCache), + !this.getCached(cs, xref, globalColorSpaceCache, localColorSpaceCache), "Expected `ColorSpace.getCached` to have been manually checked " + "before calling `ColorSpace.parseAsync`." ); } - const parsedColorSpace = this._parse( - cs, - xref, - resources, - pdfFunctionFactory - ); + const parsedCS = this.#parse(cs, xref, resources, pdfFunctionFactory); // Attempt to cache the parsed ColorSpace, by name and/or reference. - this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); + this.#cache( + cs, + xref, + globalColorSpaceCache, + localColorSpaceCache, + parsedCS + ); - return parsedColorSpace; + return parsedCS; } static parse({ @@ -397,29 +408,33 @@ class ColorSpace { xref, resources = null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { - const cachedColorSpace = this.getCached(cs, xref, localColorSpaceCache); - if (cachedColorSpace) { - return cachedColorSpace; - } - const parsedColorSpace = this._parse( + const cachedCS = this.getCached( cs, xref, - resources, - pdfFunctionFactory + globalColorSpaceCache, + localColorSpaceCache ); + if (cachedCS) { + return cachedCS; + } + const parsedCS = this.#parse(cs, xref, resources, pdfFunctionFactory); // Attempt to cache the parsed ColorSpace, by name and/or reference. - this._cache(cs, xref, localColorSpaceCache, parsedColorSpace); + this.#cache( + cs, + xref, + globalColorSpaceCache, + localColorSpaceCache, + parsedCS + ); - return parsedColorSpace; + return parsedCS; } - /** - * @private - */ - static _parse(cs, xref, resources = null, pdfFunctionFactory) { + static #parse(cs, xref, resources = null, pdfFunctionFactory) { cs = xref.fetchIfRef(cs); if (cs instanceof Name) { switch (cs.name) { @@ -443,7 +458,7 @@ class ColorSpace { const resourcesCS = colorSpaces.get(cs.name); if (resourcesCS) { if (resourcesCS instanceof Name) { - return this._parse( + return this.#parse( resourcesCS, xref, resources, @@ -493,7 +508,7 @@ class ColorSpace { numComps = dict.get("N"); const alt = dict.get("Alternate"); if (alt) { - const altCS = this._parse(alt, xref, resources, pdfFunctionFactory); + const altCS = this.#parse(alt, xref, resources, pdfFunctionFactory); // Ensure that the number of components are correct, // and also (indirectly) that it is not a PatternCS. if (altCS.numComps === numComps) { @@ -512,12 +527,12 @@ class ColorSpace { case "Pattern": baseCS = cs[1] || null; if (baseCS) { - baseCS = this._parse(baseCS, xref, resources, pdfFunctionFactory); + baseCS = this.#parse(baseCS, xref, resources, pdfFunctionFactory); } return new PatternCS(baseCS); case "I": case "Indexed": - baseCS = this._parse(cs[1], xref, resources, pdfFunctionFactory); + baseCS = this.#parse(cs[1], xref, resources, pdfFunctionFactory); const hiVal = Math.max(0, Math.min(xref.fetchIfRef(cs[2]), 255)); const lookup = xref.fetchIfRef(cs[3]); return new IndexedCS(baseCS, hiVal, lookup); @@ -525,7 +540,7 @@ class ColorSpace { case "DeviceN": const name = xref.fetchIfRef(cs[1]); numComps = Array.isArray(name) ? name.length : 1; - baseCS = this._parse(cs[2], xref, resources, pdfFunctionFactory); + baseCS = this.#parse(cs[2], xref, resources, pdfFunctionFactory); const tintFn = pdfFunctionFactory.create(cs[3]); return new AlternateCS(numComps, baseCS, tintFn); case "Lab": diff --git a/src/core/default_appearance.js b/src/core/default_appearance.js index 740a037c6..51d179daf 100644 --- a/src/core/default_appearance.js +++ b/src/core/default_appearance.js @@ -97,11 +97,12 @@ function parseDefaultAppearance(str) { } class AppearanceStreamEvaluator extends EvaluatorPreprocessor { - constructor(stream, evaluatorOptions, xref) { + constructor(stream, evaluatorOptions, xref, globalColorSpaceCache) { super(stream); this.stream = stream; this.evaluatorOptions = evaluatorOptions; this.xref = xref; + this.globalColorSpaceCache = globalColorSpaceCache; this.resources = stream.dict?.get("Resources"); } @@ -161,6 +162,7 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { xref: this.xref, resources: this.resources, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache: this._localColorSpaceCache, }); break; @@ -210,8 +212,18 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { // Parse appearance stream to extract font and color information. // It returns the font properties used to render the first text object. -function parseAppearanceStream(stream, evaluatorOptions, xref) { - return new AppearanceStreamEvaluator(stream, evaluatorOptions, xref).parse(); +function parseAppearanceStream( + stream, + evaluatorOptions, + xref, + globalColorSpaceCache +) { + return new AppearanceStreamEvaluator( + stream, + evaluatorOptions, + xref, + globalColorSpaceCache + ).parse(); } function getPdfColor(color, isFill) { diff --git a/src/core/document.js b/src/core/document.js index 8b98a51c6..755e07def 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -87,6 +87,7 @@ class Page { fontCache, builtInCMapCache, standardFontDataCache, + globalColorSpaceCache, globalImageCache, systemFontCache, nonBlendModesSet, @@ -100,6 +101,7 @@ class Page { this.fontCache = fontCache; this.builtInCMapCache = builtInCMapCache; this.standardFontDataCache = standardFontDataCache; + this.globalColorSpaceCache = globalColorSpaceCache; this.globalImageCache = globalImageCache; this.systemFontCache = systemFontCache; this.nonBlendModesSet = nonBlendModesSet; @@ -327,6 +329,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -381,6 +384,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -446,6 +450,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -670,6 +675,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -742,6 +748,7 @@ class Page { fontCache: this.fontCache, builtInCMapCache: this.builtInCMapCache, standardFontDataCache: this.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: this.globalImageCache, systemFontCache: this.systemFontCache, options: this.evaluatorOptions, @@ -1632,6 +1639,7 @@ class PDFDocument { fontCache: catalog.fontCache, builtInCMapCache: catalog.builtInCMapCache, standardFontDataCache: catalog.standardFontDataCache, + globalColorSpaceCache: catalog.globalColorSpaceCache, globalImageCache: catalog.globalImageCache, systemFontCache: catalog.systemFontCache, nonBlendModesSet: catalog.nonBlendModesSet, @@ -1731,6 +1739,7 @@ class PDFDocument { fontCache: catalog.fontCache, builtInCMapCache: catalog.builtInCMapCache, standardFontDataCache: catalog.standardFontDataCache, + globalColorSpaceCache: this.globalColorSpaceCache, globalImageCache: catalog.globalImageCache, systemFontCache: catalog.systemFontCache, nonBlendModesSet: catalog.nonBlendModesSet, diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 47e09c206..91ac7c28d 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -221,6 +221,7 @@ class PartialEvaluator { fontCache, builtInCMapCache, standardFontDataCache, + globalColorSpaceCache, globalImageCache, systemFontCache, options = null, @@ -232,6 +233,7 @@ class PartialEvaluator { this.fontCache = fontCache; this.builtInCMapCache = builtInCMapCache; this.standardFontDataCache = standardFontDataCache; + this.globalColorSpaceCache = globalColorSpaceCache; this.globalImageCache = globalImageCache; this.systemFontCache = systemFontCache; this.options = options || DefaultPartialEvaluatorOptions; @@ -492,6 +494,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( cs, this.xref, + this.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { @@ -737,6 +740,7 @@ class PartialEvaluator { image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }); // We force the use of RGBA_32BPP images here, because we can't handle @@ -839,6 +843,7 @@ class PartialEvaluator { image, isInline, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }) .then(async imageObj => { @@ -1463,6 +1468,7 @@ class PartialEvaluator { xref: this.xref, resources, pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, localColorSpaceCache, }).catch(reason => { if (reason instanceof AbortException) { @@ -1496,6 +1502,7 @@ class PartialEvaluator { this.xref, resources, this._pdfFunctionFactory, + this.globalColorSpaceCache, localColorSpaceCache ); patternIR = shadingFill.getIR(); @@ -1977,6 +1984,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( args[0], xref, + self.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { @@ -2002,6 +2010,7 @@ class PartialEvaluator { const cachedColorSpace = ColorSpace.getCached( args[0], xref, + self.globalColorSpaceCache, localColorSpaceCache ); if (cachedColorSpace) { diff --git a/src/core/image.js b/src/core/image.js index 7ab44bcf9..cfdf43ade 100644 --- a/src/core/image.js +++ b/src/core/image.js @@ -100,6 +100,7 @@ class PDFImage { mask = null, isMask = false, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { this.image = image; @@ -214,6 +215,7 @@ class PDFImage { xref, resources: isInline ? res : null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.numComps = this.colorSpace.numComps; @@ -261,6 +263,7 @@ class PDFImage { image: smask, isInline, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } else if (mask) { @@ -277,6 +280,7 @@ class PDFImage { isInline, isMask: true, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } @@ -297,6 +301,7 @@ class PDFImage { image, isInline = false, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }) { const imageData = image; @@ -328,6 +333,7 @@ class PDFImage { smask: smaskData, mask: maskData, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); } diff --git a/src/core/image_utils.js b/src/core/image_utils.js index 05e2b7b56..a907d5ceb 100644 --- a/src/core/image_utils.js +++ b/src/core/image_utils.js @@ -169,6 +169,26 @@ class RegionalImageCache extends BaseLocalCache { } } +class GlobalColorSpaceCache extends BaseLocalCache { + constructor(options) { + super({ onlyRefs: true }); + } + + set(name = null, ref, data) { + if (!ref) { + throw new Error('GlobalColorSpaceCache.set - expected "ref" argument.'); + } + if (this._imageCache.has(ref)) { + return; + } + this._imageCache.put(ref, data); + } + + clear() { + this._imageCache.clear(); + } +} + class GlobalImageCache { static NUM_PAGES_THRESHOLD = 2; @@ -290,6 +310,7 @@ class GlobalImageCache { } export { + GlobalColorSpaceCache, GlobalImageCache, LocalColorSpaceCache, LocalFunctionCache, diff --git a/src/core/pattern.js b/src/core/pattern.js index 598d5d2a9..7257a67f8 100644 --- a/src/core/pattern.js +++ b/src/core/pattern.js @@ -52,6 +52,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ) { const dict = shading instanceof BaseStream ? shading.dict : shading; @@ -66,6 +67,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ); case ShadingType.FREE_FORM_MESH: @@ -77,6 +79,7 @@ class Pattern { xref, res, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ); default: @@ -114,7 +117,14 @@ class BaseShading { // Radial and axial shading have very similar implementations // If needed, the implementations can be broken into two classes. class RadialAxialShading extends BaseShading { - constructor(dict, xref, resources, pdfFunctionFactory, localColorSpaceCache) { + constructor( + dict, + xref, + resources, + pdfFunctionFactory, + globalColorSpaceCache, + localColorSpaceCache + ) { super(); this.shadingType = dict.get("ShadingType"); let coordsLen = 0; @@ -132,6 +142,7 @@ class RadialAxialShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.bbox = lookupNormalRect(dict.getArray("BBox"), null); @@ -452,6 +463,7 @@ class MeshShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache ) { super(); @@ -466,6 +478,7 @@ class MeshShading extends BaseShading { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); this.background = dict.has("Background") diff --git a/test/unit/colorspace_spec.js b/test/unit/colorspace_spec.js index ca1a742a4..f9da88ccb 100644 --- a/test/unit/colorspace_spec.js +++ b/test/unit/colorspace_spec.js @@ -14,9 +14,12 @@ */ import { Dict, Name, Ref } from "../../src/core/primitives.js"; +import { + GlobalColorSpaceCache, + LocalColorSpaceCache, +} from "../../src/core/image_utils.js"; import { Stream, StringStream } from "../../src/core/stream.js"; import { ColorSpace } from "../../src/core/colorspace.js"; -import { LocalColorSpaceCache } from "../../src/core/image_utils.js"; import { PDFFunctionFactory } from "../../src/core/function.js"; import { XRefMock } from "./test_utils.js"; @@ -50,13 +53,15 @@ describe("colorspace", function () { }); describe("ColorSpace caching", function () { - let localColorSpaceCache = null; + let globalColorSpaceCache, localColorSpaceCache; beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); localColorSpaceCache = new LocalColorSpaceCache(); }); afterAll(function () { + globalColorSpaceCache = null; localColorSpaceCache = null; }); @@ -71,6 +76,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace1.name).toEqual("Pattern"); @@ -80,6 +86,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace2.name).toEqual("Pattern"); @@ -89,6 +96,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache: new GlobalColorSpaceCache(), localColorSpaceCache: new LocalColorSpaceCache(), }); expect(colorSpaceNonCached.name).toEqual("Pattern"); @@ -98,6 +106,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpaceOther.name).toEqual("DeviceRGB"); @@ -140,6 +149,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace1.name).toEqual("CalGray"); @@ -149,6 +159,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpace2.name).toEqual("CalGray"); @@ -158,6 +169,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache: new GlobalColorSpaceCache(), localColorSpaceCache: new LocalColorSpaceCache(), }); expect(colorSpaceNonCached.name).toEqual("CalGray"); @@ -167,6 +179,7 @@ describe("colorspace", function () { xref, resources: null, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache, }); expect(colorSpaceOther.name).toEqual("CalRGB"); @@ -180,6 +193,16 @@ describe("colorspace", function () { }); describe("DeviceGrayCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceGray"); const xref = new XRefMock([ @@ -198,6 +221,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -249,6 +273,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -278,6 +303,16 @@ describe("colorspace", function () { }); describe("DeviceRgbCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceRGB"); const xref = new XRefMock([ @@ -296,6 +331,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -353,6 +389,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -388,6 +425,16 @@ describe("colorspace", function () { }); describe("DeviceCmykCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is a Name object", function () { const cs = Name.get("DeviceCMYK"); const xref = new XRefMock([ @@ -406,6 +453,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -463,6 +511,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -498,6 +547,16 @@ describe("colorspace", function () { }); describe("CalGrayCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -521,6 +580,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -557,6 +617,16 @@ describe("colorspace", function () { }); describe("CalRGBCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -581,6 +651,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -616,6 +687,16 @@ describe("colorspace", function () { }); describe("LabCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const params = new Dict(); params.set("WhitePoint", [1, 1, 1]); @@ -639,6 +720,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -675,6 +757,16 @@ describe("colorspace", function () { }); describe("IndexedCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { // prettier-ignore const lookup = new Stream( @@ -701,6 +793,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); @@ -730,6 +823,16 @@ describe("colorspace", function () { }); describe("AlternateCS", function () { + let globalColorSpaceCache; + + beforeAll(function () { + globalColorSpaceCache = new GlobalColorSpaceCache(); + }); + + afterAll(function () { + globalColorSpaceCache = null; + }); + it("should handle the case when cs is an array", function () { const fnDict = new Dict(); fnDict.set("FunctionType", 4); @@ -769,6 +872,7 @@ describe("colorspace", function () { xref, resources, pdfFunctionFactory, + globalColorSpaceCache, localColorSpaceCache: new LocalColorSpaceCache(), }); diff --git a/test/unit/default_appearance_spec.js b/test/unit/default_appearance_spec.js index 94a844921..5599cedac 100644 --- a/test/unit/default_appearance_spec.js +++ b/test/unit/default_appearance_spec.js @@ -20,6 +20,7 @@ import { } from "../../src/core/default_appearance.js"; import { Dict, Name } from "../../src/core/primitives.js"; import { NullStream, StringStream } from "../../src/core/stream.js"; +import { GlobalColorSpaceCache } from "../../src/core/image_utils.js"; import { XRefMock } from "./test_utils.js"; describe("Default appearance", function () { @@ -56,7 +57,7 @@ describe("Default appearance", function () { }); describe("parseAppearanceStream", () => { - let evaluatorOptions, xref; + let evaluatorOptions, xref, globalColorSpaceCache; beforeAll(function () { evaluatorOptions = { @@ -64,11 +65,13 @@ describe("Default appearance", function () { isOffscreenCanvasSupported: false, }; xref = new XRefMock(); + globalColorSpaceCache = new GlobalColorSpaceCache(); }); afterAll(function () { evaluatorOptions = null; xref = null; + globalColorSpaceCache = null; }); it("should parse a FreeText (from Acrobat) appearance", () => { @@ -101,9 +104,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([107, 217, 41]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -122,9 +130,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([237, 43, 112]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -159,9 +172,14 @@ describe("Default appearance", function () { fontName: "TT1", fontColor: new Uint8ClampedArray([135, 78, 254]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -182,9 +200,14 @@ describe("Default appearance", function () { fontName: "Helv", fontColor: new Uint8ClampedArray([16, 124, 16]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -208,9 +231,14 @@ describe("Default appearance", function () { fontName: "FXF0", fontColor: new Uint8ClampedArray([149, 63, 60]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); @@ -232,9 +260,14 @@ describe("Default appearance", function () { fontName: "Invalid_font", fontColor: new Uint8ClampedArray([0, 85, 127]), }; - expect(parseAppearanceStream(appearance, evaluatorOptions, xref)).toEqual( - result - ); + expect( + parseAppearanceStream( + appearance, + evaluatorOptions, + xref, + globalColorSpaceCache + ) + ).toEqual(result); expect(appearance.pos).toEqual(0); }); });