@@ -79,7 +79,7 @@ class ColorSpaceUtils {
79
79
}
80
80
81
81
try {
82
- parsedCS = this . #parse( cs , options ) ;
82
+ parsedCS = this . #parse( cs , options , /* topLevel = */ true ) ;
83
83
} catch ( ex ) {
84
84
if ( asyncIfNotCached && ! ( ex instanceof MissingDataException ) ) {
85
85
return Promise . reject ( ex ) ;
@@ -124,39 +124,94 @@ class ColorSpaceUtils {
124
124
return parsedCS ;
125
125
}
126
126
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
+ // Ensure that any `name`-checks still work as before.
164
+ parsedCS . name = deviceCS . name ;
165
+
166
+ // Only cache the parsed ColorSpace globally, by reference.
167
+ if ( csRef ) {
168
+ globalColorSpaceCache . set ( /* name = */ null , csRef , parsedCS ) ;
169
+ }
170
+ return parsedCS ;
171
+ }
172
+
173
+ static #parse( cs , options , topLevel = false ) {
174
+ const { xref, pdfFunctionFactory, globalColorSpaceCache } = options ;
130
175
131
176
cs = xref . fetchIfRef ( cs ) ;
132
177
if ( cs instanceof Name ) {
133
178
switch ( cs . name ) {
134
179
case "G" :
135
- case "DeviceGray" :
180
+ case "DeviceGray" : {
181
+ const defaultCS = topLevel && this . #getResCS( "DefaultGray" , options ) ;
182
+ if ( defaultCS ) {
183
+ return this . #defaultParse( defaultCS , options , this . gray ) ;
184
+ }
136
185
return this . gray ;
186
+ }
137
187
case "RGB" :
138
- case "DeviceRGB" :
188
+ case "DeviceRGB" : {
189
+ const defaultCS = topLevel && this . #getResCS( "DefaultRGB" , options ) ;
190
+ if ( defaultCS ) {
191
+ return this . #defaultParse( defaultCS , options , this . rgb ) ;
192
+ }
139
193
return this . rgb ;
194
+ }
140
195
case "DeviceRGBA" :
141
196
return this . rgba ;
142
197
case "CMYK" :
143
- case "DeviceCMYK" :
198
+ case "DeviceCMYK" : {
199
+ const defaultCS = topLevel && this . #getResCS( "DefaultCMYK" , options ) ;
200
+ if ( defaultCS ) {
201
+ return this . #subParse( defaultCS , options , this . cmyk ) ;
202
+ }
144
203
return this . cmyk ;
204
+ }
145
205
case "Pattern" :
146
206
return new PatternCS ( /* baseCS = */ null ) ;
147
207
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
- }
208
+ const resourcesCS = xref . fetchIfRef ( this . #getResCS( cs . name , options ) ) ;
209
+ if ( resourcesCS ) {
210
+ if ( resourcesCS instanceof Name ) {
211
+ return this . #parse( resourcesCS , options ) ;
159
212
}
213
+ cs = resourcesCS ;
214
+ break ;
160
215
}
161
216
// Fallback to the default gray color space.
162
217
warn ( `Unrecognized ColorSpace: ${ cs . name } ` ) ;
@@ -276,6 +331,16 @@ class ColorSpaceUtils {
276
331
return this . gray ;
277
332
}
278
333
334
+ static #getResCS( name , { resources } ) {
335
+ if ( resources instanceof Dict ) {
336
+ const colorSpaces = resources . get ( "ColorSpace" ) ;
337
+ if ( colorSpaces instanceof Dict ) {
338
+ return colorSpaces . getRaw ( name ) ?? null ;
339
+ }
340
+ }
341
+ return null ;
342
+ }
343
+
279
344
static get gray ( ) {
280
345
return shadow ( this , "gray" , new DeviceGrayCS ( ) ) ;
281
346
}
0 commit comments