@@ -79,7 +79,7 @@ class ColorSpaceUtils {
7979 }
8080
8181 try {
82- parsedCS = this . #parse( cs , options ) ;
82+ parsedCS = this . #parse( cs , options , /* topLevel = */ true ) ;
8383 } catch ( ex ) {
8484 if ( asyncIfNotCached && ! ( ex instanceof MissingDataException ) ) {
8585 return Promise . reject ( ex ) ;
@@ -124,39 +124,92 @@ class ColorSpaceUtils {
124124 return parsedCS ;
125125 }
126126
127- static #parse( cs , options ) {
128- const { xref, resources, pdfFunctionFactory, globalColorSpaceCache } =
129- options ;
127+ /**
128+ * NOTE: This method should *only* be invoked from `this.#parse`,
129+ * when parsing "default" ColorSpaces (i.e. /DefaultGray, /DefaultRGB,
130+ * and /DefaultCMYK).
131+ */
132+ static #defaultParse( cs , options , deviceCS ) {
133+ const { globalColorSpaceCache } = options ;
134+ let csRef , parsedCS ;
135+
136+ // Check if the ColorSpace is cached first, to avoid re-parsing it.
137+ if ( cs instanceof Ref ) {
138+ csRef = cs ;
139+
140+ const cachedCS = globalColorSpaceCache . getByRef ( csRef ) ;
141+ if ( cachedCS ) {
142+ return cachedCS ;
143+ }
144+ }
145+ try {
146+ parsedCS = this . #parse( cs , options ) ;
147+ } catch ( ex ) {
148+ if ( ex instanceof MissingDataException ) {
149+ throw ex ;
150+ }
151+ warn ( `Cannot parse default ColorSpace: "${ ex } ".` ) ;
152+ return deviceCS ;
153+ }
154+
155+ // The default ColorSpace must be compatible with the original one.
156+ if ( parsedCS . numComps !== deviceCS . numComps ) {
157+ warn (
158+ "Incorrect number of components in default ColorSpace, " +
159+ `expected "${ deviceCS . numComps } " and got "${ parsedCS . numComps } ".`
160+ ) ;
161+ return deviceCS ;
162+ }
163+
164+ // Only cache the parsed ColorSpace globally, by reference.
165+ if ( csRef ) {
166+ globalColorSpaceCache . set ( /* name = */ null , csRef , parsedCS ) ;
167+ }
168+ return parsedCS ;
169+ }
170+
171+ static #parse( cs , options , topLevel = false ) {
172+ const { xref, pdfFunctionFactory, globalColorSpaceCache } = options ;
130173
131174 cs = xref . fetchIfRef ( cs ) ;
132175 if ( cs instanceof Name ) {
133176 switch ( cs . name ) {
134177 case "G" :
135- case "DeviceGray" :
178+ case "DeviceGray" : {
179+ const defaultCS = topLevel && this . #getResCS( "DefaultGray" , options ) ;
180+ if ( defaultCS ) {
181+ return this . #defaultParse( defaultCS , options , this . gray ) ;
182+ }
136183 return this . gray ;
184+ }
137185 case "RGB" :
138- case "DeviceRGB" :
186+ case "DeviceRGB" : {
187+ const defaultCS = topLevel && this . #getResCS( "DefaultRGB" , options ) ;
188+ if ( defaultCS ) {
189+ return this . #defaultParse( defaultCS , options , this . rgb ) ;
190+ }
139191 return this . rgb ;
192+ }
140193 case "DeviceRGBA" :
141194 return this . rgba ;
142195 case "CMYK" :
143- case "DeviceCMYK" :
196+ case "DeviceCMYK" : {
197+ const defaultCS = topLevel && this . #getResCS( "DefaultCMYK" , options ) ;
198+ if ( defaultCS ) {
199+ return this . #subParse( defaultCS , options , this . cmyk ) ;
200+ }
144201 return this . cmyk ;
202+ }
145203 case "Pattern" :
146204 return new PatternCS ( /* baseCS = */ null ) ;
147205 default :
148- if ( resources instanceof Dict ) {
149- const colorSpaces = resources . get ( "ColorSpace" ) ;
150- if ( colorSpaces instanceof Dict ) {
151- const resourcesCS = colorSpaces . get ( cs . name ) ;
152- if ( resourcesCS ) {
153- if ( resourcesCS instanceof Name ) {
154- return this . #parse( resourcesCS , options ) ;
155- }
156- cs = resourcesCS ;
157- break ;
158- }
206+ const resourcesCS = xref . fetchIfRef ( this . #getResCS( cs . name , options ) ) ;
207+ if ( resourcesCS ) {
208+ if ( resourcesCS instanceof Name ) {
209+ return this . #parse( resourcesCS , options ) ;
159210 }
211+ cs = resourcesCS ;
212+ break ;
160213 }
161214 // Fallback to the default gray color space.
162215 warn ( `Unrecognized ColorSpace: ${ cs . name } ` ) ;
@@ -276,6 +329,16 @@ class ColorSpaceUtils {
276329 return this . gray ;
277330 }
278331
332+ static #getResCS( name , { resources } ) {
333+ if ( resources instanceof Dict ) {
334+ const colorSpaces = resources . get ( "ColorSpace" ) ;
335+ if ( colorSpaces instanceof Dict ) {
336+ return colorSpaces . getRaw ( name ) ?? null ;
337+ }
338+ }
339+ return null ;
340+ }
341+
279342 static get gray ( ) {
280343 return shadow ( this , "gray" , new DeviceGrayCS ( ) ) ;
281344 }
0 commit comments