Merge pull request #12099 from Snuffleupagus/hasBlendModes-RefSet

Use a `RefSet`, rather than a plain Object, for tracking already processed nodes in `PartialEvaluator.hasBlendModes`
This commit is contained in:
Tim van der Meij 2020-07-17 12:37:37 +02:00 committed by GitHub
commit 7d26f4147f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -44,6 +44,7 @@ import {
isStream, isStream,
Name, Name,
Ref, Ref,
RefSet,
} from "./primitives.js"; } from "./primitives.js";
import { import {
ErrorFont, ErrorFont,
@ -237,9 +238,9 @@ class PartialEvaluator {
return false; return false;
} }
var processed = Object.create(null); const processed = new RefSet();
if (resources.objId) { if (resources.objId) {
processed[resources.objId] = true; processed.put(resources.objId);
} }
var nodes = [resources], var nodes = [resources],
@ -249,13 +250,10 @@ class PartialEvaluator {
// First check the current resources for blend modes. // First check the current resources for blend modes.
var graphicStates = node.get("ExtGState"); var graphicStates = node.get("ExtGState");
if (graphicStates instanceof Dict) { if (graphicStates instanceof Dict) {
var graphicStatesKeys = graphicStates.getKeys(); for (const key of graphicStates.getKeys()) {
for (let i = 0, ii = graphicStatesKeys.length; i < ii; i++) {
const key = graphicStatesKeys[i];
let graphicState = graphicStates.getRaw(key); let graphicState = graphicStates.getRaw(key);
if (graphicState instanceof Ref) { if (graphicState instanceof Ref) {
if (processed[graphicState.toString()]) { if (processed.has(graphicState)) {
continue; // The ExtGState has already been processed. continue; // The ExtGState has already been processed.
} }
try { try {
@ -264,27 +262,18 @@ class PartialEvaluator {
if (ex instanceof MissingDataException) { if (ex instanceof MissingDataException) {
throw ex; throw ex;
} }
if (this.options.ignoreErrors) { // Avoid parsing a corrupt ExtGState more than once.
if (graphicState instanceof Ref) { processed.put(graphicState);
// Avoid parsing a corrupt ExtGState more than once.
processed[graphicState.toString()] = true; info(`hasBlendModes - ignoring ExtGState: "${ex}".`);
} continue;
// Error(s) in the ExtGState -- sending unsupported feature
// notification and allow parsing/rendering to continue.
this.handler.send("UnsupportedFeature", {
featureId: UNSUPPORTED_FEATURES.errorExtGState,
});
warn(`hasBlendModes - ignoring ExtGState: "${ex}".`);
continue;
}
throw ex;
} }
} }
if (!(graphicState instanceof Dict)) { if (!(graphicState instanceof Dict)) {
continue; continue;
} }
if (graphicState.objId) { if (graphicState.objId) {
processed[graphicState.objId] = true; processed.put(graphicState.objId);
} }
const bm = graphicState.get("BM"); const bm = graphicState.get("BM");
@ -295,8 +284,8 @@ class PartialEvaluator {
continue; continue;
} }
if (bm !== undefined && Array.isArray(bm)) { if (bm !== undefined && Array.isArray(bm)) {
for (let j = 0, jj = bm.length; j < jj; j++) { for (const element of bm) {
if (bm[j] instanceof Name && bm[j].name !== "Normal") { if (element instanceof Name && element.name !== "Normal") {
return true; return true;
} }
} }
@ -308,13 +297,10 @@ class PartialEvaluator {
if (!(xObjects instanceof Dict)) { if (!(xObjects instanceof Dict)) {
continue; continue;
} }
var xObjectsKeys = xObjects.getKeys(); for (const key of xObjects.getKeys()) {
for (let i = 0, ii = xObjectsKeys.length; i < ii; i++) {
const key = xObjectsKeys[i];
var xObject = xObjects.getRaw(key); var xObject = xObjects.getRaw(key);
if (xObject instanceof Ref) { if (xObject instanceof Ref) {
if (processed[xObject.toString()]) { if (processed.has(xObject)) {
// The XObject has already been processed, and by avoiding a // The XObject has already been processed, and by avoiding a
// redundant `xref.fetch` we can *significantly* reduce the load // redundant `xref.fetch` we can *significantly* reduce the load
// time for badly generated PDF files (fixes issue6961.pdf). // time for badly generated PDF files (fixes issue6961.pdf).
@ -326,41 +312,31 @@ class PartialEvaluator {
if (ex instanceof MissingDataException) { if (ex instanceof MissingDataException) {
throw ex; throw ex;
} }
if (this.options.ignoreErrors) { // Avoid parsing a corrupt XObject more than once.
if (xObject instanceof Ref) { processed.put(xObject);
// Avoid parsing a corrupt XObject more than once.
processed[xObject.toString()] = true; info(`hasBlendModes - ignoring XObject: "${ex}".`);
} continue;
// Error(s) in the XObject -- sending unsupported feature
// notification and allow parsing/rendering to continue.
this.handler.send("UnsupportedFeature", {
featureId: UNSUPPORTED_FEATURES.errorXObject,
});
warn(`hasBlendModes - ignoring XObject: "${ex}".`);
continue;
}
throw ex;
} }
} }
if (!isStream(xObject)) { if (!isStream(xObject)) {
continue; continue;
} }
if (xObject.dict.objId) { if (xObject.dict.objId) {
if (processed[xObject.dict.objId]) { processed.put(xObject.dict.objId);
continue; // Stream has objId and was processed already.
}
processed[xObject.dict.objId] = true;
} }
var xResources = xObject.dict.get("Resources"); var xResources = xObject.dict.get("Resources");
if (!(xResources instanceof Dict)) {
continue;
}
// Checking objId to detect an infinite loop. // Checking objId to detect an infinite loop.
if ( if (xResources.objId && processed.has(xResources.objId)) {
xResources instanceof Dict && continue;
(!xResources.objId || !processed[xResources.objId]) }
) {
nodes.push(xResources); nodes.push(xResources);
if (xResources.objId) { if (xResources.objId) {
processed[xResources.objId] = true; processed.put(xResources.objId);
}
} }
} }
} }