Remove the PDFFunctionFactory.createFromArray method

This combines the `PDFFunctionFactory.create` and `PDFFunctionFactory.createFromArray` methods, which helps simplify and shorten the code.
Additionally, to simplify the parameter handling we pass the `PDFFunctionFactory`-instance directly to the various `PDFFunction`-methods.
This commit is contained in:
Jonas Jenwald 2025-03-07 12:24:27 +01:00
parent af89e77124
commit 17d7943758
2 changed files with 45 additions and 84 deletions

View File

@ -33,78 +33,41 @@ class PDFFunctionFactory {
this.isEvalSupported = isEvalSupported !== false; this.isEvalSupported = isEvalSupported !== false;
} }
create(fn) { create(fn, parseArray = false) {
const cachedFunction = this.getCached(fn); let fnRef, parsedFn;
if (cachedFunction) {
return cachedFunction;
}
const parsedFunction = PDFFunction.parse({
xref: this.xref,
isEvalSupported: this.isEvalSupported,
fn: fn instanceof Ref ? this.xref.fetch(fn) : fn,
});
// Attempt to cache the parsed Function, by reference. // Check if the Function is cached first, to avoid re-parsing it.
this._cache(fn, parsedFunction); if (fn instanceof Ref) {
fnRef = fn;
return parsedFunction; } else if (fn instanceof Dict) {
} fnRef = fn.objId;
} else if (fn instanceof BaseStream) {
createFromArray(fnObj) { fnRef = fn.dict?.objId;
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;
} }
if (fnRef) { if (fnRef) {
const localFunction = this._localFunctionCache.getByRef(fnRef); const cachedFn = this._localFunctionCache.getByRef(fnRef);
if (localFunction) { if (cachedFn) {
return localFunction; return cachedFn;
} }
} }
return null;
}
/** const fnObj = this.xref.fetchIfRef(fn);
* @private if (Array.isArray(fnObj)) {
*/ if (!parseArray) {
_cache(cacheKey, parsedFunction) { throw new Error(
if (!parsedFunction) { 'PDFFunctionFactory.create - expected "parseArray" argument.'
throw new Error( );
'PDFFunctionFactory._cache - expected "parsedFunction" argument.' }
); parsedFn = PDFFunction.parseArray(this, fnObj);
} } else {
let fnRef; parsedFn = PDFFunction.parse(this, fnObj);
if (cacheKey instanceof Ref) {
fnRef = cacheKey;
} else if (cacheKey instanceof Dict) {
fnRef = cacheKey.objId;
} else if (cacheKey instanceof BaseStream) {
fnRef = cacheKey.dict?.objId;
} }
// Attempt to cache the parsed Function, by reference.
if (fnRef) { 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; return array;
} }
static parse({ xref, isEvalSupported, fn }) { static parse(factory, fn) {
const dict = fn.dict || fn; const dict = fn.dict || fn;
const typeNum = dict.get("FunctionType"); const typeNum = dict.get("FunctionType");
switch (typeNum) { switch (typeNum) {
case 0: case 0:
return this.constructSampled({ xref, isEvalSupported, fn, dict }); return this.constructSampled(factory, fn, dict);
case 1: case 1:
break; break;
case 2: case 2:
return this.constructInterpolated({ xref, isEvalSupported, dict }); return this.constructInterpolated(factory, dict);
case 3: case 3:
return this.constructStiched({ xref, isEvalSupported, dict }); return this.constructStiched(factory, dict);
case 4: case 4:
return this.constructPostScript({ xref, isEvalSupported, fn, dict }); return this.constructPostScript(factory, fn, dict);
} }
throw new FormatError("Unknown type of function"); throw new FormatError("Unknown type of function");
} }
static parseArray({ xref, isEvalSupported, fnObj }) { static parseArray(factory, fnObj) {
if (!Array.isArray(fnObj)) { const { xref } = factory;
// not an array -- parsing as regular function
return this.parse({ xref, isEvalSupported, fn: fnObj });
}
const fnArray = []; const fnArray = [];
for (const fn of fnObj) { for (const fn of fnObj) {
fnArray.push( fnArray.push(this.parse(factory, xref.fetchIfRef(fn)));
this.parse({ xref, isEvalSupported, fn: xref.fetchIfRef(fn) })
);
} }
return function (src, srcOffset, dest, destOffset) { return function (src, srcOffset, dest, destOffset) {
for (let i = 0, ii = fnArray.length; i < ii; i++) { 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) { function toMultiArray(arr) {
const inputLength = arr.length; const inputLength = arr.length;
const out = []; 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 c0 = toNumberArray(dict.getArray("C0")) || [0];
const c1 = toNumberArray(dict.getArray("C1")) || [1]; const c1 = toNumberArray(dict.getArray("C1")) || [1];
const n = dict.get("N"); 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")); const domain = toNumberArray(dict.getArray("Domain"));
if (!domain) { if (!domain) {
@ -348,10 +306,11 @@ class PDFFunction {
if (inputSize !== 1) { if (inputSize !== 1) {
throw new FormatError("Bad domain for stiched function"); throw new FormatError("Bad domain for stiched function");
} }
const { xref } = factory;
const fns = []; const fns = [];
for (const fn of dict.get("Functions")) { 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")); 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 domain = toNumberArray(dict.getArray("Domain"));
const range = toNumberArray(dict.getArray("Range")); const range = toNumberArray(dict.getArray("Range"));
@ -411,7 +370,7 @@ class PDFFunction {
const parser = new PostScriptParser(lexer); const parser = new PostScriptParser(lexer);
const code = parser.parse(); const code = parser.parse();
if (isEvalSupported && FeatureTest.isEvalSupported) { if (factory.isEvalSupported && FeatureTest.isEvalSupported) {
const compiled = new PostScriptCompiler().compile(code, domain, range); const compiled = new PostScriptCompiler().compile(code, domain, range);
if (compiled) { if (compiled) {
// Compiled function consists of simple expressions such as addition, // Compiled function consists of simple expressions such as addition,

View File

@ -178,7 +178,7 @@ class RadialAxialShading extends BaseShading {
this.extendEnd = extendEnd; this.extendEnd = extendEnd;
const fnObj = dict.getRaw("Function"); 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) // 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 // to catch evenly spaced stops. oeis.org/A003418
@ -486,7 +486,9 @@ class MeshShading extends BaseShading {
: null; : null;
const fnObj = dict.getRaw("Function"); const fnObj = dict.getRaw("Function");
const fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null; const fn = fnObj
? pdfFunctionFactory.create(fnObj, /* parseArray = */ true)
: null;
this.coords = []; this.coords = [];
this.colors = []; this.colors = [];