Merge pull request #12034 from Snuffleupagus/Function-local-cache-3
Add local caching of `Function`s, by reference, in the `PDFFunctionFactory` (issue 2541)
This commit is contained in:
commit
29548ad498
@ -535,7 +535,7 @@ class ColorSpace {
|
|||||||
const name = xref.fetchIfRef(cs[1]);
|
const name = xref.fetchIfRef(cs[1]);
|
||||||
numComps = Array.isArray(name) ? name.length : 1;
|
numComps = Array.isArray(name) ? name.length : 1;
|
||||||
alt = this.parseToIR(cs[2], xref, resources, pdfFunctionFactory);
|
alt = this.parseToIR(cs[2], xref, resources, pdfFunctionFactory);
|
||||||
const tintFn = pdfFunctionFactory.create(xref.fetchIfRef(cs[3]));
|
const tintFn = pdfFunctionFactory.create(cs[3]);
|
||||||
return ["AlternateCS", numComps, alt, tintFn];
|
return ["AlternateCS", numComps, alt, tintFn];
|
||||||
case "Lab":
|
case "Lab":
|
||||||
params = xref.fetchIfRef(cs[1]);
|
params = xref.fetchIfRef(cs[1]);
|
||||||
|
@ -53,7 +53,6 @@ import { calculateMD5 } from "./crypto.js";
|
|||||||
import { Linearization } from "./parser.js";
|
import { Linearization } from "./parser.js";
|
||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { PartialEvaluator } from "./evaluator.js";
|
import { PartialEvaluator } from "./evaluator.js";
|
||||||
import { PDFFunctionFactory } from "./function.js";
|
|
||||||
|
|
||||||
const DEFAULT_USER_UNIT = 1.0;
|
const DEFAULT_USER_UNIT = 1.0;
|
||||||
const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
|
const LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
|
||||||
@ -75,7 +74,6 @@ class Page {
|
|||||||
fontCache,
|
fontCache,
|
||||||
builtInCMapCache,
|
builtInCMapCache,
|
||||||
globalImageCache,
|
globalImageCache,
|
||||||
pdfFunctionFactory,
|
|
||||||
}) {
|
}) {
|
||||||
this.pdfManager = pdfManager;
|
this.pdfManager = pdfManager;
|
||||||
this.pageIndex = pageIndex;
|
this.pageIndex = pageIndex;
|
||||||
@ -85,7 +83,6 @@ class Page {
|
|||||||
this.fontCache = fontCache;
|
this.fontCache = fontCache;
|
||||||
this.builtInCMapCache = builtInCMapCache;
|
this.builtInCMapCache = builtInCMapCache;
|
||||||
this.globalImageCache = globalImageCache;
|
this.globalImageCache = globalImageCache;
|
||||||
this.pdfFunctionFactory = pdfFunctionFactory;
|
|
||||||
this.evaluatorOptions = pdfManager.evaluatorOptions;
|
this.evaluatorOptions = pdfManager.evaluatorOptions;
|
||||||
this.resourcesPromise = null;
|
this.resourcesPromise = null;
|
||||||
|
|
||||||
@ -265,7 +262,6 @@ class Page {
|
|||||||
builtInCMapCache: this.builtInCMapCache,
|
builtInCMapCache: this.builtInCMapCache,
|
||||||
globalImageCache: this.globalImageCache,
|
globalImageCache: this.globalImageCache,
|
||||||
options: this.evaluatorOptions,
|
options: this.evaluatorOptions,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
|
const dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
|
||||||
@ -359,7 +355,6 @@ class Page {
|
|||||||
builtInCMapCache: this.builtInCMapCache,
|
builtInCMapCache: this.builtInCMapCache,
|
||||||
globalImageCache: this.globalImageCache,
|
globalImageCache: this.globalImageCache,
|
||||||
options: this.evaluatorOptions,
|
options: this.evaluatorOptions,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return partialEvaluator.getTextContent({
|
return partialEvaluator.getTextContent({
|
||||||
@ -508,11 +503,6 @@ class PDFDocument {
|
|||||||
this.pdfManager = pdfManager;
|
this.pdfManager = pdfManager;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.xref = new XRef(stream, pdfManager);
|
this.xref = new XRef(stream, pdfManager);
|
||||||
|
|
||||||
this.pdfFunctionFactory = new PDFFunctionFactory({
|
|
||||||
xref: this.xref,
|
|
||||||
isEvalSupported: pdfManager.evaluatorOptions.isEvalSupported,
|
|
||||||
});
|
|
||||||
this._pagePromises = [];
|
this._pagePromises = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -821,7 +811,6 @@ class PDFDocument {
|
|||||||
fontCache: catalog.fontCache,
|
fontCache: catalog.fontCache,
|
||||||
builtInCMapCache: catalog.builtInCMapCache,
|
builtInCMapCache: catalog.builtInCMapCache,
|
||||||
globalImageCache: catalog.globalImageCache,
|
globalImageCache: catalog.globalImageCache,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
isNum,
|
isNum,
|
||||||
isString,
|
isString,
|
||||||
OPS,
|
OPS,
|
||||||
|
shadow,
|
||||||
stringToPDFString,
|
stringToPDFString,
|
||||||
TextRenderingMode,
|
TextRenderingMode,
|
||||||
UNSUPPORTED_FEATURES,
|
UNSUPPORTED_FEATURES,
|
||||||
@ -72,6 +73,7 @@ import {
|
|||||||
getSymbolsFonts,
|
getSymbolsFonts,
|
||||||
} from "./standard_fonts.js";
|
} from "./standard_fonts.js";
|
||||||
import { getTilingPatternIR, Pattern } from "./pattern.js";
|
import { getTilingPatternIR, Pattern } from "./pattern.js";
|
||||||
|
import { isPDFFunction, PDFFunctionFactory } from "./function.js";
|
||||||
import { Lexer, Parser } from "./parser.js";
|
import { Lexer, Parser } from "./parser.js";
|
||||||
import { LocalColorSpaceCache, LocalImageCache } from "./image_utils.js";
|
import { LocalColorSpaceCache, LocalImageCache } from "./image_utils.js";
|
||||||
import { bidi } from "./bidi.js";
|
import { bidi } from "./bidi.js";
|
||||||
@ -79,7 +81,6 @@ import { ColorSpace } from "./colorspace.js";
|
|||||||
import { DecodeStream } from "./stream.js";
|
import { DecodeStream } from "./stream.js";
|
||||||
import { getGlyphsUnicode } from "./glyphlist.js";
|
import { getGlyphsUnicode } from "./glyphlist.js";
|
||||||
import { getMetrics } from "./metrics.js";
|
import { getMetrics } from "./metrics.js";
|
||||||
import { isPDFFunction } from "./function.js";
|
|
||||||
import { MurmurHash3_64 } from "./murmurhash3.js";
|
import { MurmurHash3_64 } from "./murmurhash3.js";
|
||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { PDFImage } from "./image.js";
|
import { PDFImage } from "./image.js";
|
||||||
@ -103,7 +104,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
builtInCMapCache,
|
builtInCMapCache,
|
||||||
globalImageCache,
|
globalImageCache,
|
||||||
options = null,
|
options = null,
|
||||||
pdfFunctionFactory,
|
|
||||||
}) {
|
}) {
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
@ -113,7 +113,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
this.builtInCMapCache = builtInCMapCache;
|
this.builtInCMapCache = builtInCMapCache;
|
||||||
this.globalImageCache = globalImageCache;
|
this.globalImageCache = globalImageCache;
|
||||||
this.options = options || DefaultPartialEvaluatorOptions;
|
this.options = options || DefaultPartialEvaluatorOptions;
|
||||||
this.pdfFunctionFactory = pdfFunctionFactory;
|
|
||||||
this.parsingType3Font = false;
|
this.parsingType3Font = false;
|
||||||
|
|
||||||
this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this);
|
this._fetchBuiltInCMapBound = this.fetchBuiltInCMap.bind(this);
|
||||||
@ -207,6 +206,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
SHADING_PATTERN = 2;
|
SHADING_PATTERN = 2;
|
||||||
|
|
||||||
PartialEvaluator.prototype = {
|
PartialEvaluator.prototype = {
|
||||||
|
/**
|
||||||
|
* Since Functions are only cached (locally) by reference, we can share one
|
||||||
|
* `PDFFunctionFactory` instance within this `PartialEvaluator` instance.
|
||||||
|
*/
|
||||||
|
get _pdfFunctionFactory() {
|
||||||
|
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||||
|
xref: this.xref,
|
||||||
|
isEvalSupported: this.options.isEvalSupported,
|
||||||
|
});
|
||||||
|
return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory);
|
||||||
|
},
|
||||||
|
|
||||||
clone(newOptions = DefaultPartialEvaluatorOptions) {
|
clone(newOptions = DefaultPartialEvaluatorOptions) {
|
||||||
var newEvaluator = Object.create(this);
|
var newEvaluator = Object.create(this);
|
||||||
newEvaluator.options = newOptions;
|
newEvaluator.options = newOptions;
|
||||||
@ -552,7 +563,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
res: resources,
|
res: resources,
|
||||||
image,
|
image,
|
||||||
isInline,
|
isInline,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
pdfFunctionFactory: this._pdfFunctionFactory,
|
||||||
localColorSpaceCache,
|
localColorSpaceCache,
|
||||||
});
|
});
|
||||||
// We force the use of RGBA_32BPP images here, because we can't handle
|
// We force the use of RGBA_32BPP images here, because we can't handle
|
||||||
@ -589,7 +600,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
res: resources,
|
res: resources,
|
||||||
image,
|
image,
|
||||||
isInline,
|
isInline,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
pdfFunctionFactory: this._pdfFunctionFactory,
|
||||||
localColorSpaceCache,
|
localColorSpaceCache,
|
||||||
})
|
})
|
||||||
.then(imageObj => {
|
.then(imageObj => {
|
||||||
@ -651,7 +662,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
// we will build a map of integer values in range 0..255 to be fast.
|
// we will build a map of integer values in range 0..255 to be fast.
|
||||||
var transferObj = smask.get("TR");
|
var transferObj = smask.get("TR");
|
||||||
if (isPDFFunction(transferObj)) {
|
if (isPDFFunction(transferObj)) {
|
||||||
const transferFn = this.pdfFunctionFactory.create(transferObj);
|
const transferFn = this._pdfFunctionFactory.create(transferObj);
|
||||||
var transferMap = new Uint8Array(256);
|
var transferMap = new Uint8Array(256);
|
||||||
var tmp = new Float32Array(1);
|
var tmp = new Float32Array(1);
|
||||||
for (var i = 0; i < 256; i++) {
|
for (var i = 0; i < 256; i++) {
|
||||||
@ -1145,7 +1156,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
cs,
|
cs,
|
||||||
xref: this.xref,
|
xref: this.xref,
|
||||||
resources,
|
resources,
|
||||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
pdfFunctionFactory: this._pdfFunctionFactory,
|
||||||
localColorSpaceCache,
|
localColorSpaceCache,
|
||||||
}).catch(reason => {
|
}).catch(reason => {
|
||||||
if (reason instanceof AbortException) {
|
if (reason instanceof AbortException) {
|
||||||
@ -1202,7 +1213,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
this.xref,
|
this.xref,
|
||||||
resources,
|
resources,
|
||||||
this.handler,
|
this.handler,
|
||||||
this.pdfFunctionFactory,
|
this._pdfFunctionFactory,
|
||||||
localColorSpaceCache
|
localColorSpaceCache
|
||||||
);
|
);
|
||||||
operatorList.addOp(fn, pattern.getIR());
|
operatorList.addOp(fn, pattern.getIR());
|
||||||
@ -1641,7 +1652,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
xref,
|
xref,
|
||||||
resources,
|
resources,
|
||||||
self.handler,
|
self.handler,
|
||||||
self.pdfFunctionFactory,
|
self._pdfFunctionFactory,
|
||||||
localColorSpaceCache
|
localColorSpaceCache
|
||||||
);
|
);
|
||||||
var patternIR = shadingFill.getIR();
|
var patternIR = shadingFill.getIR();
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Dict, isDict, isStream, Ref } from "./primitives.js";
|
||||||
import {
|
import {
|
||||||
FormatError,
|
FormatError,
|
||||||
info,
|
info,
|
||||||
@ -20,29 +21,94 @@ import {
|
|||||||
IsEvalSupportedCached,
|
IsEvalSupportedCached,
|
||||||
unreachable,
|
unreachable,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
import { isDict, isStream } from "./primitives.js";
|
|
||||||
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
|
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
|
||||||
|
import { LocalFunctionCache } from "./image_utils.js";
|
||||||
|
|
||||||
class PDFFunctionFactory {
|
class PDFFunctionFactory {
|
||||||
constructor({ xref, isEvalSupported = true }) {
|
constructor({ xref, isEvalSupported = true }) {
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
this.isEvalSupported = isEvalSupported !== false;
|
this.isEvalSupported = isEvalSupported !== false;
|
||||||
|
this._localFunctionCache = null; // Initialized lazily.
|
||||||
}
|
}
|
||||||
|
|
||||||
create(fn) {
|
create(fn) {
|
||||||
return PDFFunction.parse({
|
const cachedFunction = this.getCached(fn);
|
||||||
|
if (cachedFunction) {
|
||||||
|
return cachedFunction;
|
||||||
|
}
|
||||||
|
const parsedFunction = PDFFunction.parse({
|
||||||
xref: this.xref,
|
xref: this.xref,
|
||||||
isEvalSupported: this.isEvalSupported,
|
isEvalSupported: this.isEvalSupported,
|
||||||
fn,
|
fn: fn instanceof Ref ? this.xref.fetch(fn) : fn,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Attempt to cache the parsed Function, by reference.
|
||||||
|
this._cache(fn, parsedFunction);
|
||||||
|
|
||||||
|
return parsedFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
createFromArray(fnObj) {
|
createFromArray(fnObj) {
|
||||||
return PDFFunction.parseArray({
|
const cachedFunction = this.getCached(fnObj);
|
||||||
|
if (cachedFunction) {
|
||||||
|
return cachedFunction;
|
||||||
|
}
|
||||||
|
const parsedFunction = PDFFunction.parseArray({
|
||||||
xref: this.xref,
|
xref: this.xref,
|
||||||
isEvalSupported: this.isEvalSupported,
|
isEvalSupported: this.isEvalSupported,
|
||||||
fnObj,
|
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 (isStream(cacheKey)) {
|
||||||
|
fnRef = cacheKey.dict && cacheKey.dict.objId;
|
||||||
|
}
|
||||||
|
if (fnRef) {
|
||||||
|
if (!this._localFunctionCache) {
|
||||||
|
this._localFunctionCache = new LocalFunctionCache();
|
||||||
|
}
|
||||||
|
const localFunction = this._localFunctionCache.getByRef(fnRef);
|
||||||
|
if (localFunction) {
|
||||||
|
return localFunction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 (isStream(cacheKey)) {
|
||||||
|
fnRef = cacheKey.dict && cacheKey.dict.objId;
|
||||||
|
}
|
||||||
|
if (fnRef) {
|
||||||
|
if (!this._localFunctionCache) {
|
||||||
|
this._localFunctionCache = new LocalFunctionCache();
|
||||||
|
}
|
||||||
|
this._localFunctionCache.set(/* name = */ null, fnRef, parsedFunction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +91,22 @@ class LocalColorSpaceCache extends BaseLocalCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LocalFunctionCache extends BaseLocalCache {
|
||||||
|
getByName(name) {
|
||||||
|
unreachable("Should not call `getByName` method.");
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name = null, ref, data) {
|
||||||
|
if (!ref) {
|
||||||
|
throw new Error('LocalFunctionCache.set - expected "ref" argument.');
|
||||||
|
}
|
||||||
|
if (this._imageCache.has(ref)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._imageCache.put(ref, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class GlobalImageCache {
|
class GlobalImageCache {
|
||||||
static get NUM_PAGES_THRESHOLD() {
|
static get NUM_PAGES_THRESHOLD() {
|
||||||
return shadow(this, "NUM_PAGES_THRESHOLD", 2);
|
return shadow(this, "NUM_PAGES_THRESHOLD", 2);
|
||||||
@ -184,4 +200,9 @@ class GlobalImageCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { LocalImageCache, LocalColorSpaceCache, GlobalImageCache };
|
export {
|
||||||
|
LocalImageCache,
|
||||||
|
LocalColorSpaceCache,
|
||||||
|
LocalFunctionCache,
|
||||||
|
GlobalImageCache,
|
||||||
|
};
|
||||||
|
@ -178,7 +178,7 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
this.extendStart = extendStart;
|
this.extendStart = extendStart;
|
||||||
this.extendEnd = extendEnd;
|
this.extendEnd = extendEnd;
|
||||||
|
|
||||||
var fnObj = dict.get("Function");
|
var fnObj = dict.getRaw("Function");
|
||||||
var fn = pdfFunctionFactory.createFromArray(fnObj);
|
var fn = pdfFunctionFactory.createFromArray(fnObj);
|
||||||
|
|
||||||
// 10 samples seems good enough for now, but probably won't work
|
// 10 samples seems good enough for now, but probably won't work
|
||||||
@ -878,7 +878,7 @@ Shadings.Mesh = (function MeshClosure() {
|
|||||||
? cs.getRgb(dict.get("Background"), 0)
|
? cs.getRgb(dict.get("Background"), 0)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
var fnObj = dict.get("Function");
|
var fnObj = dict.getRaw("Function");
|
||||||
var fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
|
var fn = fnObj ? pdfFunctionFactory.createFromArray(fnObj) : null;
|
||||||
|
|
||||||
this.coords = [];
|
this.coords = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user