From 2019c43ea5463d57dcf93089acf001978e99869d Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Thu, 10 Apr 2025 12:35:03 +0200 Subject: [PATCH] Support /DefaultGray, /DefaultRGB, and /DefaultCMYK color spaces Currently we don't support default color spaces at all, despite that having been included in the PDF specification for a long time; see https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf#G7.1852999 Please also refer to https://github.com/pdf-association/pdf-differences/tree/main/DefaultColorSpaces --- src/core/colorspace_utils.js | 99 ++++++-- src/core/default_appearance.js | 40 ++-- src/core/evaluator.js | 224 +++++++++++++++---- test/pdfs/.gitignore | 3 + test/pdfs/DefaultColourSpaces-230802.pdf | Bin 0 -> 13360 bytes test/pdfs/DefaultRGBColourSpaces-inherit.pdf | Bin 0 -> 5645 bytes test/pdfs/DefaultRGBColourSpaces.pdf | Bin 0 -> 5645 bytes test/test_manifest.json | 24 ++ test/unit/annotation_spec.js | 2 + 9 files changed, 318 insertions(+), 74 deletions(-) create mode 100644 test/pdfs/DefaultColourSpaces-230802.pdf create mode 100644 test/pdfs/DefaultRGBColourSpaces-inherit.pdf create mode 100644 test/pdfs/DefaultRGBColourSpaces.pdf diff --git a/src/core/colorspace_utils.js b/src/core/colorspace_utils.js index 04fa44dd837dc..39bb5b8cb5bde 100644 --- a/src/core/colorspace_utils.js +++ b/src/core/colorspace_utils.js @@ -79,7 +79,7 @@ class ColorSpaceUtils { } try { - parsedCS = this.#parse(cs, options); + parsedCS = this.#parse(cs, options, /* topLevel = */ true); } catch (ex) { if (asyncIfNotCached && !(ex instanceof MissingDataException)) { return Promise.reject(ex); @@ -124,39 +124,92 @@ class ColorSpaceUtils { return parsedCS; } - static #parse(cs, options) { - const { xref, resources, pdfFunctionFactory, globalColorSpaceCache } = - options; + /** + * NOTE: This method should *only* be invoked from `this.#parse`, + * when parsing "default" ColorSpaces (i.e. /DefaultGray, /DefaultRGB, + * and /DefaultCMYK). + */ + static #defaultParse(cs, options, deviceCS) { + const { globalColorSpaceCache } = options; + let csRef, parsedCS; + + // Check if the ColorSpace is cached first, to avoid re-parsing it. + if (cs instanceof Ref) { + csRef = cs; + + const cachedCS = globalColorSpaceCache.getByRef(csRef); + if (cachedCS) { + return cachedCS; + } + } + try { + parsedCS = this.#parse(cs, options); + } catch (ex) { + if (ex instanceof MissingDataException) { + throw ex; + } + warn(`Cannot parse default ColorSpace: "${ex}".`); + return deviceCS; + } + + // The default ColorSpace must be compatible with the original one. + if (parsedCS.numComps !== deviceCS.numComps) { + warn( + "Incorrect number of components in default ColorSpace, " + + `expected "${deviceCS.numComps}" and got "${parsedCS.numComps}".` + ); + return deviceCS; + } + + // Only cache the parsed ColorSpace globally, by reference. + if (csRef) { + globalColorSpaceCache.set(/* name = */ null, csRef, parsedCS); + } + return parsedCS; + } + + static #parse(cs, options, topLevel = false) { + const { xref, pdfFunctionFactory, globalColorSpaceCache } = options; cs = xref.fetchIfRef(cs); if (cs instanceof Name) { switch (cs.name) { case "G": - case "DeviceGray": + case "DeviceGray": { + const defaultCS = topLevel && this.#getResCS("DefaultGray", options); + if (defaultCS) { + return this.#defaultParse(defaultCS, options, this.gray); + } return this.gray; + } case "RGB": - case "DeviceRGB": + case "DeviceRGB": { + const defaultCS = topLevel && this.#getResCS("DefaultRGB", options); + if (defaultCS) { + return this.#defaultParse(defaultCS, options, this.rgb); + } return this.rgb; + } case "DeviceRGBA": return this.rgba; case "CMYK": - case "DeviceCMYK": + case "DeviceCMYK": { + const defaultCS = topLevel && this.#getResCS("DefaultCMYK", options); + if (defaultCS) { + return this.#subParse(defaultCS, options, this.cmyk); + } return this.cmyk; + } case "Pattern": return new PatternCS(/* baseCS = */ null); default: - if (resources instanceof Dict) { - const colorSpaces = resources.get("ColorSpace"); - if (colorSpaces instanceof Dict) { - const resourcesCS = colorSpaces.get(cs.name); - if (resourcesCS) { - if (resourcesCS instanceof Name) { - return this.#parse(resourcesCS, options); - } - cs = resourcesCS; - break; - } + const resourcesCS = xref.fetchIfRef(this.#getResCS(cs.name, options)); + if (resourcesCS) { + if (resourcesCS instanceof Name) { + return this.#parse(resourcesCS, options); } + cs = resourcesCS; + break; } // Fallback to the default gray color space. warn(`Unrecognized ColorSpace: ${cs.name}`); @@ -276,6 +329,16 @@ class ColorSpaceUtils { return this.gray; } + static #getResCS(name, { resources }) { + if (resources instanceof Dict) { + const colorSpaces = resources.get("ColorSpace"); + if (colorSpaces instanceof Dict) { + return colorSpaces.getRaw(name) ?? null; + } + } + return null; + } + static get gray() { return shadow(this, "gray", new DeviceGrayCS()); } diff --git a/src/core/default_appearance.js b/src/core/default_appearance.js index b05ac5065647d..30a74231e2530 100644 --- a/src/core/default_appearance.js +++ b/src/core/default_appearance.js @@ -119,7 +119,8 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { fontColor: /* black = */ new Uint8ClampedArray(3), fillColorSpace: ColorSpaceUtils.gray, }; - let breakLoop = false; + let breakLoop = false, + cs = null; const stack = []; try { @@ -157,27 +158,29 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { } break; case OPS.setFillColorSpace: - result.fillColorSpace = ColorSpaceUtils.parse({ - cs: args[0], - xref: this.xref, - resources: this.resources, - pdfFunctionFactory: this._pdfFunctionFactory, - globalColorSpaceCache: this.globalColorSpaceCache, - localColorSpaceCache: this._localColorSpaceCache, - }); + result.fillColorSpace = this.#parseColorSpace(args[0]); break; case OPS.setFillColor: - const cs = result.fillColorSpace; + cs = result.fillColorSpace; cs.getRgbItem(args, 0, result.fontColor, 0); break; case OPS.setFillRGBColor: - ColorSpaceUtils.rgb.getRgbItem(args, 0, result.fontColor, 0); + cs = result.fillColorSpace = this.#parseColorSpace( + Name.get("DeviceRGB") + ); + cs.getRgbItem(args, 0, result.fontColor, 0); break; case OPS.setFillGray: - ColorSpaceUtils.gray.getRgbItem(args, 0, result.fontColor, 0); + cs = result.fillColorSpace = this.#parseColorSpace( + Name.get("DeviceGray") + ); + cs.getRgbItem(args, 0, result.fontColor, 0); break; case OPS.setFillCMYKColor: - ColorSpaceUtils.cmyk.getRgbItem(args, 0, result.fontColor, 0); + cs = result.fillColorSpace = this.#parseColorSpace( + Name.get("DeviceCMYK") + ); + cs.getRgbItem(args, 0, result.fontColor, 0); break; case OPS.showText: case OPS.showSpacedText: @@ -208,6 +211,17 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor { }); return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory); } + + #parseColorSpace(cs) { + return ColorSpaceUtils.parse({ + cs, + xref: this.xref, + resources: this.resources, + pdfFunctionFactory: this._pdfFunctionFactory, + globalColorSpaceCache: this.globalColorSpaceCache, + localColorSpaceCache: this._localColorSpaceCache, + }); + } } // Parse appearance stream to extract font and color information. diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 3b1cb549f9a4d..50b14c3220a09 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -1449,16 +1449,16 @@ class PartialEvaluator { }); } - async _handleColorSpace(csPromise) { + async _handleColorSpace(csPromise, name = null) { try { return await csPromise; } catch (ex) { if (ex instanceof AbortException) { - return null; + return name ? ColorSpaceUtils[name] : null; } if (this.options.ignoreErrors) { warn(`_handleColorSpace - ignoring ColorSpace: "${ex}".`); - return null; + return name ? ColorSpaceUtils[name] : null; } throw ex; } @@ -1951,9 +1951,8 @@ class PartialEvaluator { } next( - self._handleColorSpace(fillCS).then(colorSpace => { - stateManager.state.fillColorSpace = - colorSpace || ColorSpaceUtils.gray; + self._handleColorSpace(fillCS, "gray").then(CS => { + stateManager.state.fillColorSpace = CS; }) ); return; @@ -1970,9 +1969,8 @@ class PartialEvaluator { } next( - self._handleColorSpace(strokeCS).then(colorSpace => { - stateManager.state.strokeColorSpace = - colorSpace || ColorSpaceUtils.gray; + self._handleColorSpace(strokeCS, "gray").then(CS => { + stateManager.state.strokeColorSpace = CS; }) ); return; @@ -1987,41 +1985,164 @@ class PartialEvaluator { args = cs.getRgb(args, 0); fn = OPS.setStrokeRGBColor; break; - case OPS.setFillGray: - stateManager.state.fillColorSpace = ColorSpaceUtils.gray; - args = ColorSpaceUtils.gray.getRgb(args, 0); - fn = OPS.setFillRGBColor; - break; - case OPS.setStrokeGray: - stateManager.state.strokeColorSpace = ColorSpaceUtils.gray; - args = ColorSpaceUtils.gray.getRgb(args, 0); - fn = OPS.setStrokeRGBColor; - break; - case OPS.setFillCMYKColor: - stateManager.state.fillColorSpace = ColorSpaceUtils.cmyk; - args = ColorSpaceUtils.cmyk.getRgb(args, 0); - fn = OPS.setFillRGBColor; - break; - case OPS.setStrokeCMYKColor: - stateManager.state.strokeColorSpace = ColorSpaceUtils.cmyk; - args = ColorSpaceUtils.cmyk.getRgb(args, 0); - fn = OPS.setStrokeRGBColor; - break; - case OPS.setFillRGBColor: - stateManager.state.fillColorSpace = ColorSpaceUtils.rgb; - args = ColorSpaceUtils.rgb.getRgb(args, 0); - break; - case OPS.setStrokeRGBColor: - stateManager.state.strokeColorSpace = ColorSpaceUtils.rgb; - args = ColorSpaceUtils.rgb.getRgb(args, 0); - break; + case OPS.setFillGray: { + const fillCS = self._getColorSpace( + Name.get("DeviceGray"), + resources, + localColorSpaceCache + ); + if (fillCS instanceof ColorSpace) { + stateManager.state.fillColorSpace = fillCS; + + args = fillCS.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + } + + next( + self._handleColorSpace(fillCS, "gray").then(CS => { + stateManager.state.fillColorSpace = CS; + + operatorList.addOp(OPS.setFillRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } + case OPS.setStrokeGray: { + const strokeCS = self._getColorSpace( + Name.get("DeviceGray"), + resources, + localColorSpaceCache + ); + if (strokeCS instanceof ColorSpace) { + stateManager.state.strokeColorSpace = strokeCS; + + args = strokeCS.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + } + + next( + self._handleColorSpace(strokeCS, "gray").then(CS => { + stateManager.state.strokeColorSpace = CS; + + operatorList.addOp(OPS.setStrokeRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } + case OPS.setFillCMYKColor: { + const fillCS = self._getColorSpace( + Name.get("DeviceCMYK"), + resources, + localColorSpaceCache + ); + if (fillCS instanceof ColorSpace) { + stateManager.state.fillColorSpace = fillCS; + + args = fillCS.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + } + + next( + self._handleColorSpace(fillCS, "cmyk").then(CS => { + stateManager.state.fillColorSpace = CS; + + operatorList.addOp(OPS.setFillRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } + case OPS.setStrokeCMYKColor: { + const strokeCS = self._getColorSpace( + Name.get("DeviceCMYK"), + resources, + localColorSpaceCache + ); + if (strokeCS instanceof ColorSpace) { + stateManager.state.strokeColorSpace = strokeCS; + + args = strokeCS.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + } + + next( + self._handleColorSpace(strokeCS, "cmyk").then(CS => { + stateManager.state.strokeColorSpace = CS; + + operatorList.addOp(OPS.setStrokeRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } + case OPS.setFillRGBColor: { + const fillCS = self._getColorSpace( + Name.get("DeviceRGB"), + resources, + localColorSpaceCache + ); + if (fillCS instanceof ColorSpace) { + stateManager.state.fillColorSpace = fillCS; + + args = fillCS.getRgb(args, 0); + break; + } + + next( + self._handleColorSpace(fillCS, "rgb").then(CS => { + stateManager.state.fillColorSpace = CS; + + operatorList.addOp(OPS.setFillRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } + case OPS.setStrokeRGBColor: { + const strokeCS = self._getColorSpace( + Name.get("DeviceRGB"), + resources, + localColorSpaceCache + ); + if (strokeCS instanceof ColorSpace) { + stateManager.state.strokeColorSpace = strokeCS; + + args = strokeCS.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + } + + next( + self._handleColorSpace(strokeCS, "rgb").then(CS => { + stateManager.state.strokeColorSpace = CS; + + operatorList.addOp(OPS.setStrokeRGBColor, CS.getRgb(args, 0)); + }) + ); + return; + } case OPS.setFillColorN: cs = stateManager.state.patternFillColorSpace; if (!cs) { if (isNumberArray(args, null)) { - args = ColorSpaceUtils.gray.getRgb(args, 0); - fn = OPS.setFillRGBColor; - break; + const fillCS = self._getColorSpace( + Name.get("DeviceGray"), + resources, + localColorSpaceCache + ); + if (fillCS instanceof ColorSpace) { + args = fillCS.getRgb(args, 0); + fn = OPS.setFillRGBColor; + break; + } + + next( + self._handleColorSpace(fillCS, "gray").then(CS => { + operatorList.addOp(OPS.setFillRGBColor, CS.getRgb(args, 0)); + }) + ); + return; } args = []; fn = OPS.setFillTransparent; @@ -2051,9 +2172,26 @@ class PartialEvaluator { cs = stateManager.state.patternStrokeColorSpace; if (!cs) { if (isNumberArray(args, null)) { - args = ColorSpaceUtils.gray.getRgb(args, 0); - fn = OPS.setStrokeRGBColor; - break; + const strokeCS = self._getColorSpace( + Name.get("DeviceGray"), + resources, + localColorSpaceCache + ); + if (strokeCS instanceof ColorSpace) { + args = strokeCS.getRgb(args, 0); + fn = OPS.setStrokeRGBColor; + break; + } + + next( + self._handleColorSpace(strokeCS, "gray").then(CS => { + operatorList.addOp( + OPS.setStrokeRGBColor, + CS.getRgb(args, 0) + ); + }) + ); + return; } args = []; fn = OPS.setStrokeTransparent; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index a06a28688a2a7..937e6c0db06d5 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -192,6 +192,9 @@ !simpletype3font.pdf !Type3WordSpacing.pdf !IndexedCS_negative_and_high.pdf +!DefaultRGBColourSpaces.pdf +!DefaultRGBColourSpaces-inherit.pdf +!DefaultColourSpaces-230802.pdf !sizes.pdf !javauninstall-7r.pdf !file_url_link.pdf diff --git a/test/pdfs/DefaultColourSpaces-230802.pdf b/test/pdfs/DefaultColourSpaces-230802.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2e11ebd5c450ed3bee3feadf8589f3a18281c6e3 GIT binary patch literal 13360 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 literal 0 HcmV?d00001 diff --git a/test/pdfs/DefaultRGBColourSpaces-inherit.pdf b/test/pdfs/DefaultRGBColourSpaces-inherit.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f0f467d502cd31c75836bd0511079d4384e2ce13 GIT binary patch literal 5645 zcmeHL&2HO95We#%<|4oW;!sP8mJGW(P?pr9PGeIFTEGa3HMNrH%A`ZCT(=L=cj%>0 zQJ{eHVx8F~wGw~gD0b2wOhb})IXg2uGyBa)HqK6l`|MTAYJB+P{_hWe-v4v|mt`?% zLVS5;9Uof&_8Fgx6dauMX*l`qMu6Stk}u=A)$4&JqA5vRBx06nIklLUeIHI!xPTU^ zfp;|1k5`cdwxxn@8x^m`G~`}w}a&6QB%e_z% zZ&Q)}Jnk#;{EMlLD;>R_`mu*Gx_^8O_K-oFEI@vzA1~u%e8U5w?w^PmUoE9S^1wB6 zD7+bb(tweJ28<<-raYMfYr~9eWO>vHA*&Ri@#<2lOwu%?8IPwz`DA}9mbXHN0Y?k# z?2>71pwE}&%l>{3M(DL?T~Ix!xje8xEq1;k{ z)r~w{`j@6FZ>VZeWq=O8Tf2CG(j^x?a`N#ml9LrKKHxiTcgw|2m5Ufd7(Ld-2ZB}W zd&$Lam5U>E(TB(SSQ{;!&*u1Y>0R{5wh{RBQfQK}X2=yFt065z=r#7bo%_%-YU zUoIh8V^SEw4S|;i-BS_GjwUnfJqAk$he3sCnERwkZ z?x@v;2}#h^#A?(3?a`rEc8`oJN3cr6Xb#ET_D3&Za;1a^r1Ts=^)}dnv7t>T^URPQ z`fI!X;A98Qhb^O6*U(zA9va0Xe=zuA2h|;nrilw_+fYqIquM)tJ1A;Cw|2{f0!Vqe1~!zFdZYX! zH9&{3iPeKi@s>RJB|Akro>)fSR-Co8Kl(IjedhAxOgtz%@R+b7WjTp4$mXEti#oVb zv#5f_)U0C^A+sOt#{VFL&BhT+w@e-!bnVu zrTb!wWPe(j5}(|x)BN#9Ki_N>DoWy2`N)v5(vK7^5js`0s0GO=>dET1d)WOdb^B|G zRA$X|UOv+cl}$p`hv-Eu+agS>;}IGxs)PQ_BR1(C^~Q}>Peaq{{{tRT(Mz*8B8W{{ z=Y3h-LMiEaAN>q0DnQX@%PjOvt5JtU`c8fGq=z>3lB2N=L$2d|6&NbD&-_8>|9&X& zsDB}9{RN5j6VU3?akxvw%))52t>*fI1J`Z2FoX4+gZ-Q?h|F9EnOk9wweg)ZY*p_3>vJK`mOKVxHQ}Wq4XqW7^@G9D9yYy}`w<{AKE=7_d lz&QL}P_(e+`*DmfTj>PSOTAR`M5<`I&Y^2H8iO}O>tAH|^NRog literal 0 HcmV?d00001 diff --git a/test/pdfs/DefaultRGBColourSpaces.pdf b/test/pdfs/DefaultRGBColourSpaces.pdf new file mode 100644 index 0000000000000000000000000000000000000000..eadbb548252d32c416a6579a68a499ad74b45088 GIT binary patch literal 5645 zcmeHL&2Hm15Wedv<`N(aG%M4#Vy6jwkpEgV+f8gQS|C^yj7-}~6)BbIH0?w59eU|g z6ey5=vCfc^X#F@@Z?fASqQG`Y4QGZk!*4!m?`$wSV6QvY-iJT#|Nijj{Xh4ASr&r^ zM3-0A$%zGEpYf$gpow!68qR*Z5n%VZ^0^t{ZQu7)*CKk|4s#ma!UbL zH?nZ)Uz)DHp{hZZ0Xq0TckvLVD=xa^}M#CO_m!NpFUix@)~Kh?!Yg0tc|A|+~|FoYD_jk!Z>8@5lu3A@sQN3FQkn(B`Y$zx7M)?CZ zK!>n_)q_cK1MdBto+2GjEF-rNXDvM$f1I>FbJ=kw9+Vz_pu`~{pECbZf z0@Jj&<5KVfBBBV}8A@wJ2hCDm(7N`dVsvWiHGOcwqgZn+_KR(LE*5?$=78;bvvgk- zNcP8-DP?N2PP4}w{d`j>RFuT4_K_iFr4JM>5js`0s0GO=>PhRid({0Rb^EK2R3>LS zU%k)^l}$p`$LPgYwndn>jYnv(s1N!JkJzMp)EhTiJq=B({||UX4K}53L=c;@&RfKz z42LM`Ss(obEGj|KX3GS-3PrU@q(JHc9HNJ|^d(0DhMZ>ei@=a-m