Skip to content

Commit

Permalink
Remove ColorSpace.parseAsync and simplify the ColorSpace "API-surface"
Browse files Browse the repository at this point in the history
This patch reduces the number of `ColorSpace` static-methods, and in particular the `parseAsync` method is removed and it's now instead possible to have `parse` optionally return a Promise.
This thus removes the need to manually check if a `ColorSpace`-instance is cached, note the changes in the `src/core/evaluator.js` file.
  • Loading branch information
Snuffleupagus committed Mar 3, 2025
1 parent 5e6cfbe commit 93a8352
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 156 deletions.
153 changes: 46 additions & 107 deletions src/core/colorspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,134 +306,71 @@ class ColorSpace {
return shadow(this, "usesZeroToOneRange", true);
}

static #cache(
cacheKey,
parsedCS,
{ xref, globalColorSpaceCache, localColorSpaceCache }
) {
if (!globalColorSpaceCache || !localColorSpaceCache) {
throw new Error(
'ColorSpace.#cache - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.'
);
}
if (!parsedCS) {
throw new Error('ColorSpace.#cache - expected "parsedCS" argument.');
}
let csName, csRef;
if (cacheKey instanceof Ref) {
csRef = cacheKey;

// If parsing succeeded, we know that this call cannot throw.
cacheKey = xref.fetch(cacheKey);
}
if (cacheKey instanceof Name) {
csName = cacheKey.name;
}
if (csName || csRef) {
localColorSpaceCache.set(csName, csRef, parsedCS);

if (csRef) {
globalColorSpaceCache.set(/* name = */ null, csRef, parsedCS);
}
}
}

static getCached(
cacheKey,
xref,
globalColorSpaceCache,
localColorSpaceCache
) {
if (!globalColorSpaceCache || !localColorSpaceCache) {
throw new Error(
'ColorSpace.getCached - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.'
);
}
if (cacheKey instanceof Ref) {
const cachedCS =
globalColorSpaceCache.getByRef(cacheKey) ||
localColorSpaceCache.getByRef(cacheKey);
if (cachedCS) {
return cachedCS;
}

try {
cacheKey = xref.fetch(cacheKey);
} catch (ex) {
if (ex instanceof MissingDataException) {
throw ex;
}
// Any errors should be handled during parsing, rather than here.
}
}
if (cacheKey instanceof Name) {
return localColorSpaceCache.getByName(cacheKey.name) || null;
}
return null;
}

