in src/core/evaluator.js [277:387]
hasBlendModes(resources, nonBlendModesSet) {
if (!(resources instanceof Dict)) {
return false;
}
if (resources.objId && nonBlendModesSet.has(resources.objId)) {
return false;
}
const processed = new RefSet(nonBlendModesSet);
if (resources.objId) {
processed.put(resources.objId);
}
const nodes = [resources],
xref = this.xref;
while (nodes.length) {
const node = nodes.shift();
// First check the current resources for blend modes.
const graphicStates = node.get("ExtGState");
if (graphicStates instanceof Dict) {
for (let graphicState of graphicStates.getRawValues()) {
if (graphicState instanceof Ref) {
if (processed.has(graphicState)) {
continue; // The ExtGState has already been processed.
}
try {
graphicState = xref.fetch(graphicState);
} catch (ex) {
// Avoid parsing a corrupt ExtGState more than once.
processed.put(graphicState);
info(`hasBlendModes - ignoring ExtGState: "${ex}".`);
continue;
}
}
if (!(graphicState instanceof Dict)) {
continue;
}
if (graphicState.objId) {
processed.put(graphicState.objId);
}
const bm = graphicState.get("BM");
if (bm instanceof Name) {
if (bm.name !== "Normal") {
return true;
}
continue;
}
if (bm !== undefined && Array.isArray(bm)) {
for (const element of bm) {
if (element instanceof Name && element.name !== "Normal") {
return true;
}
}
}
}
}
// Descend into the XObjects to look for more resources and blend modes.
const xObjects = node.get("XObject");
if (!(xObjects instanceof Dict)) {
continue;
}
for (let xObject of xObjects.getRawValues()) {
if (xObject instanceof Ref) {
if (processed.has(xObject)) {
// The XObject has already been processed, and by avoiding a
// redundant `xref.fetch` we can *significantly* reduce the load
// time for badly generated PDF files (fixes issue6961.pdf).
continue;
}
try {
xObject = xref.fetch(xObject);
} catch (ex) {
// Avoid parsing a corrupt XObject more than once.
processed.put(xObject);
info(`hasBlendModes - ignoring XObject: "${ex}".`);
continue;
}
}
if (!(xObject instanceof BaseStream)) {
continue;
}
if (xObject.dict.objId) {
processed.put(xObject.dict.objId);
}
const xResources = xObject.dict.get("Resources");
if (!(xResources instanceof Dict)) {
continue;
}
// Checking objId to detect an infinite loop.
if (xResources.objId && processed.has(xResources.objId)) {
continue;
}
nodes.push(xResources);
if (xResources.objId) {
processed.put(xResources.objId);
}
}
}
// When no blend modes exist, there's no need re-fetch/re-parse any of the
// processed `Ref`s again for subsequent pages. This helps reduce redundant
// `XRef.fetch` calls for some documents (e.g. issue6961.pdf).
for (const ref of processed) {
nonBlendModesSet.put(ref);
}
return false;
}