diff --git a/src/core/function.js b/src/core/function.js index 7cd9eee42..f426f15bf 100644 --- a/src/core/function.js +++ b/src/core/function.js @@ -33,78 +33,41 @@ class PDFFunctionFactory { this.isEvalSupported = isEvalSupported !== false; } - create(fn) { - const cachedFunction = this.getCached(fn); - if (cachedFunction) { - return cachedFunction; - } - const parsedFunction = PDFFunction.parse({ - xref: this.xref, - isEvalSupported: this.isEvalSupported, - fn: fn instanceof Ref ? this.xref.fetch(fn) : fn, - }); + create(fn, parseArray = false) { + let fnRef, parsedFn; - // Attempt to cache the parsed Function, by reference. - this._cache(fn, parsedFunction); - - return parsedFunction; - } - - createFromArray(fnObj) { - const cachedFunction = this.getCached(fnObj); - if (cachedFunction) { - return cachedFunction; - } - const parsedFunction = PDFFunction.parseArray({ - xref: this.xref, - isEvalSupported: this.isEvalSupported, - fnObj: fnObj instanceof Ref ? this.xref.fetch(fnObj) : fnObj, - }); - - // Attempt to cache the parsed Function, by reference. - this._cache(fnObj, parsedFunction); - - return parsedFunction; - } - - getCached(cacheKey) { - let fnRef; - if (cacheKey instanceof Ref) { - fnRef = cacheKey; - } else if (cacheKey instanceof Dict) { - fnRef = cacheKey.objId; - } else if (cacheKey instanceof BaseStream) { - fnRef = cacheKey.dict?.objId; + // Check if the Function is cached first, to avoid re-parsing it. + if (fn instanceof Ref) { + fnRef = fn; + } else if (fn instanceof Dict) { + fnRef = fn.objId; + } else if (fn instanceof BaseStream) { + fnRef = fn.dict?.objId; } if (fnRef) { - const localFunction = this._localFunctionCache.getByRef(fnRef); - if (localFunction) { - return localFunction; + const cachedFn = this._localFunctionCache.getByRef(fnRef); + if (cachedFn) { + return cachedFn; } } - return null; - } - /** - * @private - */ - _cache(cacheKey, parsedFunction) { - if (!parsedFunction) { - throw new Error( - 'PDFFunctionFactory._cache - expected "parsedFunction" argument.' - ); - } - let fnRef; - if (cacheKey instanceof Ref) { - fnRef = cacheKey; - } else if (cacheKey instanceof Dict) { - fnRef = cacheKey.objId; - } else if (cacheKey instanceof BaseStream) { - fnRef = cacheKey.dict?.objId; + const fnObj = this.xref.fetchIfRef(fn); + if (Array.isArray(fnObj)) { + if (!parseArray) { + throw new Error( + 'PDFFunctionFactory.create - expected "parseArray" argument.' + ); + } + parsedFn = PDFFunction.parseArray(this, fnObj); + } else { + parsedFn = PDFFunction.parse(this, fnObj); } + + // Attempt to cache the parsed Function, by reference. if (fnRef) { - this._localFunctionCache.set(/* name = */ null, fnRef, parsedFunction); + this._localFunctionCache.set(/* name = */ null, fnRef, parsedFn); } + return parsedFn; } /** @@ -156,36 +119,31 @@ class PDFFunction { return array; } - static parse({ xref, isEvalSupported, fn }) { + static parse(factory, fn) { const dict = fn.dict || fn; const typeNum = dict.get("FunctionType"); switch (typeNum) { case 0: - return this.constructSampled({ xref, isEvalSupported, fn, dict }); + return this.constructSampled(factory, fn, dict); case 1: break; case 2: - return this.constructInterpolated({ xref, isEvalSupported, dict }); + return this.constructInterpolated(factory, dict); case 3: - return this.constructStiched({ xref, isEvalSupported, dict }); + return this.constructStiched(factory, dict); case 4: - return this.constructPostScript({ xref, isEvalSupported, fn, dict }); + return this.constructPostScript(factory, fn, dict); } throw new FormatError("Unknown type of function"); } - static parseArray({ xref, isEvalSupported, fnObj }) { - if (!Array.isArray(fnObj)) { - // not an array -- parsing as regular function - return this.parse({ xref, isEvalSupported, fn: fnObj }); - } + static parseArray(factory, fnObj) { + const { xref } = factory; const fnArray = []; for (const fn of fnObj) { - fnArray.push( - this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fn) }) - ); + fnArray.push(this.parse(factory, xref.fetchIfRef(fn))); } return function (src, srcOffset, dest, destOffset) { for (let i = 0, ii = fnArray.length; i < ii; i++) { @@ -194,7 +152,7 @@ class PDFFunction { }; } - static constructSampled({ xref, isEvalSupported, fn, dict }) { + static constructSampled(factory, fn, dict) { function toMultiArray(arr) { const inputLength = arr.length; const out = []; @@ -317,7 +275,7 @@ class PDFFunction { }; } - static constructInterpolated({ xref, isEvalSupported, dict }) { + static constructInterpolated(factory, dict) { const c0 = toNumberArray(dict.getArray("C0")) || [0]; const c1 = toNumberArray(dict.getArray("C1")) || [1]; const n = dict.get("N"); @@ -337,7 +295,7 @@ class PDFFunction { }; } - static constructStiched({ xref, isEvalSupported, dict }) { + static constructStiched(factory, dict) { const domain = toNumberArray(dict.getArray("Domain")); if (!domain) { @@ -348,10 +306,11 @@ class PDFFunction { if (inputSize !== 1) { throw new FormatError("Bad domain for stiched function"); } + const { xref } = factory; const fns = []; for (const fn of dict.get("Functions")) { - fns.push(this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fn) })); + fns.push(this.parse(factory, xref.fetchIfRef(fn))); } const bounds = toNumberArray(dict.getArray("Bounds")); @@ -395,7 +354,7 @@ class PDFFunction { }; } - static constructPostScript({ xref, isEvalSupported, fn, dict }) { + static constructPostScript(factory, fn, dict) { const domain = toNumberArray(dict.getArray("Domain")); const range = toNumberArray(dict.getArray("Range")); @@ -411,7 +370,7 @@ class PDFFunction { const parser = new PostScriptParser(lexer); const code = parser.parse(); - if (isEvalSupported && FeatureTest.isEvalSupported) { + if (factory.isEvalSupported && FeatureTest.isEvalSupported) { const compiled = new PostScriptCompiler().compile(code, domain, range); if (compiled) { // Compiled function consists of simple expressions such as addition, diff --git a/src/core/pattern.js b/src/core/pattern.js index 033e72383..b24d062b5 100644 --- a/src/core/pattern.js +++ b/src/core/pattern.js @@ -178,7 +178,7 @@ class RadialAxialShading extends BaseShading { this.extendEnd = extendEnd; const fnObj = dict.getRaw("Function"); - const fn = pdfFunctionFactory.createFromArray(fnObj); + const fn = pdfFunctionFactory.create(fnObj, /* parseArray = */ true); // Use lcm(1,2,3,4,5,6,7,8,10) = 840 (including 9 increases this to 2520) // to catch evenly spaced stops. oeis.org/A003418 @@ -486,7 +486,9 @@ class MeshShading extends BaseShading { : null; const fnObj = dict.getRaw("Function"); - const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null; + const fn = fnObj + ? pdfFunctionFactory.create(fnObj, /* parseArray = */ true) + : null; this.coords = []; this.colors = [];