static async parseAsync({
static parse({
cs,
xref,
resources = null,
pdfFunctionFactory,
globalColorSpaceCache,
localColorSpaceCache,
asyncIfNotCached = false,
}) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
!this.getCached(cs, xref, globalColorSpaceCache, localColorSpaceCache),
"Expected `ColorSpace.getCached` to have been manually checked " +
"before calling `ColorSpace.parseAsync`."
if (
(typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) &&
(!globalColorSpaceCache || !localColorSpaceCache)
) {
unreachable(
'ColorSpace.parse - expected "globalColorSpaceCache"/"localColorSpaceCache" argument.'
);
}

const options = {
xref,
resources,
pdfFunctionFactory,
globalColorSpaceCache,
localColorSpaceCache,
};
const parsedCS = this.#parse(cs, options);
let csName, csRef, parsedCS;

// Attempt to cache the parsed ColorSpace, by name and/or reference.
this.#cache(cs, parsedCS, options);
// Check if the ColorSpace is cached first, to avoid re-parsing it.
if (cs instanceof Ref) {
csRef = cs;

return parsedCS;
}
const cachedCS =
globalColorSpaceCache.getByRef(csRef) ||
localColorSpaceCache.getByRef(csRef);
if (cachedCS) {
return cachedCS;
}
cs = xref.fetch(cs);
}
if (cs instanceof Name) {
csName = cs.name;

static parse({
cs,
xref,
resources = null,
pdfFunctionFactory,
globalColorSpaceCache,
localColorSpaceCache,
}) {
const cachedCS = this.getCached(
cs,
xref,
globalColorSpaceCache,
localColorSpaceCache
);
if (cachedCS) {
return cachedCS;
const cachedCS = localColorSpaceCache.getByName(csName);
if (cachedCS) {
return cachedCS;
}
}

const options = {
xref,
resources,
pdfFunctionFactory,
globalColorSpaceCache,
localColorSpaceCache,
};
const parsedCS = this.#parse(cs, options);
try {
parsedCS = this.#parse(cs, options);
} catch (ex) {
if (asyncIfNotCached && !(ex instanceof MissingDataException)) {
return Promise.reject(ex);
}
throw ex;
}

// Attempt to cache the parsed ColorSpace, by name and/or reference.
this.#cache(cs, parsedCS, options);
if (csName || csRef) {
localColorSpaceCache.set(csName, csRef, parsedCS);

return parsedCS;
if (csRef) {
globalColorSpaceCache.set(/* name = */ null, csRef, parsedCS);
}
}
return asyncIfNotCached ? Promise.resolve(parsedCS) : parsedCS;
}

/**
Expand All @@ -442,14 +379,16 @@ class ColorSpace {
*/
static #subParse(cs, options) {
const { globalColorSpaceCache } = options;

let csRef;

// Check if the ColorSpace is cached first, to avoid re-parsing it.
if (cs instanceof Ref) {
const cachedCS = globalColorSpaceCache.getByRef(cs);
csRef = cs;

const cachedCS = globalColorSpaceCache.getByRef(csRef);
if (cachedCS) {
return cachedCS;
}
csRef = cs;
}
const parsedCS = this.#parse(cs, options);

Expand Down
79 changes: 30 additions & 49 deletions src/core/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,23 +489,13 @@ class PartialEvaluator {
groupOptions.isolated = group.get("I") || false;
groupOptions.knockout = group.get("K") || false;
if (group.has("CS")) {
const cs = group.getRaw("CS");

const cachedColorSpace = ColorSpace.getCached(
cs,
this.xref,
this.globalColorSpaceCache,
const cs = this._getColorSpace(
group.getRaw("CS"),
resources,
localColorSpaceCache
);
if (cachedColorSpace) {
colorSpace = cachedColorSpace;
} else {
colorSpace = await this.parseColorSpace({
cs,
resources,
localColorSpaceCache,
});
}
colorSpace =
cs instanceof ColorSpace ? cs : await this._handleColorSpace(cs);
}
}

Expand Down Expand Up @@ -1462,20 +1452,25 @@ class PartialEvaluator {
}
}

parseColorSpace({ cs, resources, localColorSpaceCache }) {
return ColorSpace.parseAsync({
_getColorSpace(cs, resources, localColorSpaceCache) {
return ColorSpace.parse({
cs,
xref: this.xref,
resources,
pdfFunctionFactory: this._pdfFunctionFactory,
globalColorSpaceCache: this.globalColorSpaceCache,
localColorSpaceCache,
}).catch(reason => {
asyncIfNotCached: true,
});
}

_handleColorSpace(csPromise) {
return csPromise.catch(reason => {
if (reason instanceof AbortException) {
return null;
}
if (this.options.ignoreErrors) {
warn(`parseColorSpace - ignoring ColorSpace: "${reason}".`);
warn(`_handleColorSpace - ignoring ColorSpace: "${reason}".`);
return null;
}
throw reason;
Expand Down Expand Up @@ -1981,54 +1976,40 @@ class PartialEvaluator {
break;

case OPS.setFillColorSpace: {
const cachedColorSpace = ColorSpace.getCached(
const fillCS = self._getColorSpace(
args[0],
xref,
self.globalColorSpaceCache,
resources,
localColorSpaceCache
);
if (cachedColorSpace) {
stateManager.state.fillColorSpace = cachedColorSpace;
if (fillCS instanceof ColorSpace) {
stateManager.state.fillColorSpace = fillCS;
continue;
}

next(
self
.parseColorSpace({
cs: args[0],
resources,
localColorSpaceCache,
})
.then(function (colorSpace) {
stateManager.state.fillColorSpace =
colorSpace || ColorSpace.singletons.gray;
})
self._handleColorSpace(fillCS).then(colorSpace => {
stateManager.state.fillColorSpace =
colorSpace || ColorSpace.singletons.gray;
})
);
return;
}
case OPS.setStrokeColorSpace: {
const cachedColorSpace = ColorSpace.getCached(
const strokeCS = self._getColorSpace(
args[0],
xref,
self.globalColorSpaceCache,
resources,
localColorSpaceCache
);
if (cachedColorSpace) {
stateManager.state.strokeColorSpace = cachedColorSpace;
if (strokeCS instanceof ColorSpace) {
stateManager.state.strokeColorSpace = strokeCS;
continue;
}

next(
self
.parseColorSpace({
cs: args[0],
resources,
localColorSpaceCache,
})
.then(function (colorSpace) {
stateManager.state.strokeColorSpace =
colorSpace || ColorSpace.singletons.gray;
})
self._handleColorSpace(strokeCS).then(colorSpace => {
stateManager.state.strokeColorSpace =
colorSpace || ColorSpace.singletons.gray;
})
);
return;
}
Expand Down

0 comments on commit 93a8352

Please sign in to comment.