Merge pull request #12012 from Snuffleupagus/ColorSpace-parse-cache
Improve (local) caching of parsed `ColorSpace`s (PR 12001 follow-up)
This commit is contained in:
commit
276d917b7c
@ -22,7 +22,8 @@ import {
|
||||
unreachable,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { isDict, isName, isStream } from "./primitives.js";
|
||||
import { isDict, isName, isStream, Name, Ref } from "./primitives.js";
|
||||
import { MissingDataException } from "./core_utils.js";
|
||||
|
||||
/**
|
||||
* Resizes an RGB image with 3 components.
|
||||
@ -259,9 +260,109 @@ class ColorSpace {
|
||||
return shadow(this, "usesZeroToOneRange", true);
|
||||
}
|
||||
|
||||
static parse(cs, xref, res, pdfFunctionFactory) {
|
||||
const IR = this.parseToIR(cs, xref, res, pdfFunctionFactory);
|
||||
return this.fromIR(IR);
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
static _cache(cacheKey, xref, localColorSpaceCache, parsedColorSpace) {
|
||||
if (!localColorSpaceCache) {
|
||||
throw new Error(
|
||||
'ColorSpace._cache - expected "localColorSpaceCache" argument.'
|
||||
);
|
||||
}
|
||||
if (!parsedColorSpace) {
|
||||
throw new Error(
|
||||
'ColorSpace._cache - expected "parsedColorSpace" 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, parsedColorSpace);
|
||||
}
|
||||
}
|
||||
|
||||
static getCached(cacheKey, xref, localColorSpaceCache) {
|
||||
if (!localColorSpaceCache) {
|
||||
throw new Error(
|
||||
'ColorSpace.getCached - expected "localColorSpaceCache" argument.'
|
||||
);
|
||||
}
|
||||
if (cacheKey instanceof Ref) {
|
||||
const localColorSpace = localColorSpaceCache.getByRef(cacheKey);
|
||||
if (localColorSpace) {
|
||||
return localColorSpace;
|
||||
}
|
||||
|
||||
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) {
|
||||
const localColorSpace = localColorSpaceCache.getByName(cacheKey.name);
|
||||
if (localColorSpace) {
|
||||
return localColorSpace;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static async parseAsync({
|
||||
cs,
|
||||
xref,
|
||||
resources = null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
}) {
|
||||
if (
|
||||
typeof PDFJSDev === "undefined" ||
|
||||
PDFJSDev.test("!PRODUCTION || TESTING")
|
||||
) {
|
||||
assert(
|
||||
!this.getCached(cs, xref, localColorSpaceCache),
|
||||
"Expected `ColorSpace.getCached` to have been manually checked " +
|
||||
"before calling `ColorSpace.parseAsync`."
|
||||
);
|
||||
}
|
||||
const IR = this.parseToIR(cs, xref, resources, pdfFunctionFactory);
|
||||
const parsedColorSpace = this.fromIR(IR);
|
||||
|
||||
// Attempt to cache the parsed ColorSpace, by name and/or reference.
|
||||
this._cache(cs, xref, localColorSpaceCache, parsedColorSpace);
|
||||
|
||||
return parsedColorSpace;
|
||||
}
|
||||
|
||||
static parse({
|
||||
cs,
|
||||
xref,
|
||||
resources = null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
}) {
|
||||
const cachedColorSpace = this.getCached(cs, xref, localColorSpaceCache);
|
||||
if (cachedColorSpace) {
|
||||
return cachedColorSpace;
|
||||
}
|
||||
const IR = this.parseToIR(cs, xref, resources, pdfFunctionFactory);
|
||||
const parsedColorSpace = this.fromIR(IR);
|
||||
|
||||
// Attempt to cache the parsed ColorSpace, by name and/or reference.
|
||||
this._cache(cs, xref, localColorSpaceCache, parsedColorSpace);
|
||||
|
||||
return parsedColorSpace;
|
||||
}
|
||||
|
||||
static fromIR(IR) {
|
||||
@ -312,7 +413,7 @@ class ColorSpace {
|
||||
}
|
||||
}
|
||||
|
||||
static parseToIR(cs, xref, res = null, pdfFunctionFactory) {
|
||||
static parseToIR(cs, xref, resources = null, pdfFunctionFactory) {
|
||||
cs = xref.fetchIfRef(cs);
|
||||
if (isName(cs)) {
|
||||
switch (cs.name) {
|
||||
@ -328,15 +429,20 @@ class ColorSpace {
|
||||
case "Pattern":
|
||||
return ["PatternCS", null];
|
||||
default:
|
||||
if (isDict(res)) {
|
||||
const colorSpaces = res.get("ColorSpace");
|
||||
if (isDict(resources)) {
|
||||
const colorSpaces = resources.get("ColorSpace");
|
||||
if (isDict(colorSpaces)) {
|
||||
const resCS = colorSpaces.get(cs.name);
|
||||
if (resCS) {
|
||||
if (isName(resCS)) {
|
||||
return this.parseToIR(resCS, xref, res, pdfFunctionFactory);
|
||||
const resourcesCS = colorSpaces.get(cs.name);
|
||||
if (resourcesCS) {
|
||||
if (isName(resourcesCS)) {
|
||||
return this.parseToIR(
|
||||
resourcesCS,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory
|
||||
);
|
||||
}
|
||||
cs = resCS;
|
||||
cs = resourcesCS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -377,10 +483,15 @@ class ColorSpace {
|
||||
numComps = dict.get("N");
|
||||
alt = dict.get("Alternate");
|
||||
if (alt) {
|
||||
const altIR = this.parseToIR(alt, xref, res, pdfFunctionFactory);
|
||||
const altIR = this.parseToIR(
|
||||
alt,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory
|
||||
);
|
||||
// Parse the /Alternate CS to ensure that the number of components
|
||||
// are correct, and also (indirectly) that it is not a PatternCS.
|
||||
const altCS = this.fromIR(altIR, pdfFunctionFactory);
|
||||
const altCS = this.fromIR(altIR);
|
||||
if (altCS.numComps === numComps) {
|
||||
return altIR;
|
||||
}
|
||||
@ -400,7 +511,7 @@ class ColorSpace {
|
||||
basePatternCS = this.parseToIR(
|
||||
basePatternCS,
|
||||
xref,
|
||||
res,
|
||||
resources,
|
||||
pdfFunctionFactory
|
||||
);
|
||||
}
|
||||
@ -410,7 +521,7 @@ class ColorSpace {
|
||||
const baseIndexedCS = this.parseToIR(
|
||||
cs[1],
|
||||
xref,
|
||||
res,
|
||||
resources,
|
||||
pdfFunctionFactory
|
||||
);
|
||||
const hiVal = xref.fetchIfRef(cs[2]) + 1;
|
||||
@ -423,7 +534,7 @@ class ColorSpace {
|
||||
case "DeviceN":
|
||||
const name = xref.fetchIfRef(cs[1]);
|
||||
numComps = Array.isArray(name) ? name.length : 1;
|
||||
alt = this.parseToIR(cs[2], xref, res, pdfFunctionFactory);
|
||||
alt = this.parseToIR(cs[2], xref, resources, pdfFunctionFactory);
|
||||
const tintFn = pdfFunctionFactory.create(xref.fetchIfRef(cs[3]));
|
||||
return ["AlternateCS", numComps, alt, tintFn];
|
||||
case "Lab":
|
||||
|
@ -73,13 +73,13 @@ import {
|
||||
} from "./standard_fonts.js";
|
||||
import { getTilingPatternIR, Pattern } from "./pattern.js";
|
||||
import { Lexer, Parser } from "./parser.js";
|
||||
import { LocalColorSpaceCache, LocalImageCache } from "./image_utils.js";
|
||||
import { bidi } from "./bidi.js";
|
||||
import { ColorSpace } from "./colorspace.js";
|
||||
import { DecodeStream } from "./stream.js";
|
||||
import { getGlyphsUnicode } from "./glyphlist.js";
|
||||
import { getMetrics } from "./metrics.js";
|
||||
import { isPDFFunction } from "./function.js";
|
||||
import { LocalImageCache } from "./image_utils.js";
|
||||
import { MurmurHash3_64 } from "./murmurhash3.js";
|
||||
import { OperatorList } from "./operator_list.js";
|
||||
import { PDFImage } from "./image.js";
|
||||
@ -411,12 +411,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
groupOptions.isolated = group.get("I") || false;
|
||||
groupOptions.knockout = group.get("K") || false;
|
||||
if (group.has("CS")) {
|
||||
const cs = group.get("CS");
|
||||
const cs = group.getRaw("CS");
|
||||
|
||||
const localColorSpace =
|
||||
cs instanceof Name && localColorSpaceCache.getByName(cs.name);
|
||||
if (localColorSpace) {
|
||||
colorSpace = localColorSpace;
|
||||
const cachedColorSpace = ColorSpace.getCached(
|
||||
cs,
|
||||
this.xref,
|
||||
localColorSpaceCache
|
||||
);
|
||||
if (cachedColorSpace) {
|
||||
colorSpace = cachedColorSpace;
|
||||
} else {
|
||||
colorSpace = await this.parseColorSpace({
|
||||
cs,
|
||||
@ -483,6 +486,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
operatorList,
|
||||
cacheKey,
|
||||
localImageCache,
|
||||
localColorSpaceCache,
|
||||
}) {
|
||||
var dict = image.dict;
|
||||
const imageRef = dict.objId;
|
||||
@ -549,6 +553,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
image,
|
||||
isInline,
|
||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
// We force the use of RGBA_32BPP images here, because we can't handle
|
||||
// any other kind.
|
||||
@ -585,6 +590,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
image,
|
||||
isInline,
|
||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
})
|
||||
.then(imageObj => {
|
||||
imgData = imageObj.createImageData(/* forceRGBA = */ false);
|
||||
@ -1135,19 +1141,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
},
|
||||
|
||||
parseColorSpace({ cs, resources, localColorSpaceCache }) {
|
||||
return new Promise(resolve => {
|
||||
const parsedColorSpace = ColorSpace.parse(
|
||||
return ColorSpace.parseAsync({
|
||||
cs,
|
||||
this.xref,
|
||||
xref: this.xref,
|
||||
resources,
|
||||
this.pdfFunctionFactory
|
||||
);
|
||||
|
||||
const csName = cs instanceof Name ? cs.name : null;
|
||||
if (csName) {
|
||||
localColorSpaceCache.set(csName, /* ref = */ null, parsedColorSpace);
|
||||
}
|
||||
resolve(parsedColorSpace);
|
||||
pdfFunctionFactory: this.pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
}).catch(reason => {
|
||||
if (reason instanceof AbortException) {
|
||||
return null;
|
||||
@ -1165,7 +1164,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
});
|
||||
},
|
||||
|
||||
async handleColorN(operatorList, fn, args, cs, patterns, resources, task) {
|
||||
async handleColorN(
|
||||
operatorList,
|
||||
fn,
|
||||
args,
|
||||
cs,
|
||||
patterns,
|
||||
resources,
|
||||
task,
|
||||
localColorSpaceCache
|
||||
) {
|
||||
// compile tiling patterns
|
||||
var patternName = args[args.length - 1];
|
||||
// SCN/scn applies patterns along with normal colors
|
||||
@ -1194,7 +1202,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
this.xref,
|
||||
resources,
|
||||
this.handler,
|
||||
this.pdfFunctionFactory
|
||||
this.pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
);
|
||||
operatorList.addOp(fn, pattern.getIR());
|
||||
return undefined;
|
||||
@ -1224,7 +1233,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
var xref = this.xref;
|
||||
let parsingText = false;
|
||||
const localImageCache = new LocalImageCache();
|
||||
const localColorSpaceCache = new LocalImageCache();
|
||||
const localColorSpaceCache = new LocalColorSpaceCache();
|
||||
|
||||
var xobjs = resources.get("XObject") || Dict.empty;
|
||||
var patterns = resources.get("Pattern") || Dict.empty;
|
||||
@ -1352,6 +1361,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
operatorList,
|
||||
cacheKey: name,
|
||||
localImageCache,
|
||||
localColorSpaceCache,
|
||||
})
|
||||
.then(resolveXObject, rejectXObject);
|
||||
return;
|
||||
@ -1425,6 +1435,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
operatorList,
|
||||
cacheKey,
|
||||
localImageCache,
|
||||
localColorSpaceCache,
|
||||
})
|
||||
);
|
||||
return;
|
||||
@ -1483,11 +1494,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
break;
|
||||
|
||||
case OPS.setFillColorSpace: {
|
||||
const localColorSpace =
|
||||
args[0] instanceof Name &&
|
||||
localColorSpaceCache.getByName(args[0].name);
|
||||
if (localColorSpace) {
|
||||
stateManager.state.fillColorSpace = localColorSpace;
|
||||
const cachedColorSpace = ColorSpace.getCached(
|
||||
args[0],
|
||||
xref,
|
||||
localColorSpaceCache
|
||||
);
|
||||
if (cachedColorSpace) {
|
||||
stateManager.state.fillColorSpace = cachedColorSpace;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1507,11 +1520,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
return;
|
||||
}
|
||||
case OPS.setStrokeColorSpace: {
|
||||
const localColorSpace =
|
||||
args[0] instanceof Name &&
|
||||
localColorSpaceCache.getByName(args[0].name);
|
||||
if (localColorSpace) {
|
||||
stateManager.state.strokeColorSpace = localColorSpace;
|
||||
const cachedColorSpace = ColorSpace.getCached(
|
||||
args[0],
|
||||
xref,
|
||||
localColorSpaceCache
|
||||
);
|
||||
if (cachedColorSpace) {
|
||||
stateManager.state.strokeColorSpace = cachedColorSpace;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1579,7 +1594,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
cs,
|
||||
patterns,
|
||||
resources,
|
||||
task
|
||||
task,
|
||||
localColorSpaceCache
|
||||
)
|
||||
);
|
||||
return;
|
||||
@ -1598,7 +1614,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
cs,
|
||||
patterns,
|
||||
resources,
|
||||
task
|
||||
task,
|
||||
localColorSpaceCache
|
||||
)
|
||||
);
|
||||
return;
|
||||
@ -1624,7 +1641,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
xref,
|
||||
resources,
|
||||
self.handler,
|
||||
self.pdfFunctionFactory
|
||||
self.pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
);
|
||||
var patternIR = shadingFill.getIR();
|
||||
args = [patternIR];
|
||||
|
@ -89,6 +89,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
mask = null,
|
||||
isMask = false,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
}) {
|
||||
this.image = image;
|
||||
var dict = image.dict;
|
||||
@ -159,7 +160,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
this.bpc = bitsPerComponent;
|
||||
|
||||
if (!this.imageMask) {
|
||||
var colorSpace = dict.get("ColorSpace", "CS");
|
||||
let colorSpace = dict.getRaw("ColorSpace") || dict.getRaw("CS");
|
||||
if (!colorSpace) {
|
||||
info("JPX images (which do not require color spaces)");
|
||||
switch (image.numComps) {
|
||||
@ -179,13 +180,13 @@ var PDFImage = (function PDFImageClosure() {
|
||||
);
|
||||
}
|
||||
}
|
||||
const resources = isInline ? res : null;
|
||||
this.colorSpace = ColorSpace.parse(
|
||||
colorSpace,
|
||||
this.colorSpace = ColorSpace.parse({
|
||||
cs: colorSpace,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory
|
||||
);
|
||||
resources: isInline ? res : null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
this.numComps = this.colorSpace.numComps;
|
||||
}
|
||||
|
||||
@ -221,6 +222,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
image: smask,
|
||||
isInline,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
} else if (mask) {
|
||||
if (isStream(mask)) {
|
||||
@ -236,6 +238,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
isInline,
|
||||
isMask: true,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -254,6 +257,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
image,
|
||||
isInline = false,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
}) {
|
||||
const imageData = image;
|
||||
let smaskData = null;
|
||||
@ -280,6 +284,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
smask: smaskData,
|
||||
mask: maskData,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -14,11 +14,14 @@
|
||||
*/
|
||||
/* eslint no-var: error */
|
||||
|
||||
import { assert, info, shadow } from "../shared/util.js";
|
||||
import { assert, info, shadow, unreachable } from "../shared/util.js";
|
||||
import { RefSetCache } from "./primitives.js";
|
||||
|
||||
class LocalImageCache {
|
||||
class BaseLocalCache {
|
||||
constructor() {
|
||||
if (this.constructor === BaseLocalCache) {
|
||||
unreachable("Cannot initialize BaseLocalCache.");
|
||||
}
|
||||
this._nameRefMap = new Map();
|
||||
this._imageMap = new Map();
|
||||
this._imageCache = new RefSetCache();
|
||||
@ -36,6 +39,12 @@ class LocalImageCache {
|
||||
return this._imageCache.get(ref) || null;
|
||||
}
|
||||
|
||||
set(name, ref, data) {
|
||||
unreachable("Abstract method `set` called.");
|
||||
}
|
||||
}
|
||||
|
||||
class LocalImageCache extends BaseLocalCache {
|
||||
set(name, ref = null, data) {
|
||||
if (!name) {
|
||||
throw new Error('LocalImageCache.set - expected "name" argument.');
|
||||
@ -56,6 +65,32 @@ class LocalImageCache {
|
||||
}
|
||||
}
|
||||
|
||||
class LocalColorSpaceCache extends BaseLocalCache {
|
||||
set(name = null, ref = null, data) {
|
||||
if (!name && !ref) {
|
||||
throw new Error(
|
||||
'LocalColorSpaceCache.set - expected "name" and/or "ref" argument.'
|
||||
);
|
||||
}
|
||||
if (ref) {
|
||||
if (this._imageCache.has(ref)) {
|
||||
return;
|
||||
}
|
||||
if (name) {
|
||||
// Optional when `ref` is defined.
|
||||
this._nameRefMap.set(name, ref);
|
||||
}
|
||||
this._imageCache.put(ref, data);
|
||||
return;
|
||||
}
|
||||
// name
|
||||
if (this._imageMap.has(name)) {
|
||||
return;
|
||||
}
|
||||
this._imageMap.set(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalImageCache {
|
||||
static get NUM_PAGES_THRESHOLD() {
|
||||
return shadow(this, "NUM_PAGES_THRESHOLD", 2);
|
||||
@ -149,4 +184,4 @@ class GlobalImageCache {
|
||||
}
|
||||
}
|
||||
|
||||
export { LocalImageCache, GlobalImageCache };
|
||||
export { LocalImageCache, LocalColorSpaceCache, GlobalImageCache };
|
||||
|
@ -57,7 +57,8 @@ var Pattern = (function PatternClosure() {
|
||||
xref,
|
||||
res,
|
||||
handler,
|
||||
pdfFunctionFactory
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
) {
|
||||
var dict = isStream(shading) ? shading.dict : shading;
|
||||
var type = dict.get("ShadingType");
|
||||
@ -72,7 +73,8 @@ var Pattern = (function PatternClosure() {
|
||||
matrix,
|
||||
xref,
|
||||
res,
|
||||
pdfFunctionFactory
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
);
|
||||
case ShadingType.FREE_FORM_MESH:
|
||||
case ShadingType.LATTICE_FORM_MESH:
|
||||
@ -83,7 +85,8 @@ var Pattern = (function PatternClosure() {
|
||||
matrix,
|
||||
xref,
|
||||
res,
|
||||
pdfFunctionFactory
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
);
|
||||
default:
|
||||
throw new FormatError("Unsupported ShadingType: " + type);
|
||||
@ -111,13 +114,25 @@ Shadings.SMALL_NUMBER = 1e-6;
|
||||
// Radial and axial shading have very similar implementations
|
||||
// If needed, the implementations can be broken into two classes
|
||||
Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||
function RadialAxial(dict, matrix, xref, res, pdfFunctionFactory) {
|
||||
function RadialAxial(
|
||||
dict,
|
||||
matrix,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
) {
|
||||
this.matrix = matrix;
|
||||
this.coordsArr = dict.getArray("Coords");
|
||||
this.shadingType = dict.get("ShadingType");
|
||||
this.type = "Pattern";
|
||||
var cs = dict.get("ColorSpace", "CS");
|
||||
cs = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const cs = ColorSpace.parse({
|
||||
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
this.cs = cs;
|
||||
const bbox = dict.getArray("BBox");
|
||||
if (Array.isArray(bbox) && bbox.length === 4) {
|
||||
@ -830,7 +845,14 @@ Shadings.Mesh = (function MeshClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
function Mesh(stream, matrix, xref, res, pdfFunctionFactory) {
|
||||
function Mesh(
|
||||
stream,
|
||||
matrix,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache
|
||||
) {
|
||||
if (!isStream(stream)) {
|
||||
throw new FormatError("Mesh data is not a stream");
|
||||
}
|
||||
@ -844,8 +866,13 @@ Shadings.Mesh = (function MeshClosure() {
|
||||
} else {
|
||||
this.bbox = null;
|
||||
}
|
||||
var cs = dict.get("ColorSpace", "CS");
|
||||
cs = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const cs = ColorSpace.parse({
|
||||
cs: dict.getRaw("ColorSpace") || dict.getRaw("CS"),
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
this.cs = cs;
|
||||
this.background = dict.has("Background")
|
||||
? cs.getRgb(dict.get("Background"), 0)
|
||||
|
@ -16,18 +16,21 @@
|
||||
import { Dict, Name, Ref } from "../../src/core/primitives.js";
|
||||
import { Stream, StringStream } from "../../src/core/stream.js";
|
||||
import { ColorSpace } from "../../src/core/colorspace.js";
|
||||
import { LocalColorSpaceCache } from "../../src/core/image_utils.js";
|
||||
import { PDFFunctionFactory } from "../../src/core/function.js";
|
||||
import { XRefMock } from "./test_utils.js";
|
||||
|
||||
describe("colorspace", function () {
|
||||
describe("ColorSpace", function () {
|
||||
describe("ColorSpace.isDefaultDecode", function () {
|
||||
it("should be true if decode is not an array", function () {
|
||||
expect(ColorSpace.isDefaultDecode("string", 0)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should be true if length of decode array is not correct", function () {
|
||||
expect(ColorSpace.isDefaultDecode([0], 1)).toBeTruthy();
|
||||
expect(ColorSpace.isDefaultDecode([0, 1, 0], 1)).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should be true if decode map matches the default decode map", function () {
|
||||
expect(ColorSpace.isDefaultDecode([], 0)).toBeTruthy();
|
||||
|
||||
@ -46,6 +49,138 @@ describe("colorspace", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ColorSpace caching", function () {
|
||||
let localColorSpaceCache = null;
|
||||
|
||||
beforeAll(function (done) {
|
||||
localColorSpaceCache = new LocalColorSpaceCache();
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function (done) {
|
||||
localColorSpaceCache = null;
|
||||
done();
|
||||
});
|
||||
|
||||
it("caching by Name", function () {
|
||||
const xref = new XRefMock();
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
|
||||
const colorSpace1 = ColorSpace.parse({
|
||||
cs: Name.get("Pattern"),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpace1.name).toEqual("Pattern");
|
||||
|
||||
const colorSpace2 = ColorSpace.parse({
|
||||
cs: Name.get("Pattern"),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpace2.name).toEqual("Pattern");
|
||||
|
||||
const colorSpaceNonCached = ColorSpace.parse({
|
||||
cs: Name.get("Pattern"),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
expect(colorSpaceNonCached.name).toEqual("Pattern");
|
||||
|
||||
const colorSpaceOther = ColorSpace.parse({
|
||||
cs: Name.get("RGB"),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpaceOther.name).toEqual("DeviceRGB");
|
||||
|
||||
// These two must be *identical* if caching worked as intended.
|
||||
expect(colorSpace1).toBe(colorSpace2);
|
||||
|
||||
expect(colorSpace1).not.toBe(colorSpaceNonCached);
|
||||
expect(colorSpace1).not.toBe(colorSpaceOther);
|
||||
});
|
||||
|
||||
it("caching by Ref", function () {
|
||||
const paramsCalGray = new Dict();
|
||||
paramsCalGray.set("WhitePoint", [1, 1, 1]);
|
||||
paramsCalGray.set("BlackPoint", [0, 0, 0]);
|
||||
paramsCalGray.set("Gamma", 2.0);
|
||||
|
||||
const paramsCalRGB = new Dict();
|
||||
paramsCalRGB.set("WhitePoint", [1, 1, 1]);
|
||||
paramsCalRGB.set("BlackPoint", [0, 0, 0]);
|
||||
paramsCalRGB.set("Gamma", [1, 1, 1]);
|
||||
paramsCalRGB.set("Matrix", [1, 0, 0, 0, 1, 0, 0, 0, 1]);
|
||||
|
||||
const xref = new XRefMock([
|
||||
{
|
||||
ref: Ref.get(50, 0),
|
||||
data: [Name.get("CalGray"), paramsCalGray],
|
||||
},
|
||||
{
|
||||
ref: Ref.get(100, 0),
|
||||
data: [Name.get("CalRGB"), paramsCalRGB],
|
||||
},
|
||||
]);
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
|
||||
const colorSpace1 = ColorSpace.parse({
|
||||
cs: Ref.get(50, 0),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpace1.name).toEqual("CalGray");
|
||||
|
||||
const colorSpace2 = ColorSpace.parse({
|
||||
cs: Ref.get(50, 0),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpace2.name).toEqual("CalGray");
|
||||
|
||||
const colorSpaceNonCached = ColorSpace.parse({
|
||||
cs: Ref.get(50, 0),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
expect(colorSpaceNonCached.name).toEqual("CalGray");
|
||||
|
||||
const colorSpaceOther = ColorSpace.parse({
|
||||
cs: Ref.get(100, 0),
|
||||
xref,
|
||||
resources: null,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache,
|
||||
});
|
||||
expect(colorSpaceOther.name).toEqual("CalRGB");
|
||||
|
||||
// These two must be *identical* if caching worked as intended.
|
||||
expect(colorSpace1).toBe(colorSpace2);
|
||||
|
||||
expect(colorSpace1).not.toBe(colorSpaceNonCached);
|
||||
expect(colorSpace1).not.toBe(colorSpaceOther);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DeviceGrayCS", function () {
|
||||
it("should handle the case when cs is a Name object", function () {
|
||||
const cs = Name.get("DeviceGray");
|
||||
@ -55,12 +190,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
const testSrc = new Uint8Array([27, 125, 250, 131]);
|
||||
const testDest = new Uint8ClampedArray(4 * 4 * 3);
|
||||
@ -100,12 +241,18 @@ describe("colorspace", function () {
|
||||
data: Name.get("DeviceGray"),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
const testSrc = new Uint8Array([27, 125, 250, 131]);
|
||||
const testDest = new Uint8ClampedArray(3 * 3 * 3);
|
||||
@ -141,12 +288,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -192,12 +345,18 @@ describe("colorspace", function () {
|
||||
data: Name.get("DeviceRGB"),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -239,12 +398,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -290,12 +455,18 @@ describe("colorspace", function () {
|
||||
data: Name.get("DeviceCMYK"),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -342,12 +513,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
const testSrc = new Uint8Array([27, 125, 250, 131]);
|
||||
const testDest = new Uint8ClampedArray(4 * 4 * 3);
|
||||
@ -396,12 +573,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -448,12 +631,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
// prettier-ignore
|
||||
const testSrc = new Uint8Array([
|
||||
@ -502,12 +691,18 @@ describe("colorspace", function () {
|
||||
data: new Dict(),
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
const testSrc = new Uint8Array([2, 2, 0, 1]);
|
||||
const testDest = new Uint8ClampedArray(3 * 3 * 3);
|
||||
@ -564,12 +759,18 @@ describe("colorspace", function () {
|
||||
data: fn,
|
||||
},
|
||||
]);
|
||||
const res = new Dict();
|
||||
const resources = new Dict();
|
||||
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref,
|
||||
});
|
||||
const colorSpace = ColorSpace.parse(cs, xref, res, pdfFunctionFactory);
|
||||
const colorSpace = ColorSpace.parse({
|
||||
cs,
|
||||
xref,
|
||||
resources,
|
||||
pdfFunctionFactory,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
const testSrc = new Uint8Array([27, 25, 50, 31]);
|
||||
const testDest = new Uint8ClampedArray(3 * 3 * 3);
|
||||
|
Loading…
Reference in New Issue
Block a user