/src/ghostpdl/pdf/pdf_check.c
Line | Count | Source |
1 | | /* Copyright (C) 2019-2026 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | /* Checks for transparency and spots for the PDF interpreter */ |
17 | | |
18 | | #include "pdf_int.h" |
19 | | #include "pdf_stack.h" |
20 | | #include "pdf_deref.h" |
21 | | #include "pdf_page.h" |
22 | | #include "pdf_file.h" |
23 | | #include "pdf_dict.h" |
24 | | #include "pdf_array.h" |
25 | | #include "pdf_loop_detect.h" |
26 | | #include "pdf_colour.h" |
27 | | #include "pdf_trans.h" |
28 | | #include "pdf_font_types.h" |
29 | | #include "pdf_gstate.h" |
30 | | #include "pdf_misc.h" |
31 | | #include "pdf_check.h" |
32 | | #include "pdf_device.h" |
33 | | #include "gsdevice.h" /* For gs_setdevice_no_erase */ |
34 | | #include "gspaint.h" /* For gs_erasepage() */ |
35 | | |
36 | | /* For performance and resource reasons we do not want to install the transparency blending |
37 | | * compositor unless we need it. Similarly, if a device handles spot colours it can minimise |
38 | | * memory usage if it knows ahead of time how many spot colours there will be. |
39 | | * |
40 | | * The PDF interpreter written in PostScript performed these as two separate tasks, on opening |
41 | | * a PDF file it would count spot colour usage and then for each page it would chek if the page |
42 | | * used any transparency. The code below is used to check for both transparency and spot colours. |
43 | | * If the int pointer to num_spots is NULL then we aren't interested in spot colours (not supported |
44 | | * by the device), if the int pointer to transparent is NULL then we aren't interested in transparency |
45 | | * (-dNOTRANSPARENCY is set). |
46 | | * |
47 | | * Currently the code is run when we open the PDF file, the existence of transparency on any given |
48 | | * page is recorded in a bit array for later use. If it turns out that checking every page when we |
49 | | * open the file is a performance burden then we could do it on a per-page basis instead. NB if |
50 | | * the device supports spot colours then we do need to scan the file before starting any page. |
51 | | * |
52 | | * The technique is fairly straight-forward, we start with each page, and open its Resources |
53 | | * dictionary, we then check by type each possible resource. Some resources (eg Pattern, XObject) |
54 | | * can themselves contain Resources, in which case we recursively check that dictionary. Note; not |
55 | | * all Resource types need to be checked for both transparency and spot colours, some types can |
56 | | * only contain one or the other. |
57 | | * |
58 | | * Routines with the name pdfi_check_xxx_dict are intended to check a Resource dictionary entry, this |
59 | | * will be a dictioanry of names and values, where the values are objects of the given Resource type. |
60 | | * |
61 | | */ |
62 | | |
63 | | /* Structure for keeping track of resources as they are being checked */ |
64 | | /* CheckedResources is a bit of a hack, for the file Bug697655.pdf. That file has many (unused) |
65 | | * XObjects which use Resources and the Resources use other XObjects and so on nested |
66 | | * very deeply. This takes *hours* to check. Ghostscript gets around this because it |
67 | | * stores all the objects (which we don't want to do because its wasteful) and checking |
68 | | * to see if its already tested a given resource for spots/transparency. |
69 | | * This is a temporary allocation, big enough to hold all the objects in the file (1 per bit) |
70 | | * each time we have fully checked a resource we add it here, when checking a resource we |
71 | | * first check this list to see if its already been checked, in which case we can skip |
72 | | * it. When done we release the memory. |
73 | | */ |
74 | | typedef struct { |
75 | | bool transparent; |
76 | | bool BM_Not_Normal; |
77 | | bool has_overprint; /* Does it have OP or op in an ExtGState? */ |
78 | | pdf_dict *spot_dict; |
79 | | pdf_array *font_array; |
80 | | uint32_t size; |
81 | | byte *CheckedResources; |
82 | | } pdfi_check_tracker_t; |
83 | | |
84 | | static int pdfi_check_Resources(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, pdfi_check_tracker_t *tracker); |
85 | | |
86 | | static inline bool resource_is_checked(pdfi_check_tracker_t *tracker, pdf_obj *o) |
87 | 4.98M | { |
88 | 4.98M | uint32_t byte_offset; |
89 | 4.98M | byte bit_offset; |
90 | 4.98M | int object_num; |
91 | | |
92 | 4.98M | if(tracker->CheckedResources == NULL) |
93 | 65.4k | return 0; |
94 | | |
95 | | /* objects with object number 0 are directly defined, we can't |
96 | | * store those so just return immediately |
97 | | */ |
98 | 4.91M | object_num = pdf_object_num(o); |
99 | 4.91M | if (object_num > 0 && (object_num >> 3) < tracker->size) { |
100 | | /* CheckedResources is a byte array, each byte represents |
101 | | * 8 objects. So the object number / 8 is the byte offset |
102 | | * into the array, and then object number % 8 is the bit |
103 | | * within that byte that we want. |
104 | | */ |
105 | 2.06M | bit_offset = 0x01 << (object_num % 8); |
106 | 2.06M | byte_offset = object_num >> 3; |
107 | | |
108 | | /* If its already set, then return that. */ |
109 | 2.06M | if (tracker->CheckedResources[byte_offset] & bit_offset) |
110 | 224k | return true; |
111 | 1.83M | else |
112 | | /* Otherwise set it for futre reference */ |
113 | 1.83M | tracker->CheckedResources[byte_offset] |= bit_offset; |
114 | 2.06M | } |
115 | 4.69M | return false; |
116 | 4.91M | } |
117 | | |
118 | | |
119 | | static int |
120 | | pdfi_check_free_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker) |
121 | 202k | { |
122 | 202k | gs_free_object(ctx->memory, tracker->CheckedResources, "pdfi_check_free_tracker(flags)"); |
123 | 202k | pdfi_countdown(tracker->spot_dict); |
124 | 202k | pdfi_countdown(tracker->font_array); |
125 | 202k | memset(tracker, 0, sizeof(*tracker)); |
126 | 202k | return 0; |
127 | 202k | } |
128 | | |
129 | | static int |
130 | | pdfi_check_init_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker, pdf_array **fonts_array, pdf_array **spot_array) |
131 | 202k | { |
132 | 202k | int code = 0; |
133 | | |
134 | 202k | memset(tracker, 0, sizeof(*tracker)); |
135 | | |
136 | 202k | tracker->size = (ctx->xref_table->xref_size + 7) / 8; |
137 | 202k | tracker->CheckedResources = gs_alloc_bytes(ctx->memory, tracker->size, |
138 | 202k | "pdfi_check_init_tracker(flags)"); |
139 | 202k | if (tracker->CheckedResources == NULL) |
140 | 0 | return_error(gs_error_VMerror); |
141 | | |
142 | 202k | memset(tracker->CheckedResources, 0x00, tracker->size); |
143 | | |
144 | 202k | if (ctx->device_state.spot_capable || |
145 | 179k | (ctx->pgs->device->icc_struct->overprint_control) == gs_overprint_control_simulate || |
146 | 179k | spot_array != NULL) |
147 | 22.9k | { |
148 | 22.9k | code = pdfi_dict_alloc(ctx, 32, &tracker->spot_dict); |
149 | 22.9k | if (code < 0) |
150 | 0 | goto cleanup; |
151 | 22.9k | pdfi_countup(tracker->spot_dict); |
152 | 22.9k | } |
153 | | |
154 | 202k | if (fonts_array != NULL) { |
155 | 0 | code = pdfi_array_alloc(ctx, 0, &tracker->font_array); |
156 | 0 | if (code < 0) |
157 | 0 | goto cleanup; |
158 | 0 | pdfi_countup(tracker->font_array); |
159 | 0 | } |
160 | | |
161 | 202k | return 0; |
162 | | |
163 | 0 | cleanup: |
164 | 0 | pdfi_check_free_tracker(ctx, tracker); |
165 | 0 | return code; |
166 | 202k | } |
167 | | |
168 | | /* |
169 | | * Check the Resources dictionary ColorSpace entry. pdfi_check_ColorSpace_for_spots is defined |
170 | | * in pdf_colour.c |
171 | | */ |
172 | | static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict, |
173 | | pdf_dict *page_dict, pdfi_check_tracker_t *tracker) |
174 | 51.5k | { |
175 | 51.5k | int code; |
176 | 51.5k | uint64_t i, index; |
177 | 51.5k | pdf_obj *Key = NULL, *Value = NULL; |
178 | | |
179 | 51.5k | if (resource_is_checked(tracker, (pdf_obj *)cspace_dict)) |
180 | 22 | return 0; |
181 | | |
182 | 51.5k | if (pdfi_type_of(cspace_dict) != PDF_DICT) |
183 | 44.3k | return_error(gs_error_typecheck); |
184 | | |
185 | 7.22k | if (pdfi_dict_entries(cspace_dict) > 0) { |
186 | 7.20k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */ |
187 | 7.20k | if (code < 0) |
188 | 0 | return code; |
189 | | |
190 | 7.20k | code = pdfi_dict_first(ctx, cspace_dict, &Key, &Value, &index); |
191 | 7.20k | if (code < 0) |
192 | 1.25k | goto error1; |
193 | | |
194 | 5.95k | i = 1; |
195 | 8.14k | do { |
196 | 8.14k | code = pdfi_check_ColorSpace_for_spots(ctx, Value, cspace_dict, page_dict, tracker->spot_dict); |
197 | 8.14k | if (code < 0) |
198 | 122 | goto error2; |
199 | | |
200 | 8.02k | pdfi_countdown(Key); |
201 | 8.02k | Key = NULL; |
202 | 8.02k | pdfi_countdown(Value); |
203 | 8.02k | Value = NULL; |
204 | | |
205 | 8.02k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
206 | | |
207 | 8.17k | do { |
208 | 8.17k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */ |
209 | 8.17k | if (code < 0) |
210 | 0 | goto error1; |
211 | | |
212 | 8.17k | if (i++ >= pdfi_dict_entries(cspace_dict)) { |
213 | 5.83k | code = 0; |
214 | 5.83k | goto transparency_exit; |
215 | 5.83k | } |
216 | | |
217 | 2.33k | code = pdfi_dict_next(ctx, cspace_dict, &Key, &Value, &index); |
218 | 2.33k | if (code == 0 && pdfi_type_of(Value) == PDF_ARRAY) |
219 | 2.18k | break; |
220 | 152 | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
221 | 152 | pdfi_countdown(Key); |
222 | 152 | Key = NULL; |
223 | 152 | pdfi_countdown(Value); |
224 | 152 | Value = NULL; |
225 | 152 | } while(1); |
226 | 8.02k | }while (1); |
227 | 5.95k | } |
228 | 12 | return 0; |
229 | | |
230 | 5.83k | transparency_exit: |
231 | 5.95k | error2: |
232 | 5.95k | pdfi_countdown(Key); |
233 | 5.95k | pdfi_countdown(Value); |
234 | | |
235 | 7.20k | error1: |
236 | 7.20k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
237 | 7.20k | return code; |
238 | 5.95k | } |
239 | | |
240 | | /* |
241 | | * Process an individual Shading dictionary to see if it contains a ColorSpace with a spot colour |
242 | | */ |
243 | | static int pdfi_check_Shading(pdf_context *ctx, pdf_obj *shading, |
244 | | pdf_dict *page_dict, pdfi_check_tracker_t *tracker) |
245 | 6.78k | { |
246 | 6.78k | int code; |
247 | 6.78k | pdf_obj *o = NULL; |
248 | 6.78k | pdf_dict *shading_dict = NULL; |
249 | | |
250 | 6.78k | if (resource_is_checked(tracker, shading)) |
251 | 378 | return 0; |
252 | | |
253 | 6.40k | code = pdfi_dict_from_obj(ctx, shading, &shading_dict); |
254 | 6.40k | if (code < 0) |
255 | 4 | return code; |
256 | | |
257 | 6.40k | if (pdfi_type_of(shading_dict) != PDF_DICT) |
258 | 0 | return_error(gs_error_typecheck); |
259 | | |
260 | 6.40k | code = pdfi_dict_knownget(ctx, shading_dict, "ColorSpace", (pdf_obj **)&o); |
261 | 6.40k | if (code > 0) { |
262 | 5.88k | code = pdfi_check_ColorSpace_for_spots(ctx, o, shading_dict, page_dict, tracker->spot_dict); |
263 | 5.88k | pdfi_countdown(o); |
264 | 5.88k | return code; |
265 | 5.88k | } |
266 | 515 | return 0; |
267 | 6.40k | } |
268 | | |
269 | | /* |
270 | | * Check the Resources dictionary Shading entry. |
271 | | */ |
272 | | static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict, |
273 | | pdf_dict *page_dict, pdfi_check_tracker_t *tracker) |
274 | 51.5k | { |
275 | 51.5k | int code; |
276 | 51.5k | uint64_t i, index; |
277 | 51.5k | pdf_obj *Key = NULL, *Value = NULL; |
278 | | |
279 | 51.5k | if (resource_is_checked(tracker, (pdf_obj *)shading_dict)) |
280 | 0 | return 0; |
281 | | |
282 | 51.5k | if (pdfi_type_of(shading_dict) != PDF_DICT) |
283 | 49.8k | return_error(gs_error_typecheck); |
284 | | |
285 | 1.78k | if (pdfi_dict_entries(shading_dict) > 0) { |
286 | 1.78k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Shading dictionary loop */ |
287 | 1.78k | if (code < 0) |
288 | 0 | return code; |
289 | | |
290 | 1.78k | code = pdfi_dict_first(ctx, shading_dict, &Key, &Value, &index); |
291 | 1.78k | if (code < 0) |
292 | 994 | goto error2; |
293 | | |
294 | 794 | i = 1; |
295 | 6.11k | do { |
296 | 6.11k | if ((pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM)) { |
297 | 6.10k | code = pdfi_check_Shading(ctx, Value, page_dict, tracker); |
298 | 6.10k | if (code < 0) |
299 | 20 | goto error2; |
300 | 6.10k | } |
301 | | |
302 | 6.09k | pdfi_countdown(Key); |
303 | 6.09k | Key = NULL; |
304 | 6.09k | pdfi_countdown(Value); |
305 | 6.09k | Value = NULL; |
306 | | |
307 | 6.09k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
308 | | |
309 | 11.6k | do { |
310 | 11.6k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */ |
311 | 11.6k | if (code < 0) |
312 | 0 | goto error1; |
313 | | |
314 | 11.6k | if (i++ >= pdfi_dict_entries(shading_dict)) { |
315 | 774 | code = 0; |
316 | 774 | goto transparency_exit; |
317 | 774 | } |
318 | | |
319 | 10.9k | code = pdfi_dict_next(ctx, shading_dict, &Key, &Value, &index); |
320 | 10.9k | if (code == 0 && pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM) |
321 | 5.32k | break; |
322 | 5.58k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
323 | 5.58k | pdfi_countdown(Key); |
324 | 5.58k | Key = NULL; |
325 | 5.58k | pdfi_countdown(Value); |
326 | 5.58k | Value = NULL; |
327 | 5.58k | } while(1); |
328 | 6.09k | }while (1); |
329 | 794 | } |
330 | 0 | return 0; |
331 | | |
332 | 774 | transparency_exit: |
333 | 1.78k | error2: |
334 | 1.78k | pdfi_countdown(Key); |
335 | 1.78k | pdfi_countdown(Value); |
336 | | |
337 | 1.78k | error1: |
338 | 1.78k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
339 | 1.78k | return code; |
340 | 1.78k | } |
341 | | |
342 | | /* |
343 | | * This routine checks an XObject to see if it contains any spot |
344 | | * colour definitions, or transparency usage. |
345 | | */ |
346 | | static int pdfi_check_XObject(pdf_context *ctx, pdf_dict *xobject, pdf_dict *page_dict, |
347 | | pdfi_check_tracker_t *tracker) |
348 | 337k | { |
349 | 337k | int code = 0; |
350 | 337k | pdf_name *n = NULL; |
351 | 337k | bool known = false; |
352 | 337k | double f; |
353 | | |
354 | 337k | if (resource_is_checked(tracker, (pdf_obj *)xobject)) |
355 | 109k | return 0; |
356 | | |
357 | 228k | if (pdfi_type_of(xobject) != PDF_DICT) |
358 | 258 | return_error(gs_error_typecheck); |
359 | | |
360 | 228k | code = pdfi_dict_get_type(ctx, xobject, "Subtype", PDF_NAME, (pdf_obj **)&n); |
361 | 228k | if (code >= 0) { |
362 | 227k | if (pdfi_name_is((const pdf_name *)n, "Image")) { |
363 | 115k | pdf_obj *CS = NULL; |
364 | | |
365 | 115k | pdfi_countdown(n); |
366 | 115k | n = NULL; |
367 | 115k | code = pdfi_dict_known(ctx, xobject, "SMask", &known); |
368 | 115k | if (code >= 0) { |
369 | 115k | if (known == true) { |
370 | 29.9k | tracker->transparent = true; |
371 | 29.9k | if (tracker->spot_dict == NULL) |
372 | 26.0k | goto transparency_exit; |
373 | 29.9k | } |
374 | 89.0k | code = pdfi_dict_knownget_number(ctx, xobject, "SMaskInData", &f); |
375 | 89.0k | if (code > 0) { |
376 | 0 | code = 0; |
377 | 0 | if (f != 0.0) |
378 | 0 | tracker->transparent = true; |
379 | 0 | if (tracker->spot_dict == NULL) |
380 | 0 | goto transparency_exit; |
381 | 0 | } |
382 | | /* Check the image dictionary for a ColorSpace entry, if we are checking spot names */ |
383 | 89.0k | if (tracker->spot_dict) { |
384 | 12.9k | code = pdfi_dict_knownget(ctx, xobject, "ColorSpace", (pdf_obj **)&CS); |
385 | 12.9k | if (code > 0) { |
386 | | /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */ |
387 | 10.6k | (void)pdfi_check_ColorSpace_for_spots(ctx, CS, xobject, page_dict, tracker->spot_dict); |
388 | 10.6k | pdfi_countdown(CS); |
389 | 10.6k | } |
390 | 12.9k | } |
391 | 89.0k | } |
392 | 115k | } else { |
393 | 112k | if (pdfi_name_is((const pdf_name *)n, "Form")) { |
394 | 111k | pdf_dict *group_dict = NULL, *resource_dict = NULL; |
395 | 111k | pdf_obj *CS = NULL; |
396 | | |
397 | 111k | pdfi_countdown(n); |
398 | 111k | code = pdfi_dict_knownget_type(ctx, xobject, "Group", PDF_DICT, (pdf_obj **)&group_dict); |
399 | 111k | if (code > 0) { |
400 | 12.7k | tracker->transparent = true; |
401 | 12.7k | if (tracker->spot_dict != NULL) { |
402 | | /* Start a new loop detector group to avoid this being detected in the Resources check below */ |
403 | 1.74k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */ |
404 | 1.74k | if (code == 0) { |
405 | 1.74k | code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS); |
406 | 1.74k | if (code > 0) |
407 | | /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */ |
408 | 1.25k | (void)pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, tracker->spot_dict); |
409 | 1.74k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */ |
410 | 1.74k | } |
411 | 1.74k | pdfi_countdown(group_dict); |
412 | 1.74k | pdfi_countdown(CS); |
413 | 1.74k | } |
414 | 11.0k | else if (tracker->BM_Not_Normal) |
415 | 54 | { |
416 | | /* We already know it has a non-normal Blend Mode. No point in keeping searching. */ |
417 | 54 | pdfi_countdown(group_dict); |
418 | 54 | goto transparency_exit; |
419 | 54 | } |
420 | 10.9k | else |
421 | 10.9k | { |
422 | 10.9k | pdfi_countdown(group_dict); |
423 | 10.9k | } |
424 | | /* We need to keep checking Resources in case there are non-Normal blend mode things still to be found. */ |
425 | 12.7k | } |
426 | | |
427 | 111k | code = pdfi_dict_knownget_type(ctx, xobject, "Resources", PDF_DICT, (pdf_obj **)&resource_dict); |
428 | 111k | if (code > 0) { |
429 | 109k | if (ctx->loop_detection && pdf_object_num((pdf_obj *)resource_dict) != 0) { |
430 | 76.6k | code = pdfi_loop_detector_add_object(ctx, resource_dict->object_num); |
431 | 76.6k | if (code < 0) { |
432 | 0 | pdfi_countdown(resource_dict); |
433 | 0 | goto transparency_exit; |
434 | 0 | } |
435 | 76.6k | } |
436 | 109k | code = pdfi_check_Resources(ctx, resource_dict, page_dict, tracker); |
437 | 109k | pdfi_countdown(resource_dict); |
438 | 109k | if (code < 0) |
439 | 0 | goto transparency_exit; |
440 | 109k | } |
441 | 111k | } else |
442 | 454 | pdfi_countdown(n); |
443 | 112k | } |
444 | 227k | } |
445 | | |
446 | 202k | return 0; |
447 | | |
448 | 26.1k | transparency_exit: |
449 | 26.1k | return code; |
450 | 228k | } |
451 | | |
452 | | /* |
453 | | * Check the Resources dictionary XObject entry. |
454 | | */ |
455 | | static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf_dict *page_dict, |
456 | | pdfi_check_tracker_t *tracker) |
457 | 596k | { |
458 | 596k | int code; |
459 | 596k | uint64_t i, index; |
460 | 596k | pdf_obj *Key = NULL, *Value = NULL; |
461 | 596k | pdf_dict *Value_dict = NULL; |
462 | | |
463 | 596k | if (resource_is_checked(tracker, (pdf_obj *)xobject_dict)) |
464 | 0 | return 0; |
465 | | |
466 | 596k | if (pdfi_type_of(xobject_dict) != PDF_DICT) |
467 | 375k | return_error(gs_error_typecheck); |
468 | | |
469 | 220k | if (pdfi_dict_entries(xobject_dict) > 0) { |
470 | 218k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */ |
471 | 218k | if (code < 0) |
472 | 0 | return code; |
473 | | |
474 | 218k | code = pdfi_dict_first(ctx, xobject_dict, &Key, &Value, &index); |
475 | 218k | if (code < 0) |
476 | 38.5k | goto error_exit; |
477 | | |
478 | 180k | i = 1; |
479 | 338k | do { |
480 | 338k | if (pdfi_type_of(Value) == PDF_STREAM) { |
481 | 337k | code = pdfi_dict_from_obj(ctx, Value, &Value_dict); |
482 | 337k | if (code < 0) |
483 | 0 | goto error_exit; |
484 | | |
485 | 337k | code = pdfi_check_XObject(ctx, Value_dict, page_dict, tracker); |
486 | 337k | if (code < 0) |
487 | 0 | goto error_exit; |
488 | 337k | } |
489 | | |
490 | 338k | pdfi_countdown(Key); |
491 | 338k | Key = NULL; |
492 | 338k | pdfi_countdown(Value); |
493 | 338k | Value = NULL; |
494 | 338k | Value_dict = NULL; |
495 | | |
496 | 338k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
497 | | |
498 | 389k | do { |
499 | 389k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the XObject dictionary loop */ |
500 | 389k | if (code < 0) |
501 | 0 | goto error_exit; |
502 | | |
503 | 389k | if (i++ >= pdfi_dict_entries(xobject_dict)) { |
504 | 180k | code = 0; |
505 | 180k | goto transparency_exit; |
506 | 180k | } |
507 | | |
508 | 209k | code = pdfi_dict_next(ctx, xobject_dict, &Key, &Value, &index); |
509 | 209k | if (code == 0 && pdfi_type_of(Value) == PDF_STREAM) |
510 | 158k | break; |
511 | 50.9k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */ |
512 | 50.9k | pdfi_countdown(Key); |
513 | 50.9k | Key = NULL; |
514 | 50.9k | pdfi_countdown(Value); |
515 | 50.9k | Value = NULL; |
516 | 50.9k | } while(1); |
517 | 338k | }while(1); |
518 | 180k | } |
519 | 1.50k | return 0; |
520 | | |
521 | 180k | transparency_exit: |
522 | 218k | error_exit: |
523 | 218k | pdfi_countdown(Key); |
524 | 218k | pdfi_countdown(Value); |
525 | 218k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
526 | 218k | return code; |
527 | 180k | } |
528 | | |
529 | | /* |
530 | | * This routine checks an ExtGState dictionary to see if it contains transparency or OP |
531 | | */ |
532 | | static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, |
533 | | pdfi_check_tracker_t *tracker) |
534 | 191k | { |
535 | 191k | int code; |
536 | 191k | pdf_obj *o = NULL; |
537 | 191k | double f; |
538 | 191k | bool overprint; |
539 | | |
540 | 191k | if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict)) |
541 | 69.4k | return 0; |
542 | | |
543 | 122k | if (pdfi_type_of(extgstate_dict) != PDF_DICT) |
544 | 1.29k | return_error(gs_error_typecheck); |
545 | | |
546 | 120k | if (pdfi_dict_entries(extgstate_dict) > 0) { |
547 | | /* See if /OP or /op is true */ |
548 | 120k | code = pdfi_dict_get_bool(ctx, extgstate_dict, "OP", &overprint); |
549 | 120k | if (code == 0 && overprint) |
550 | 5.44k | tracker->has_overprint = true; |
551 | 120k | code = pdfi_dict_get_bool(ctx, extgstate_dict, "op", &overprint); |
552 | 120k | if (code == 0 && overprint) |
553 | 6.18k | tracker->has_overprint = true; |
554 | | |
555 | | /* Check Blend Mode *first* because we short-circuit transparency detection |
556 | | * when we find transparency, and we want to be able to record the use of |
557 | | * blend modes other than Compatible/Normal so that Patterns using these |
558 | | * always use the clist and not tiles. The tiles implementation can't work |
559 | | * if we change to a blend mode other than Normal or Compatible during |
560 | | * the course of a Pattern. |
561 | | */ |
562 | 120k | code = pdfi_dict_knownget_type(ctx, extgstate_dict, "BM", PDF_NAME, &o); |
563 | 120k | if (code > 0) { |
564 | 48.9k | if (!pdfi_name_is((pdf_name *)o, "Normal")) { |
565 | 6.70k | if (!pdfi_name_is((pdf_name *)o, "Compatible")) { |
566 | 6.66k | pdfi_countdown(o); |
567 | 6.66k | tracker->transparent = true; |
568 | 6.66k | tracker->BM_Not_Normal = true; |
569 | 6.66k | return 0; |
570 | 6.66k | } |
571 | 6.70k | } |
572 | 48.9k | } |
573 | 114k | pdfi_countdown(o); |
574 | 114k | o = NULL; |
575 | | |
576 | | /* Check SMask */ |
577 | 114k | code = pdfi_dict_knownget(ctx, extgstate_dict, "SMask", &o); |
578 | 114k | if (code > 0) { |
579 | 17.2k | switch (pdfi_type_of(o)) { |
580 | 14.5k | case PDF_NAME: |
581 | 14.5k | if (!pdfi_name_is((pdf_name *)o, "None")) { |
582 | 47 | pdfi_countdown(o); |
583 | 47 | tracker->transparent = true; |
584 | 47 | return 0; |
585 | 47 | } |
586 | 14.5k | break; |
587 | 14.5k | case PDF_DICT: |
588 | 2.70k | { |
589 | 2.70k | pdf_obj *G = NULL; |
590 | | |
591 | 2.70k | tracker->transparent = true; |
592 | | |
593 | 2.70k | if (tracker->spot_dict != NULL) { |
594 | | /* Check if the SMask has a /G (Group) */ |
595 | 387 | code = pdfi_dict_knownget(ctx, (pdf_dict *)o, "G", &G); |
596 | 387 | if (code > 0) { |
597 | 258 | code = pdfi_check_XObject(ctx, (pdf_dict *)G, page_dict, |
598 | 258 | tracker); |
599 | 258 | pdfi_countdown(G); |
600 | 258 | } |
601 | 387 | } |
602 | 2.70k | pdfi_countdown(o); |
603 | 2.70k | return code; |
604 | 14.5k | } |
605 | 0 | default: |
606 | 0 | break; |
607 | 17.2k | } |
608 | 17.2k | } |
609 | 111k | pdfi_countdown(o); |
610 | 111k | o = NULL; |
611 | | |
612 | 111k | code = pdfi_dict_knownget_number(ctx, extgstate_dict, "CA", &f); |
613 | 111k | if (code > 0) { |
614 | 41.8k | if (f != 1.0) { |
615 | 7.59k | tracker->transparent = true; |
616 | 7.59k | return 0; |
617 | 7.59k | } |
618 | 41.8k | } |
619 | | |
620 | 103k | code = pdfi_dict_knownget_number(ctx, extgstate_dict, "ca", &f); |
621 | 103k | if (code > 0) { |
622 | 36.5k | if (f != 1.0) { |
623 | 942 | tracker->transparent = true; |
624 | 942 | return 0; |
625 | 942 | } |
626 | 36.5k | } |
627 | | |
628 | 103k | } |
629 | 102k | return 0; |
630 | 120k | } |
631 | | |
632 | | /* |
633 | | * Check the Resources dictionary ExtGState entry. |
634 | | */ |
635 | | static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict, |
636 | | pdfi_check_tracker_t *tracker) |
637 | 596k | { |
638 | 596k | int code; |
639 | 596k | uint64_t i, index; |
640 | 596k | pdf_obj *Key = NULL, *Value = NULL; |
641 | | |
642 | 596k | if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict)) |
643 | 118 | return 0; |
644 | | |
645 | 596k | if (pdfi_type_of(extgstate_dict) != PDF_DICT) |
646 | 434k | return_error(gs_error_typecheck); |
647 | | |
648 | 161k | if (pdfi_dict_entries(extgstate_dict) > 0) { |
649 | 160k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */ |
650 | 160k | if (code < 0) |
651 | 0 | return code; |
652 | | |
653 | 160k | code = pdfi_dict_first(ctx, extgstate_dict, &Key, &Value, &index); |
654 | 160k | if (code < 0) |
655 | 15.3k | goto error1; |
656 | | |
657 | 145k | i = 1; |
658 | 191k | do { |
659 | | |
660 | 191k | (void)pdfi_check_ExtGState(ctx, (pdf_dict *)Value, page_dict, tracker); |
661 | 191k | if (tracker->transparent == true && tracker->spot_dict == NULL) |
662 | 17.5k | goto transparency_exit; |
663 | | |
664 | 173k | pdfi_countdown(Key); |
665 | 173k | Key = NULL; |
666 | 173k | pdfi_countdown(Value); |
667 | 173k | Value = NULL; |
668 | | |
669 | 173k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the ExtGState dictionary loop */ |
670 | | |
671 | 185k | do { |
672 | 185k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the ExtGState dictionary loop */ |
673 | 185k | if (code < 0) |
674 | 0 | goto error1; |
675 | | |
676 | 185k | if (i++ >= pdfi_dict_entries(extgstate_dict)) { |
677 | 127k | code = 0; |
678 | 127k | goto transparency_exit; |
679 | 127k | } |
680 | | |
681 | 57.3k | code = pdfi_dict_next(ctx, extgstate_dict, &Key, &Value, &index); |
682 | 57.3k | if (code == 0 && pdfi_type_of(Value) == PDF_DICT) |
683 | 45.9k | break; |
684 | 11.3k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the ExtGState dictionary loop */ |
685 | 11.3k | pdfi_countdown(Key); |
686 | 11.3k | Key = NULL; |
687 | 11.3k | pdfi_countdown(Value); |
688 | 11.3k | Value = NULL; |
689 | 11.3k | } while(1); |
690 | 173k | }while (1); |
691 | 145k | } |
692 | 1.07k | return 0; |
693 | | |
694 | 145k | transparency_exit: |
695 | 145k | pdfi_countdown(Key); |
696 | 145k | pdfi_countdown(Value); |
697 | | |
698 | 160k | error1: |
699 | 160k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
700 | 160k | return code; |
701 | 145k | } |
702 | | |
703 | | /* |
704 | | * This routine checks a Pattern dictionary to see if it contains any spot |
705 | | * colour definitions, or transparency usage (or blend modes other than Normal/Compatible). |
706 | | */ |
707 | | static int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, |
708 | | pdfi_check_tracker_t *tracker) |
709 | 68.9k | { |
710 | 68.9k | int code = 0; |
711 | 68.9k | pdf_obj *o = NULL; |
712 | | |
713 | 68.9k | if (resource_is_checked(tracker, (pdf_obj *)pattern)) |
714 | 918 | return 0; |
715 | | |
716 | 68.0k | if (pdfi_type_of(pattern) != PDF_DICT) |
717 | 0 | return_error(gs_error_typecheck); |
718 | | |
719 | 68.0k | if (tracker->spot_dict != NULL) { |
720 | 10.6k | code = pdfi_dict_knownget(ctx, pattern, "Shading", &o); |
721 | 10.6k | if (code > 0) |
722 | 675 | (void)pdfi_check_Shading(ctx, o, page_dict, tracker); |
723 | 10.6k | pdfi_countdown(o); |
724 | 10.6k | o = NULL; |
725 | 10.6k | } |
726 | | |
727 | 68.0k | code = pdfi_dict_knownget_type(ctx, pattern, "Resources", PDF_DICT, &o); |
728 | 68.0k | if (code > 0) |
729 | 60.6k | (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); |
730 | 68.0k | pdfi_countdown(o); |
731 | 68.0k | o = NULL; |
732 | 68.0k | if (tracker->transparent == true && tracker->spot_dict == NULL) |
733 | 21.9k | goto transparency_exit; |
734 | | |
735 | 46.0k | code = pdfi_dict_knownget_type(ctx, pattern, "ExtGState", PDF_DICT, &o); |
736 | 46.0k | if (code > 0) |
737 | 0 | (void)pdfi_check_ExtGState(ctx, (pdf_dict *)o, page_dict, tracker); |
738 | 46.0k | pdfi_countdown(o); |
739 | 46.0k | o = NULL; |
740 | | |
741 | 68.0k | transparency_exit: |
742 | 68.0k | return 0; |
743 | 46.0k | } |
744 | | |
745 | | /* |
746 | | * This routine checks a Pattern dictionary for transparency. |
747 | | */ |
748 | | int pdfi_check_Pattern_transparency(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict, |
749 | | bool *transparent, bool *BM_Not_Normal) |
750 | 9.45k | { |
751 | 9.45k | int code; |
752 | 9.45k | pdfi_check_tracker_t tracker = {0, 0, 0, NULL, NULL, 0, NULL}; |
753 | | |
754 | | /* NOTE: We use a "null" tracker that won't do any optimization to prevent |
755 | | * checking the same resource twice. |
756 | | * This means we don't get the optimization, but we also don't have the overhead |
757 | | * of setting it up. |
758 | | * If we do want the optimization, call pdfi_check_init_tracker() and |
759 | | * pdfi_check_free_tracker() as in pdfi_check_page(), below. |
760 | | */ |
761 | 9.45k | code = pdfi_check_Pattern(ctx, pattern, page_dict, &tracker); |
762 | 9.45k | if (code == 0) { |
763 | 9.45k | *transparent = tracker.transparent; |
764 | 9.45k | *BM_Not_Normal = tracker.BM_Not_Normal; |
765 | 9.45k | } |
766 | 0 | else |
767 | 0 | *transparent = false; |
768 | 9.45k | return code; |
769 | 9.45k | } |
770 | | |
771 | | /* |
772 | | * Check the Resources dictionary Pattern entry. |
773 | | */ |
774 | | static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf_dict *page_dict, |
775 | | pdfi_check_tracker_t *tracker) |
776 | 596k | { |
777 | 596k | int code; |
778 | 596k | uint64_t i, index; |
779 | 596k | pdf_obj *Key = NULL, *Value = NULL; |
780 | 596k | pdf_dict *instance_dict = NULL; |
781 | | |
782 | 596k | if (resource_is_checked(tracker, (pdf_obj *)pattern_dict)) |
783 | 0 | return 0; |
784 | | |
785 | 596k | if (pdfi_type_of(pattern_dict) != PDF_DICT) |
786 | 584k | return_error(gs_error_typecheck); |
787 | | |
788 | 11.9k | if (pdfi_dict_entries(pattern_dict) > 0) { |
789 | 10.8k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Pattern dictionary loop */ |
790 | 10.8k | if (code < 0) |
791 | 0 | return code; |
792 | | |
793 | 10.8k | code = pdfi_dict_first(ctx, pattern_dict, &Key, &Value, &index); |
794 | 10.8k | if (code < 0) |
795 | 2.11k | goto error1; |
796 | | |
797 | 8.76k | i = 1; |
798 | 59.6k | do { |
799 | 59.6k | if (pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM) { |
800 | 59.4k | code = pdfi_dict_from_obj(ctx, Value, &instance_dict); |
801 | 59.4k | if (code < 0) |
802 | 0 | goto transparency_exit; |
803 | | |
804 | 59.4k | code = pdfi_check_Pattern(ctx, instance_dict, page_dict, tracker); |
805 | 59.4k | if (code < 0) |
806 | 0 | goto transparency_exit; |
807 | 59.4k | } |
808 | | |
809 | 59.6k | pdfi_countdown(Key); |
810 | 59.6k | Key = NULL; |
811 | 59.6k | pdfi_countdown(Value); |
812 | 59.6k | instance_dict = NULL; |
813 | 59.6k | Value = NULL; |
814 | 59.6k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
815 | | |
816 | 157k | do { |
817 | 157k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */ |
818 | 157k | if (code < 0) |
819 | 0 | goto error1; |
820 | | |
821 | 157k | if (i++ >= pdfi_dict_entries(pattern_dict)) { |
822 | 8.76k | code = 0; |
823 | 8.76k | goto transparency_exit; |
824 | 8.76k | } |
825 | | |
826 | 149k | code = pdfi_dict_next(ctx, pattern_dict, &Key, &Value, &index); |
827 | 149k | if (code == 0 && (pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM)) |
828 | 50.8k | break; |
829 | 98.3k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */ |
830 | 98.3k | pdfi_countdown(Key); |
831 | 98.3k | Key = NULL; |
832 | 98.3k | pdfi_countdown(Value); |
833 | 98.3k | Value = NULL; |
834 | 98.3k | } while(1); |
835 | 59.6k | }while (1); |
836 | 8.76k | } |
837 | 1.04k | return 0; |
838 | | |
839 | 8.76k | transparency_exit: |
840 | 8.76k | pdfi_countdown(Key); |
841 | 8.76k | pdfi_countdown(Value); |
842 | 8.76k | pdfi_countdown(instance_dict); |
843 | | |
844 | 10.8k | error1: |
845 | 10.8k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
846 | 10.8k | return code; |
847 | 8.76k | } |
848 | | |
849 | | /* |
850 | | * This routine checks a Font dictionary to see if it contains any spot |
851 | | * colour definitions, or transparency usage. While we are here, if the tracker's font_array |
852 | | * is not NULL, pick up the font information and store it in the array. |
853 | | */ |
854 | | static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict, |
855 | | pdfi_check_tracker_t *tracker) |
856 | 344k | { |
857 | 344k | int code = 0; |
858 | 344k | pdf_obj *o = NULL; |
859 | | |
860 | 344k | if (resource_is_checked(tracker, (pdf_obj *)font)) |
861 | 43.4k | return 0; |
862 | | |
863 | 301k | if (pdfi_type_of(font) != PDF_DICT) |
864 | 0 | return_error(gs_error_typecheck); |
865 | | |
866 | 301k | if (tracker->font_array != NULL) { |
867 | | /* If we get to here this is a font we have not seen before. We need |
868 | | * to make a new font array big enough to hold the existing entries +1 |
869 | | * copy the existing entries to the new array and free the old array. |
870 | | * Finally create a dictionary with all the font information we want |
871 | | * and add it to the array. |
872 | | */ |
873 | 0 | pdf_array *new_fonts = NULL; |
874 | 0 | int index = 0; |
875 | 0 | pdf_obj *array_obj = NULL; |
876 | 0 | pdf_dict *font_info_dict = NULL; |
877 | | |
878 | | /* Let's start by gathering the information we need and storing it in a dictionary */ |
879 | 0 | code = pdfi_dict_alloc(ctx, 4, &font_info_dict); |
880 | 0 | if (code < 0) |
881 | 0 | return code; |
882 | 0 | pdfi_countup(font_info_dict); |
883 | |
|
884 | 0 | if (font->object_num != 0) { |
885 | 0 | pdf_num *int_obj = NULL; |
886 | |
|
887 | 0 | code = pdfi_object_alloc(ctx, PDF_INT, 0, (pdf_obj **)&int_obj); |
888 | 0 | if (code >= 0) { |
889 | 0 | pdfi_countup(int_obj); |
890 | 0 | int_obj->value.i = font->object_num; |
891 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "ObjectNum", (pdf_obj *)int_obj); |
892 | 0 | pdfi_countdown(int_obj); |
893 | 0 | } |
894 | 0 | if (code < 0) { |
895 | 0 | pdfi_countdown(font_info_dict); |
896 | 0 | return code; |
897 | 0 | } |
898 | 0 | } |
899 | | |
900 | | /* Try to get font name with fallback: |
901 | | * 1. Try /BaseFont (standard for most fonts) |
902 | | * 2. Try /FontDescriptor/FontName (common for Type 3 fonts) |
903 | | * 3. Try /Name as last resort |
904 | | */ |
905 | 0 | { |
906 | 0 | pdf_obj *font_name = NULL; |
907 | 0 | int lookup_code; |
908 | 0 | bool found = false; |
909 | |
|
910 | 0 | lookup_code = pdfi_dict_get(ctx, font, "BaseFont", &font_name); |
911 | 0 | if (lookup_code >= 0 && pdfi_type_of(font_name) == PDF_NAME) { |
912 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "BaseFont", font_name); |
913 | 0 | pdfi_countdown(font_name); |
914 | 0 | if (code < 0) { |
915 | 0 | pdfi_countdown(font_info_dict); |
916 | 0 | return code; |
917 | 0 | } |
918 | 0 | found = true; |
919 | 0 | } else { |
920 | 0 | pdfi_countdown(font_name); |
921 | 0 | font_name = NULL; |
922 | 0 | } |
923 | | |
924 | 0 | if (!found) { |
925 | 0 | lookup_code = pdfi_dict_get(ctx, font, "FontDescriptor", &font_name); |
926 | 0 | if (lookup_code >= 0 && pdfi_type_of(font_name) == PDF_DICT) { |
927 | 0 | pdf_dict *font_desc = (pdf_dict *)font_name; |
928 | 0 | pdf_obj *desc_font_name = NULL; |
929 | |
|
930 | 0 | lookup_code = pdfi_dict_get(ctx, font_desc, "FontName", &desc_font_name); |
931 | 0 | pdfi_countdown(font_name); |
932 | 0 | font_name = NULL; |
933 | |
|
934 | 0 | if (lookup_code >= 0 && pdfi_type_of(desc_font_name) == PDF_NAME) { |
935 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "BaseFont", desc_font_name); |
936 | 0 | pdfi_countdown(desc_font_name); |
937 | 0 | if (code < 0) { |
938 | 0 | pdfi_countdown(font_info_dict); |
939 | 0 | return code; |
940 | 0 | } |
941 | 0 | found = true; |
942 | 0 | } else { |
943 | 0 | pdfi_countdown(desc_font_name); |
944 | 0 | } |
945 | 0 | } else { |
946 | 0 | pdfi_countdown(font_name); |
947 | 0 | font_name = NULL; |
948 | 0 | } |
949 | 0 | } |
950 | | |
951 | 0 | if (!found) { |
952 | 0 | lookup_code = pdfi_dict_get(ctx, font, "Name", &font_name); |
953 | 0 | if (lookup_code >= 0 && pdfi_type_of(font_name) == PDF_NAME) { |
954 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "BaseFont", font_name); |
955 | 0 | pdfi_countdown(font_name); |
956 | 0 | if (code < 0) { |
957 | 0 | pdfi_countdown(font_info_dict); |
958 | 0 | return code; |
959 | 0 | } |
960 | 0 | } else { |
961 | 0 | pdfi_countdown(font_name); |
962 | 0 | } |
963 | 0 | } |
964 | | |
965 | 0 | array_obj = NULL; |
966 | 0 | } |
967 | | |
968 | 0 | code = pdfi_dict_get(ctx, font, "ToUnicode", &array_obj); |
969 | 0 | if (code >= 0) |
970 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_TRUE_OBJ); |
971 | 0 | else |
972 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_FALSE_OBJ); |
973 | 0 | pdfi_countdown(array_obj); |
974 | 0 | array_obj = NULL; |
975 | 0 | if (code < 0) |
976 | 0 | return code; |
977 | | |
978 | 0 | code = pdfi_dict_get(ctx, font, "FontDescriptor", &array_obj); |
979 | 0 | if (code >= 0) { |
980 | 0 | bool known = false; |
981 | |
|
982 | 0 | (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile", &known); |
983 | 0 | if (!known) { |
984 | 0 | (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile2", &known); |
985 | 0 | if (!known) { |
986 | 0 | (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile3", &known); |
987 | 0 | } |
988 | 0 | } |
989 | |
|
990 | 0 | if (known > 0) |
991 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_TRUE_OBJ); |
992 | 0 | else |
993 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ); |
994 | 0 | } else |
995 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ); |
996 | |
|
997 | 0 | pdfi_countdown(array_obj); |
998 | 0 | array_obj = NULL; |
999 | |
|
1000 | 0 | if (code < 0) |
1001 | 0 | return code; |
1002 | | |
1003 | | |
1004 | 0 | code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &array_obj); |
1005 | 0 | if (code > 0) { |
1006 | 0 | code = pdfi_dict_put(ctx, font_info_dict, "Subtype", array_obj); |
1007 | 0 | if (code < 0) { |
1008 | 0 | pdfi_countdown(array_obj); |
1009 | 0 | pdfi_countdown(font_info_dict); |
1010 | 0 | return code; |
1011 | 0 | } |
1012 | | |
1013 | 0 | if (pdfi_name_is((pdf_name *)array_obj, "Type3")) { |
1014 | 0 | pdfi_countdown(o); |
1015 | 0 | o = NULL; |
1016 | |
|
1017 | 0 | code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o); |
1018 | 0 | if (code > 0) |
1019 | 0 | (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); |
1020 | 0 | } |
1021 | |
|
1022 | 0 | if (pdfi_name_is((const pdf_name *)array_obj, "Type0")){ |
1023 | 0 | pdf_array *descendants = NULL; |
1024 | 0 | pdf_dict *desc_font = NULL; |
1025 | |
|
1026 | 0 | code = pdfi_dict_get(ctx, font, "DescendantFonts", (pdf_obj **)&descendants); |
1027 | 0 | if (code >= 0) { |
1028 | 0 | code = pdfi_array_get(ctx, descendants, 0, (pdf_obj **)&desc_font); |
1029 | 0 | if (code >= 0){ |
1030 | 0 | pdf_array *desc_array = NULL; |
1031 | |
|
1032 | 0 | code = pdfi_array_alloc(ctx, 0, &desc_array); |
1033 | 0 | if (code >= 0) { |
1034 | 0 | pdf_array *saved = tracker->font_array; |
1035 | |
|
1036 | 0 | pdfi_countup(desc_array); |
1037 | 0 | tracker->font_array = desc_array; |
1038 | 0 | (void)pdfi_check_Font(ctx, desc_font, page_dict, tracker); |
1039 | 0 | (void)pdfi_dict_put(ctx, font_info_dict, "Descendants", (pdf_obj *)tracker->font_array); |
1040 | 0 | pdfi_countdown((pdf_obj *)tracker->font_array); |
1041 | 0 | tracker->font_array = saved; |
1042 | 0 | } |
1043 | 0 | pdfi_countdown(descendants); |
1044 | 0 | pdfi_countdown(desc_font); |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 | } |
1048 | 0 | } |
1049 | 0 | pdfi_countdown(array_obj); |
1050 | 0 | array_obj = NULL; |
1051 | |
|
1052 | 0 | code = pdfi_array_alloc(ctx, pdfi_array_size(tracker->font_array) + 1, &new_fonts); |
1053 | 0 | if (code < 0) { |
1054 | 0 | pdfi_countdown(font_info_dict); |
1055 | 0 | return code; |
1056 | 0 | } |
1057 | 0 | pdfi_countup(new_fonts); |
1058 | |
|
1059 | 0 | for (index = 0; index < pdfi_array_size(tracker->font_array); index++) { |
1060 | 0 | code = pdfi_array_get(ctx, tracker->font_array, index, &array_obj); |
1061 | 0 | if (code < 0) { |
1062 | 0 | pdfi_countdown(font_info_dict); |
1063 | 0 | pdfi_countdown(new_fonts); |
1064 | 0 | return code; |
1065 | 0 | } |
1066 | 0 | code = pdfi_array_put(ctx, new_fonts, index, array_obj); |
1067 | 0 | pdfi_countdown(array_obj); |
1068 | 0 | if (code < 0) { |
1069 | 0 | pdfi_countdown(font_info_dict); |
1070 | 0 | pdfi_countdown(new_fonts); |
1071 | 0 | return code; |
1072 | 0 | } |
1073 | 0 | } |
1074 | 0 | code = pdfi_array_put(ctx, new_fonts, index, (pdf_obj *)font_info_dict); |
1075 | 0 | if (code < 0) { |
1076 | 0 | pdfi_countdown(font_info_dict); |
1077 | 0 | pdfi_countdown(new_fonts); |
1078 | 0 | return code; |
1079 | 0 | } |
1080 | 0 | pdfi_countdown(font_info_dict); |
1081 | 0 | pdfi_countdown(tracker->font_array); |
1082 | 0 | tracker->font_array = new_fonts; |
1083 | 301k | } else { |
1084 | 301k | code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &o); |
1085 | 301k | if (code > 0) { |
1086 | 299k | if (pdfi_name_is((pdf_name *)o, "Type3")) { |
1087 | 8.24k | pdfi_countdown(o); |
1088 | 8.24k | o = NULL; |
1089 | | |
1090 | 8.24k | code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o); |
1091 | 8.24k | if (code > 0) |
1092 | 2.70k | (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker); |
1093 | 8.24k | } |
1094 | 299k | } |
1095 | | |
1096 | 301k | pdfi_countdown(o); |
1097 | 301k | o = NULL; |
1098 | 301k | } |
1099 | | |
1100 | 301k | return 0; |
1101 | 301k | } |
1102 | | |
1103 | | /* |
1104 | | * Check the Resources dictionary Font entry. |
1105 | | */ |
1106 | | static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *page_dict, |
1107 | | pdfi_check_tracker_t *tracker) |
1108 | 596k | { |
1109 | 596k | int code = 0; |
1110 | 596k | uint64_t i, index; |
1111 | 596k | pdf_obj *Key = NULL, *Value = NULL; |
1112 | | |
1113 | 596k | if (resource_is_checked(tracker, (pdf_obj *)font_dict)) |
1114 | 126 | return 0; |
1115 | | |
1116 | 595k | if (pdfi_type_of(font_dict) != PDF_DICT) |
1117 | 403k | return_error(gs_error_typecheck); |
1118 | | |
1119 | 192k | if (pdfi_dict_entries(font_dict) > 0) { |
1120 | 191k | code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Font dictionary loop */ |
1121 | 191k | if (code < 0) |
1122 | 0 | return code; |
1123 | | |
1124 | | /* We don't want to store the dereferenced Font objects in the Resources dictionary |
1125 | | * Because we later create a substitute object, if we use the object here we will not |
1126 | | * find the correct font if we reference it through the Resources dictionary. |
1127 | | */ |
1128 | 191k | code = pdfi_dict_first_no_store_R(ctx, font_dict, &Key, &Value, &index); |
1129 | 191k | if (code < 0) { |
1130 | 40.8k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
1131 | 40.8k | goto error1; |
1132 | 40.8k | } |
1133 | | |
1134 | 150k | i = 1; |
1135 | 351k | do { |
1136 | 351k | if (pdfi_type_of(Value) == PDF_DICT) |
1137 | 265k | code = pdfi_check_Font(ctx, (pdf_dict *)Value, page_dict, tracker); |
1138 | 86.3k | else if (pdfi_type_of(Value) == PDF_FONT) { |
1139 | 79.3k | pdf_dict *d = ((pdf_font *)Value)->PDF_font; |
1140 | | |
1141 | 79.3k | code = pdfi_check_Font(ctx, d, page_dict, tracker); |
1142 | 79.3k | } else { |
1143 | 7.03k | pdfi_set_warning(ctx, 0, NULL, W_PDF_FONTRESOURCE_TYPE, "pdfi_check_Font_dict", ""); |
1144 | 7.03k | } |
1145 | 351k | if (code < 0) |
1146 | 0 | break; |
1147 | | |
1148 | 351k | pdfi_countdown(Key); |
1149 | 351k | Key = NULL; |
1150 | 351k | pdfi_countdown(Value); |
1151 | 351k | Value = NULL; |
1152 | | |
1153 | 351k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Font dictionary loop */ |
1154 | | |
1155 | 351k | code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Font dictionary loop */ |
1156 | 351k | if (code < 0) |
1157 | 0 | goto error1; |
1158 | | |
1159 | 351k | if (i++ >= pdfi_dict_entries(font_dict)) { |
1160 | 134k | code = 0; |
1161 | 134k | break; |
1162 | 134k | } |
1163 | | |
1164 | 217k | code = pdfi_dict_next_no_store_R(ctx, font_dict, &Key, &Value, &index); |
1165 | 217k | if (code < 0) |
1166 | 15.8k | break; |
1167 | 217k | }while (1); |
1168 | | |
1169 | 150k | (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */ |
1170 | 150k | } |
1171 | | |
1172 | 151k | pdfi_countdown(Key); |
1173 | 151k | pdfi_countdown(Value); |
1174 | | |
1175 | 192k | error1: |
1176 | 192k | return code; |
1177 | 151k | } |
1178 | | |
1179 | | static int pdfi_check_Resources(pdf_context *ctx, pdf_dict *Resources_dict, |
1180 | | pdf_dict *page_dict, pdfi_check_tracker_t *tracker) |
1181 | 597k | { |
1182 | 597k | int code; |
1183 | 597k | pdf_obj *d = NULL; |
1184 | | |
1185 | 597k | if (resource_is_checked(tracker, (pdf_obj *)Resources_dict)) |
1186 | 1.29k | return 0; |
1187 | | |
1188 | 596k | if (pdfi_type_of(Resources_dict) != PDF_DICT) |
1189 | 0 | return_error(gs_error_typecheck); |
1190 | | |
1191 | | /* First up, check any colour spaces, for new spot colours. |
1192 | | * We only do this if asked because its expensive. spot_dict being NULL |
1193 | | * means we aren't interested in spot colours (not a DeviceN or Separation device) |
1194 | | */ |
1195 | 596k | if (tracker->spot_dict != NULL) { |
1196 | 51.5k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "ColorSpace", PDF_DICT, &d); |
1197 | 51.5k | if (code < 0 && code != gs_error_undefined) { |
1198 | 16 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "ColorSpace")) < 0) |
1199 | 0 | return code; |
1200 | 16 | } |
1201 | 51.5k | (void)pdfi_check_ColorSpace_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1202 | | |
1203 | 51.5k | pdfi_countdown(d); |
1204 | 51.5k | d = NULL; |
1205 | | |
1206 | | /* Put the Resources dictionary back in cache (or promote it) in case checking ColorSpace dict |
1207 | | * created so many cache entires it flushed Resources from cache |
1208 | | */ |
1209 | 51.5k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1210 | 51.5k | if (code < 0) { |
1211 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1212 | 0 | return code; |
1213 | 0 | } |
1214 | | |
1215 | 51.5k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "Shading", PDF_DICT, &d); |
1216 | 51.5k | if (code < 0 && code != gs_error_undefined) { |
1217 | 11 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "Shading")) < 0) |
1218 | 0 | return code; |
1219 | 11 | } |
1220 | 51.5k | (void)pdfi_check_Shading_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1221 | 51.5k | pdfi_countdown(d); |
1222 | 51.5k | d = NULL; |
1223 | | |
1224 | | /* Put the Resources dictionary back in cache (or promote it) in case checking Shading dict |
1225 | | * created so many cache entires it flushed Resources from cache |
1226 | | */ |
1227 | 51.5k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1228 | 51.5k | if (code < 0) { |
1229 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1230 | 0 | return code; |
1231 | 0 | } |
1232 | 51.5k | } |
1233 | | |
1234 | 596k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "XObject", PDF_DICT, &d); |
1235 | 596k | if (code < 0 && code != gs_error_undefined) { |
1236 | 154 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "XObject")) < 0) |
1237 | 0 | return code; |
1238 | 154 | } |
1239 | 596k | code = pdfi_check_XObject_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1240 | 596k | if (code == gs_error_Fatal) { |
1241 | 0 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "XObject")) < 0) |
1242 | 0 | return code; |
1243 | 0 | } |
1244 | 596k | pdfi_countdown(d); |
1245 | 596k | d = NULL; |
1246 | | |
1247 | | /* Put the Resources dictionary back in cache (or promote it) in case checking XObject dict |
1248 | | * created so many cache entires it flushed Resources from cache |
1249 | | */ |
1250 | 596k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1251 | 596k | if (code < 0) { |
1252 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1253 | 0 | return code; |
1254 | 0 | } |
1255 | | |
1256 | 596k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "Pattern", PDF_DICT, &d); |
1257 | 596k | if (code < 0 && code != gs_error_undefined) { |
1258 | 129 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "Pattern")) < 0) { |
1259 | 0 | return code; |
1260 | 0 | } |
1261 | 129 | } |
1262 | 596k | (void)pdfi_check_Pattern_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1263 | 596k | pdfi_countdown(d); |
1264 | 596k | d = NULL; |
1265 | | |
1266 | | /* Put the Resources dictionary back in cache (or promote it) in case checking Pattern dict |
1267 | | * created so many cache entires it flushed Resources from cache |
1268 | | */ |
1269 | 596k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1270 | 596k | if (code < 0) { |
1271 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1272 | 0 | return code; |
1273 | 0 | } |
1274 | | |
1275 | 596k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "Font", PDF_DICT, &d); |
1276 | 596k | if (code < 0) { |
1277 | 6.94k | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "Font")) < 0) |
1278 | 0 | return code; |
1279 | 6.94k | } |
1280 | 596k | (void)pdfi_check_Font_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1281 | | /* From this point onwards, if we detect transparency (or have already detected it) we |
1282 | | * can exit, we have already counted up any spot colours. |
1283 | | */ |
1284 | 596k | pdfi_countdown(d); |
1285 | 596k | d = NULL; |
1286 | | |
1287 | | /* Put the Resources dictionary back in cache (or promote it) in case checking Font dict |
1288 | | * created so many cache entires it flushed Resources from cache |
1289 | | */ |
1290 | 596k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1291 | 596k | if (code < 0) { |
1292 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1293 | 0 | return code; |
1294 | 0 | } |
1295 | | |
1296 | 596k | code = pdfi_dict_knownget_type(ctx, Resources_dict, "ExtGState", PDF_DICT, &d); |
1297 | 596k | if (code < 0 && code != gs_error_undefined) { |
1298 | 109 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_RESOURCE, "pdfi_check_Resources", "ExtGState")) < 0) |
1299 | 0 | return code; |
1300 | 109 | } |
1301 | 596k | (void)pdfi_check_ExtGState_dict(ctx, (pdf_dict *)d, page_dict, tracker); |
1302 | 596k | pdfi_countdown(d); |
1303 | 596k | d = NULL; |
1304 | | |
1305 | | /* Put the Resources dictionary back in cache (or promote it) in case checking ExtGState dict |
1306 | | * created so many cache entires it flushed Resources from cache |
1307 | | */ |
1308 | 596k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources_dict); |
1309 | 596k | if (code < 0) { |
1310 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", "")) < 0) |
1311 | 0 | return code; |
1312 | 0 | } |
1313 | | |
1314 | 596k | return 0; |
1315 | 596k | } |
1316 | | |
1317 | | static int pdfi_check_annot_for_transparency(pdf_context *ctx, pdf_dict *annot, pdf_dict *page_dict, |
1318 | | pdfi_check_tracker_t *tracker) |
1319 | 896k | { |
1320 | 896k | int code; |
1321 | 896k | pdf_name *n; |
1322 | 896k | pdf_obj *N = NULL; |
1323 | 896k | pdf_dict *ap = NULL; |
1324 | 896k | pdf_dict *Resources = NULL; |
1325 | 896k | double f; |
1326 | | |
1327 | 896k | if (resource_is_checked(tracker, (pdf_obj *)annot)) |
1328 | 84 | return 0; |
1329 | | |
1330 | 896k | if (pdfi_type_of(annot) != PDF_DICT) |
1331 | 0 | return_error(gs_error_typecheck); |
1332 | | |
1333 | | /* Check #1 Does the (Normal) Appearnce stream use any Resources which include transparency. |
1334 | | * We check this first, because this also checks for spot colour spaces. Once we've done that we |
1335 | | * can exit the checks as soon as we detect transparency. |
1336 | | */ |
1337 | 896k | code = pdfi_dict_knownget_type(ctx, annot, "AP", PDF_DICT, (pdf_obj **)&ap); |
1338 | 896k | if (code > 0) |
1339 | 427k | { |
1340 | | /* Fetch without resolving indirect ref because pdfmark wants it that way later */ |
1341 | 427k | code = pdfi_dict_get_no_store_R(ctx, ap, "N", (pdf_obj **)&N); |
1342 | 427k | if (code >= 0) { |
1343 | 323k | pdf_dict *dict = NULL; |
1344 | | |
1345 | 323k | code = pdfi_dict_from_obj(ctx, N, &dict); |
1346 | 323k | if (code == 0) |
1347 | 323k | code = pdfi_dict_knownget_type(ctx, dict, "Resources", PDF_DICT, (pdf_obj **)&Resources); |
1348 | 323k | if (code > 0) |
1349 | 233k | code = pdfi_check_Resources(ctx, (pdf_dict *)Resources, page_dict, tracker); |
1350 | 323k | } |
1351 | 427k | if (code == gs_error_undefined) |
1352 | 96.7k | code = 0; |
1353 | 427k | } |
1354 | 896k | pdfi_countdown(ap); |
1355 | 896k | pdfi_countdown(N); |
1356 | 896k | pdfi_countdown(Resources); |
1357 | | |
1358 | 896k | if (code < 0) |
1359 | 25.6k | return code; |
1360 | | /* We've checked the Resources, and nothing else in an annotation can define spot colours, so |
1361 | | * if we detected transparency in the Resources we need not check further. |
1362 | | */ |
1363 | 870k | if (tracker->transparent == true) |
1364 | 11.5k | return 0; |
1365 | | |
1366 | 859k | code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&n); |
1367 | 859k | if (code < 0) { |
1368 | 1.14k | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_TYPE, "pdfi_check_annot_for_transparency", "")) < 0) { |
1369 | 0 | return code; |
1370 | 0 | } |
1371 | 857k | } else { |
1372 | | /* Check #2, Highlight annotations are always preformed with transparency */ |
1373 | 857k | if (pdfi_name_is((const pdf_name *)n, "Highlight")) { |
1374 | 2.41k | pdfi_countdown(n); |
1375 | 2.41k | tracker->transparent = true; |
1376 | 2.41k | return 0; |
1377 | 2.41k | } |
1378 | 855k | pdfi_countdown(n); |
1379 | 855k | n = NULL; |
1380 | | |
1381 | | /* Check #3 Blend Mode (BM) not being 'Normal' or 'Compatible' */ |
1382 | 855k | code = pdfi_dict_knownget_type(ctx, annot, "BM", PDF_NAME, (pdf_obj **)&n); |
1383 | 855k | if (code > 0) { |
1384 | 0 | if (!pdfi_name_is((const pdf_name *)n, "Normal")) { |
1385 | 0 | if (!pdfi_name_is((const pdf_name *)n, "Compatible")) { |
1386 | 0 | pdfi_countdown(n); |
1387 | 0 | tracker->transparent = true; |
1388 | 0 | return 0; |
1389 | 0 | } |
1390 | 0 | } |
1391 | 0 | code = 0; |
1392 | 0 | } |
1393 | 855k | pdfi_countdown(n); |
1394 | 855k | if (code < 0) |
1395 | 0 | return code; |
1396 | | |
1397 | | /* Check #4 stroke constant alpha (CA) is not 1 (100% opaque) */ |
1398 | 855k | code = pdfi_dict_knownget_number(ctx, annot, "CA", &f); |
1399 | 855k | if (code > 0) { |
1400 | 2.08k | if (f != 1.0) { |
1401 | 222 | tracker->transparent = true; |
1402 | 222 | return 0; |
1403 | 222 | } |
1404 | 2.08k | } |
1405 | 855k | if (code < 0) |
1406 | 0 | return code; |
1407 | | |
1408 | | /* Check #5 non-stroke constant alpha (ca) is not 1 (100% opaque) */ |
1409 | 855k | code = pdfi_dict_knownget_number(ctx, annot, "ca", &f); |
1410 | 855k | if (code > 0) { |
1411 | 0 | if (f != 1.0) { |
1412 | 0 | tracker->transparent = true; |
1413 | 0 | return 0; |
1414 | 0 | } |
1415 | 0 | } |
1416 | 855k | if (code < 0) |
1417 | 0 | return code; |
1418 | 855k | } |
1419 | | |
1420 | 856k | return 0; |
1421 | 859k | } |
1422 | | |
1423 | | static int pdfi_check_Annots_for_transparency(pdf_context *ctx, pdf_array *annots_array, |
1424 | | pdf_dict *page_dict, pdfi_check_tracker_t *tracker) |
1425 | 50.8k | { |
1426 | 50.8k | int i, code = 0; |
1427 | 50.8k | pdf_dict *annot = NULL; |
1428 | | |
1429 | 50.8k | if (resource_is_checked(tracker, (pdf_obj *)annots_array)) |
1430 | 0 | return 0; |
1431 | | |
1432 | 50.8k | if (pdfi_type_of(annots_array) != PDF_ARRAY) |
1433 | 0 | return_error(gs_error_typecheck); |
1434 | | |
1435 | 1.06M | for (i=0; i < pdfi_array_size(annots_array); i++) { |
1436 | 1.02M | code = pdfi_array_get_type(ctx, annots_array, (uint64_t)i, PDF_DICT, (pdf_obj **)&annot); |
1437 | 1.02M | if (code >= 0) { |
1438 | 896k | code = pdfi_check_annot_for_transparency(ctx, annot, page_dict, tracker); |
1439 | 896k | if (code < 0 && (code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_INVALID_TRANS_XOBJECT, "pdfi_check_Annots_for_transparency", "")) < 0) { |
1440 | 0 | goto exit; |
1441 | 0 | } |
1442 | | |
1443 | | /* If we've found transparency, and don't need to continue checkign for spot colours |
1444 | | * just exit as fast as possible. |
1445 | | */ |
1446 | 896k | if (tracker->transparent == true && tracker->spot_dict == NULL) |
1447 | 9.13k | goto exit; |
1448 | | |
1449 | 887k | pdfi_countdown(annot); |
1450 | 887k | annot = NULL; |
1451 | 887k | } |
1452 | 127k | else if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_INVALID_TRANS_XOBJECT, "pdfi_check_Annots_for_transparency", "")) < 0) { |
1453 | 0 | goto exit; |
1454 | 0 | } |
1455 | 1.02M | } |
1456 | 50.8k | exit: |
1457 | 50.8k | pdfi_countdown(annot); |
1458 | 50.8k | return code; |
1459 | 50.8k | } |
1460 | | |
1461 | | /* Check for transparency and spots on page. |
1462 | | * |
1463 | | * Sets ctx->spot_capable_device |
1464 | | * Builds a dictionary of the unique spot names in spot_dict |
1465 | | * Set 'transparent' to true if there is transparency on the page |
1466 | | * |
1467 | | * From the original PDF interpreter written in PostScript: |
1468 | | * Note: we deliberately don't check to see whether a Group is defined, |
1469 | | * because Adobe Illustrator 10 (and possibly other applications) define |
1470 | | * a page-level group whether transparency is actually used or not. |
1471 | | * Ignoring the presence of Group is justified because, in the absence |
1472 | | * of any other transparency features, they have no effect. |
1473 | | */ |
1474 | | static int pdfi_check_page_inner(pdf_context *ctx, pdf_dict *page_dict, |
1475 | | pdfi_check_tracker_t *tracker) |
1476 | 202k | { |
1477 | 202k | int code; |
1478 | 202k | pdf_dict *Resources = NULL; |
1479 | 202k | pdf_array *Annots = NULL; |
1480 | 202k | pdf_dict *Group = NULL; |
1481 | 202k | pdf_obj *CS = NULL; |
1482 | | |
1483 | 202k | tracker->transparent = false; |
1484 | | |
1485 | 202k | if (pdfi_type_of(page_dict) != PDF_DICT) |
1486 | 12 | return_error(gs_error_typecheck); |
1487 | | |
1488 | | |
1489 | | /* Check if the page dictionary has a page Group entry (for spots). |
1490 | | * Page group should mean the page has transparency but we ignore it for the purposes |
1491 | | * of transparency detection. See above. |
1492 | | */ |
1493 | 202k | if (tracker->spot_dict) { |
1494 | 22.9k | code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&Group); |
1495 | 22.9k | if (code > 0) { |
1496 | | /* If Group has a ColorSpace (CS), then check it for spot colours */ |
1497 | 3.03k | code = pdfi_dict_knownget(ctx, Group, "CS", &CS); |
1498 | 3.03k | if (code > 0) |
1499 | 2.95k | code = pdfi_check_ColorSpace_for_spots(ctx, CS, Group, page_dict, tracker->spot_dict); |
1500 | | |
1501 | 3.03k | if (code < 0) { |
1502 | 52 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_check_page_inner", "")) < 0) { |
1503 | 0 | goto exit; |
1504 | 0 | } |
1505 | 52 | } |
1506 | 3.03k | } |
1507 | 22.9k | } |
1508 | | |
1509 | | /* Now check any Resources dictionary in the Page dictionary */ |
1510 | 202k | code = pdfi_dict_knownget_type(ctx, page_dict, "Resources", PDF_DICT, (pdf_obj **)&Resources); |
1511 | 202k | if (code > 0) |
1512 | 191k | code = pdfi_check_Resources(ctx, Resources, page_dict, tracker); |
1513 | | |
1514 | 202k | if (code == gs_error_pdf_stackoverflow || (code < 0 && |
1515 | 7.62k | (code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_check_page_inner", "")) < 0)) { |
1516 | 0 | goto exit; |
1517 | 0 | } |
1518 | | |
1519 | | /* If we are drawing Annotations, check to see if the page uses any Annots */ |
1520 | 202k | if (ctx->args.showannots) { |
1521 | 202k | code = pdfi_dict_knownget_type(ctx, page_dict, "Annots", PDF_ARRAY, (pdf_obj **)&Annots); |
1522 | 202k | if (code > 0) |
1523 | 50.8k | code = pdfi_check_Annots_for_transparency(ctx, Annots, page_dict, |
1524 | 50.8k | tracker); |
1525 | 202k | if (code < 0) { |
1526 | 2.47k | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_check_page_inner", "")) < 0) { |
1527 | 0 | goto exit; |
1528 | 0 | } |
1529 | 2.47k | } |
1530 | 202k | } |
1531 | | |
1532 | 202k | code = 0; |
1533 | 202k | exit: |
1534 | 202k | pdfi_countdown(Resources); |
1535 | 202k | pdfi_countdown(Annots); |
1536 | 202k | pdfi_countdown(CS); |
1537 | 202k | pdfi_countdown(Group); |
1538 | 202k | return code; |
1539 | 202k | } |
1540 | | |
1541 | | /* Checks page for transparency, and sets up device for spots, if applicable |
1542 | | * Sets ctx->page.has_transparency and ctx->page.num_spots |
1543 | | * do_setup -- indicates whether to actually set up the device with the spot count. |
1544 | | */ |
1545 | | int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, pdf_array **fonts_array, pdf_array **spots_array, bool do_setup) |
1546 | 202k | { |
1547 | 202k | int code, code1; |
1548 | 202k | int spots = 0; |
1549 | 202k | pdfi_check_tracker_t tracker; |
1550 | 202k | pdf_dict *Resources = NULL; |
1551 | | |
1552 | 202k | ctx->page.num_spots = 0; |
1553 | 202k | ctx->page.has_transparency = false; |
1554 | | |
1555 | 202k | code = pdfi_check_init_tracker(ctx, &tracker, fonts_array, spots_array); |
1556 | 202k | if (code < 0) |
1557 | 0 | goto exit; |
1558 | | |
1559 | | /* Check for spots and transparency in this page */ |
1560 | 202k | code = pdfi_check_page_inner(ctx, page_dict, &tracker); |
1561 | 202k | if (code < 0) |
1562 | 12 | goto exit; |
1563 | | |
1564 | | /* Count the spots */ |
1565 | 202k | if (tracker.spot_dict) |
1566 | 22.9k | spots = pdfi_dict_entries(tracker.spot_dict); |
1567 | | |
1568 | | /* If setup requested, tell the device about spots and transparency */ |
1569 | 202k | if (do_setup) { |
1570 | 96 | gs_c_param_list list; |
1571 | 96 | int a = 0; |
1572 | 96 | pdf_name *Key = NULL; |
1573 | 96 | pdf_obj *Value = NULL; |
1574 | 96 | uint64_t index = 0; |
1575 | | |
1576 | 96 | gs_c_param_list_write(&list, ctx->memory); |
1577 | | |
1578 | | /* If there are spot colours (and by inference, the device renders spot plates) then |
1579 | | * send the number of Spots to the device, so it can setup correctly. |
1580 | | */ |
1581 | 96 | if (tracker.spot_dict) { |
1582 | | /* There is some awkwardness here. If the SeparationColorNames setting |
1583 | | * fails, we want to ignore it (this can mean that we exceeded the maximum |
1584 | | * number of colourants and some will be converted to CMYK). But if that happens, |
1585 | | * any other parameters in the same list which haven't already been prcoessed |
1586 | | * will be lost. So we need to send two lists, the SeparationColorNames and |
1587 | | * 'everything else'. |
1588 | | */ |
1589 | 0 | if (spots > 0) { |
1590 | 0 | gs_param_string_array sa; |
1591 | 0 | gs_param_string *table = NULL; |
1592 | |
|
1593 | 0 | table = (gs_param_string *)gs_alloc_byte_array(ctx->memory, spots, sizeof(gs_param_string), "SeparationNames"); |
1594 | 0 | if (table != NULL) |
1595 | 0 | { |
1596 | 0 | memset(table, 0x00, spots * sizeof(gs_param_string)); |
1597 | |
|
1598 | 0 | code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); |
1599 | 0 | while (code >= 0) |
1600 | 0 | { |
1601 | 0 | if (pdfi_type_of(Key) == PDF_NAME) { |
1602 | 0 | table[a].data = ((pdf_string *)Key)->data; |
1603 | 0 | table[a].size = ((pdf_string *)Key)->length; |
1604 | 0 | table[a++].persistent = false; |
1605 | 0 | } |
1606 | | /* Although we count down the returned PDF objects here, the pointers |
1607 | | * to the name data remain valid and won't move. Provided we don't |
1608 | | * retain the pointers after we free the tracker dictionary this is |
1609 | | * safe to do. |
1610 | | */ |
1611 | 0 | pdfi_countdown(Key); |
1612 | 0 | Key = NULL; |
1613 | 0 | pdfi_countdown(Value); |
1614 | 0 | Value = NULL; |
1615 | 0 | code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); |
1616 | 0 | } |
1617 | 0 | sa.data = table; |
1618 | 0 | sa.size = spots; |
1619 | 0 | sa.persistent = false; |
1620 | |
|
1621 | 0 | (void)param_write_string_array((gs_param_list *)&list, "SeparationColorNames", &sa); |
1622 | 0 | gs_c_param_list_read(&list); |
1623 | 0 | code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list); |
1624 | 0 | gs_c_param_list_release(&list); |
1625 | |
|
1626 | 0 | gs_free_object(ctx->memory, table, "SeparationNames"); |
1627 | 0 | if (code > 0) { |
1628 | | /* The device was closed, we need to reopen it */ |
1629 | 0 | code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device); |
1630 | 0 | if (code < 0) |
1631 | 0 | goto exit; |
1632 | 0 | gs_erasepage(ctx->pgs); |
1633 | 0 | } |
1634 | | |
1635 | | /* Reset the list back to being writeable */ |
1636 | 0 | gs_c_param_list_write(&list, ctx->memory); |
1637 | 0 | } |
1638 | 0 | else { |
1639 | 0 | code = gs_note_error(gs_error_VMerror); |
1640 | 0 | goto exit; |
1641 | 0 | } |
1642 | 0 | } |
1643 | | /* Update the number of spots */ |
1644 | 0 | param_write_int((gs_param_list *)&list, "PageSpotColors", &spots); |
1645 | 0 | } |
1646 | | /* Update the page transparency */ |
1647 | 96 | (void)param_write_bool((gs_param_list *)&list, "PageUsesTransparency", |
1648 | 96 | &tracker.transparent); |
1649 | 96 | gs_c_param_list_read(&list); |
1650 | 96 | code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list); |
1651 | 96 | gs_c_param_list_release(&list); |
1652 | | |
1653 | 96 | if (code > 0) { |
1654 | | /* The device was closed, we need to reopen it */ |
1655 | 0 | code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device); |
1656 | 0 | if (code < 0) |
1657 | 0 | goto exit; |
1658 | 0 | gs_erasepage(ctx->pgs); |
1659 | 0 | } |
1660 | 96 | } |
1661 | | |
1662 | | /* Set our values in the context, for caller */ |
1663 | 202k | if (!ctx->args.notransparency) |
1664 | 202k | ctx->page.has_transparency = tracker.transparent; |
1665 | 202k | ctx->page.num_spots = spots; |
1666 | 202k | ctx->page.has_OP = tracker.has_overprint; |
1667 | | |
1668 | | /* High level devices do not render overprint */ |
1669 | 202k | if (ctx->device_state.HighLevelDevice) |
1670 | 93.9k | ctx->page.has_OP = false; |
1671 | | |
1672 | 202k | exit: |
1673 | 202k | code1 = code; |
1674 | 202k | if (fonts_array != NULL) { |
1675 | 0 | *fonts_array = tracker.font_array; |
1676 | 0 | pdfi_countup(*fonts_array); |
1677 | 0 | } |
1678 | | |
1679 | 202k | if (spots_array != NULL && tracker.spot_dict != NULL && pdfi_dict_entries(tracker.spot_dict) != 0) { |
1680 | 0 | pdf_array *new_array = NULL; |
1681 | 0 | pdf_name *Key = NULL; |
1682 | 0 | pdf_obj *Value = NULL; |
1683 | 0 | uint64_t index = 0, a_index = 0; |
1684 | |
|
1685 | 0 | index = pdfi_dict_entries(tracker.spot_dict); |
1686 | |
|
1687 | 0 | code = pdfi_array_alloc(ctx, index, &new_array); |
1688 | 0 | if (code < 0) |
1689 | 0 | goto error; |
1690 | 0 | pdfi_countup(new_array); |
1691 | |
|
1692 | 0 | code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); |
1693 | 0 | while (code >= 0) |
1694 | 0 | { |
1695 | 0 | if (pdfi_type_of(Key) == PDF_NAME) { |
1696 | 0 | code = pdfi_array_put(ctx, new_array, a_index++, (pdf_obj *)Key); |
1697 | 0 | if (code < 0) { |
1698 | 0 | pdfi_countdown(new_array); |
1699 | 0 | pdfi_countdown(Key); |
1700 | 0 | pdfi_countdown(Value); |
1701 | 0 | goto error; |
1702 | 0 | } |
1703 | 0 | } |
1704 | | |
1705 | 0 | pdfi_countdown(Key); |
1706 | 0 | Key = NULL; |
1707 | 0 | pdfi_countdown(Value); |
1708 | 0 | Value = NULL; |
1709 | 0 | code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index); |
1710 | 0 | } |
1711 | 0 | *spots_array = new_array; |
1712 | 0 | } |
1713 | | |
1714 | | /* Put the Resources dictionary back in cache (or promote it) in case checking the page |
1715 | | * created so many cache entires it flushed Resources from cache |
1716 | | */ |
1717 | 202k | code = pdfi_dict_knownget_type(ctx, page_dict, "Resources", PDF_DICT, (pdf_obj **)&Resources); |
1718 | 202k | if (code > 0) { |
1719 | 191k | code = pdfi_cache_object(ctx, (pdf_obj *)Resources); |
1720 | 191k | if (code < 0) |
1721 | 0 | code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_CACHE_FAIL, "pdfi_check_Resources", ""); |
1722 | 191k | pdfi_countdown(Resources); |
1723 | 191k | } |
1724 | 10.6k | else { |
1725 | 10.6k | if (code < 0) { |
1726 | 7.63k | if (code == gs_error_undefined) |
1727 | 2.55k | code = 0; |
1728 | 5.08k | else |
1729 | 5.08k | code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BAD_PAGE_RESOURCES, "pdfi_check_page", NULL); |
1730 | 7.63k | } |
1731 | 10.6k | } |
1732 | | |
1733 | 202k | error: |
1734 | 202k | (void)pdfi_check_free_tracker(ctx, &tracker); |
1735 | 202k | if (code1 < 0) |
1736 | 12 | return code1; |
1737 | 202k | return code; |
1738 | 202k | } |