From 6ea824ee43bda7aad91766a814c04b289bd03951 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sat, 11 Jul 2020 14:05:53 +0200 Subject: [PATCH] Add local caching of non-font Graphics State (ExtGState) data in `PartialEvaluator.getTextContent` --- src/core/evaluator.js | 78 +++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index ea99df4dd626b0..b4631d494e3cd3 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -1866,6 +1866,7 @@ class PartialEvaluator { // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd. var xobjs = null; const emptyXObjectCache = new LocalImageCache(); + const emptyGStateCache = new LocalGStateCache(); var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); @@ -2442,25 +2443,68 @@ class PartialEvaluator { ); return; case OPS.setGState: - flushTextContentItem(); - var dictName = args[0]; - var extGState = resources.get("ExtGState"); - - if (!isDict(extGState) || !isName(dictName)) { - break; - } - var gState = extGState.get(dictName.name); - if (!isDict(gState)) { + name = args[0].name; + if (name && emptyGStateCache.getByName(name)) { break; } - var gStateFont = gState.get("Font"); - if (gStateFont) { - textState.fontName = null; - textState.fontSize = gStateFont[1]; - next(handleSetFont(null, gStateFont[0])); - return; - } - break; + + next( + new Promise(function (resolveGState, rejectGState) { + if (!name) { + throw new FormatError("GState must be referred to by name."); + } + + const extGState = resources.get("ExtGState"); + if (!(extGState instanceof Dict)) { + throw new FormatError("ExtGState should be a dictionary."); + } + + let gState = extGState.getRaw(name); + if (gState instanceof Ref) { + if (emptyGStateCache.getByRef(gState)) { + resolveGState(); + return; + } + + gState = xref.fetch(gState); + } + + if (!(gState instanceof Dict)) { + throw new FormatError("GState should be a dictionary."); + } + + const gStateFont = gState.get("Font"); + if (!gStateFont) { + emptyGStateCache.set(name, gState.objId, true); + + resolveGState(); + return; + } + flushTextContentItem(); + + textState.fontName = null; + textState.fontSize = gStateFont[1]; + handleSetFont(null, gStateFont[0]).then( + resolveGState, + rejectGState + ); + }).catch(function (reason) { + if (reason instanceof AbortException) { + return; + } + if (self.options.ignoreErrors) { + // Error(s) in the ExtGState -- sending unsupported feature + // notification and allow parsing/rendering to continue. + self.handler.send("UnsupportedFeature", { + featureId: UNSUPPORTED_FEATURES.errorExtGState, + }); + warn(`getTextContent - ignoring ExtGState: "${reason}".`); + return; + } + throw reason; + }) + ); + return; } // switch if (textContent.items.length >= sink.desiredSize) { // Wait for ready, if we reach highWaterMark.