/src/ghostpdl/pdf/pdf_doc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2020-2025 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 | | /* Functions to deal with PDF structure, such as retrieving |
17 | | * the Info, Catalog, Root dictionaries, and finding resources |
18 | | * and page dictionaries. |
19 | | */ |
20 | | |
21 | | #include "ghostpdf.h" |
22 | | #include "pdf_stack.h" |
23 | | #include "pdf_deref.h" |
24 | | #include "pdf_array.h" |
25 | | #include "pdf_dict.h" |
26 | | #include "pdf_loop_detect.h" |
27 | | #include "pdf_misc.h" |
28 | | #include "pdf_repair.h" |
29 | | #include "pdf_doc.h" |
30 | | #include "pdf_mark.h" |
31 | | #include "pdf_colour.h" |
32 | | #include "pdf_device.h" |
33 | | |
34 | | int pdfi_read_Root(pdf_context *ctx) |
35 | 37.0k | { |
36 | 37.0k | pdf_obj *o, *o1; |
37 | 37.0k | pdf_dict *d; |
38 | 37.0k | int code; |
39 | | |
40 | 37.0k | if (ctx->args.pdfdebug) |
41 | 0 | outprintf(ctx->memory, "%% Reading Root dictionary\n"); |
42 | | |
43 | | /* Unusual code. This is because if the entry in the trailer dictionary causes |
44 | | * us to repair the file, the Trailer dictionary in the context can be replaced. |
45 | | * This counts it down and frees it, potentially while pdfi_dict_get is still |
46 | | * using it! Rather than countup and down in the dict_get routine, which is |
47 | | * normally unnecessary, count it up and down round the access here. |
48 | | */ |
49 | 37.0k | d = ctx->Trailer; |
50 | 37.0k | pdfi_countup(d); |
51 | 37.0k | code = pdfi_dict_get(ctx, d, "Root", &o1); |
52 | 37.0k | if (code < 0) { |
53 | 956 | pdfi_countdown(d); |
54 | 956 | return code; |
55 | 956 | } |
56 | 36.1k | pdfi_countdown(d); |
57 | | |
58 | 36.1k | if (pdfi_type_of(o1) == PDF_INDIRECT) { |
59 | 0 | code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num, ((pdf_indirect_ref *)o1)->ref_generation_num, &o); |
60 | 0 | pdfi_countdown(o1); |
61 | 0 | if (code < 0) |
62 | 0 | return code; |
63 | | |
64 | 0 | if (pdfi_type_of(o) != PDF_DICT) { |
65 | 0 | pdfi_countdown(o); |
66 | 0 | return_error(gs_error_typecheck); |
67 | 0 | } |
68 | | |
69 | 0 | code = pdfi_dict_put(ctx, ctx->Trailer, "Root", o); |
70 | 0 | if (code < 0) { |
71 | 0 | pdfi_countdown(o); |
72 | 0 | return code; |
73 | 0 | } |
74 | 0 | o1 = o; |
75 | 36.1k | } else { |
76 | 36.1k | if (pdfi_type_of(o1) != PDF_DICT) { |
77 | 189 | pdfi_countdown(o1); |
78 | 189 | if (ctx->Root == NULL) |
79 | 68 | return_error(gs_error_typecheck); |
80 | 121 | return 0; |
81 | 189 | } |
82 | 36.1k | } |
83 | | |
84 | 35.9k | code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Type", PDF_NAME, &o); |
85 | 35.9k | if (code < 0) { |
86 | 274 | bool known = false; |
87 | | |
88 | 274 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_MISSINGTYPE, "pdfi_read_Root", NULL)) < 0) { |
89 | 0 | pdfi_countdown(o1); |
90 | 0 | return code; |
91 | 0 | } |
92 | | |
93 | | /* Missing the *required* /Type key! See if it has /Pages at least, if it does carry on */ |
94 | 274 | code = pdfi_dict_known(ctx, (pdf_dict *)o1, "Pages", &known); |
95 | 274 | if (code < 0 || known == false) { |
96 | 100 | pdfi_countdown(o1); |
97 | 100 | return code; |
98 | 100 | } |
99 | 274 | } |
100 | 35.6k | else { |
101 | 35.6k | if (pdfi_name_strcmp((pdf_name *)o, "Catalog") != 0){ |
102 | 577 | pdf_obj *pages = NULL; |
103 | | |
104 | | /* Bug #706038 has a Root dictionary with /Type /Calalog which (of course) Acrobat |
105 | | * happily opens :-( So if we find a /Type and it's not /Catalog, try and see if |
106 | | * we have a /Pages key (the only other required entry). If we do assume that the |
107 | | * Type is wrong and use this dictionary, otherwise fall back to seeing if we |
108 | | * have a repaired Root. See above for handling a missing /Type..... |
109 | | */ |
110 | 577 | code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Pages", PDF_DICT, &pages); |
111 | 577 | if (code < 0) { |
112 | 299 | pdfi_countdown(o); |
113 | 299 | pdfi_countdown(o1); |
114 | | /* If we repaired the file, we may already have spotted a potential Root dictionary |
115 | | * so if the one we found here isn't valid, try the one we found when scanning |
116 | | */ |
117 | 299 | if (ctx->Root == NULL) { |
118 | 63 | pdfi_set_error(ctx, 0, NULL, E_PDF_NO_ROOT, "pdfi_read_Root", NULL); |
119 | 63 | return_error(gs_error_syntaxerror); |
120 | 63 | } |
121 | 236 | return 0; |
122 | 299 | } |
123 | 278 | pdfi_countdown(pages); |
124 | 278 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BAD_ROOT_TYPE, "pdfi_read_Root", NULL)) < 0) { |
125 | 0 | pdfi_countdown(o); |
126 | 0 | pdfi_countdown(o1); |
127 | 0 | return code; |
128 | 0 | } |
129 | 278 | } |
130 | 35.3k | pdfi_countdown(o); |
131 | 35.3k | } |
132 | | |
133 | 35.5k | if (ctx->args.pdfdebug) |
134 | 0 | outprintf(ctx->memory, "\n"); |
135 | | /* We don't pdfi_countdown(o1) now, because we've transferred our |
136 | | * reference to the pointer in the pdf_context structure. |
137 | | */ |
138 | 35.5k | pdfi_countdown(ctx->Root); /* If file was repaired it might be set already */ |
139 | 35.5k | ctx->Root = (pdf_dict *)o1; |
140 | 35.5k | return 0; |
141 | 35.9k | } |
142 | | |
143 | | static int Info_check_dict(pdf_context *ctx, pdf_dict *d); |
144 | | |
145 | | static int Info_check_array(pdf_context *ctx, pdf_array *a) |
146 | 3.77k | { |
147 | 3.77k | int code = 0, i = 0; |
148 | 3.77k | pdf_obj *array_obj = NULL; |
149 | | |
150 | 3.77k | code = pdfi_loop_detector_mark(ctx); |
151 | 3.77k | if (code < 0) |
152 | 0 | return code; |
153 | | |
154 | 36.7k | for (i = 0;i < pdfi_array_size(a); i++) { |
155 | 33.5k | code = pdfi_array_fetch_recursing(ctx, a, i, &array_obj, true, true); |
156 | 33.5k | if (code < 0) |
157 | 285 | goto error; |
158 | | |
159 | 33.2k | switch(pdfi_type_of(array_obj)) { |
160 | 920 | case PDF_DICT: |
161 | 920 | if (array_obj->object_num != 0) { |
162 | 882 | code = pdfi_loop_detector_add_object(ctx, array_obj->object_num); |
163 | 882 | if (code < 0) |
164 | 0 | goto error; |
165 | 882 | } |
166 | 920 | code = Info_check_dict(ctx, (pdf_dict *)array_obj); |
167 | 920 | if (code < 0) { |
168 | 239 | if (array_obj->object_num != 0) { |
169 | 239 | pdf_indirect_ref *i_o; |
170 | | |
171 | 239 | code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&i_o); |
172 | 239 | if (code < 0) |
173 | 0 | goto error; |
174 | 239 | i_o->ref_generation_num = array_obj->generation_num; |
175 | 239 | i_o->ref_object_num = array_obj->object_num; |
176 | 239 | i_o->indirect_num = array_obj->object_num; |
177 | 239 | i_o->indirect_gen = array_obj->generation_num; |
178 | 239 | code = pdfi_array_put(ctx, a, i, (pdf_obj *)i_o); |
179 | 239 | if (code < 0) |
180 | 0 | return code; |
181 | 239 | } |
182 | 239 | goto error; |
183 | 239 | } |
184 | 681 | break; |
185 | 681 | case PDF_ARRAY: |
186 | 190 | if (array_obj->object_num != 0) { |
187 | 42 | code = pdfi_loop_detector_add_object(ctx, array_obj->object_num); |
188 | 42 | if (code < 0) |
189 | 0 | goto error; |
190 | 42 | } |
191 | 190 | code = Info_check_array(ctx, (pdf_array *)array_obj); |
192 | 190 | if (code < 0) { |
193 | 11 | if (array_obj->object_num != 0) { |
194 | 11 | pdf_indirect_ref *i_o; |
195 | | |
196 | 11 | code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&i_o); |
197 | 11 | if (code < 0) |
198 | 0 | goto error; |
199 | 11 | i_o->ref_generation_num = array_obj->generation_num; |
200 | 11 | i_o->ref_object_num = array_obj->object_num; |
201 | 11 | i_o->indirect_num = array_obj->object_num; |
202 | 11 | i_o->indirect_gen = array_obj->generation_num; |
203 | 11 | code = pdfi_array_put(ctx, a, i, (pdf_obj *)i_o); |
204 | 11 | if (code < 0) |
205 | 0 | return code; |
206 | 11 | } |
207 | 11 | goto error; |
208 | 11 | } |
209 | 179 | break; |
210 | 32.1k | default: |
211 | 32.1k | break; |
212 | 33.2k | } |
213 | | |
214 | 32.9k | pdfi_countdown(array_obj); |
215 | 32.9k | array_obj = NULL; |
216 | 32.9k | } |
217 | 3.77k | error: |
218 | 3.77k | pdfi_countdown(array_obj); |
219 | 3.77k | pdfi_loop_detector_cleartomark(ctx); |
220 | 3.77k | return code; |
221 | 3.77k | } |
222 | | |
223 | | static int Info_check_dict(pdf_context *ctx, pdf_dict *d) |
224 | 3.36k | { |
225 | 3.36k | int code = 0; |
226 | 3.36k | uint64_t index = 0; |
227 | 3.36k | pdf_name *Key = NULL; |
228 | 3.36k | pdf_obj *Value = NULL; |
229 | | |
230 | 3.36k | code = pdfi_loop_detector_mark(ctx); |
231 | 3.36k | if (code < 0) |
232 | 0 | return code; |
233 | | |
234 | 3.36k | code = pdfi_dict_first(ctx, d, (pdf_obj **)&Key, &Value, &index); |
235 | 3.36k | if (code < 0) { |
236 | 365 | if (code == gs_error_undefined) |
237 | 284 | code = 0; |
238 | 365 | goto error; |
239 | 365 | } |
240 | | |
241 | 11.6k | while (code >= 0) { |
242 | 11.4k | switch(pdfi_type_of(Value)) { |
243 | 1.72k | case PDF_DICT: |
244 | 1.72k | if (Value->object_num != 0) { |
245 | 817 | code = pdfi_loop_detector_add_object(ctx, Value->object_num); |
246 | 817 | if (code < 0) |
247 | 0 | goto error; |
248 | 817 | } |
249 | 1.72k | code = Info_check_dict(ctx, (pdf_dict *)Value); |
250 | 1.72k | if (code < 0) |
251 | 366 | goto error; |
252 | 1.36k | break; |
253 | 2.29k | case PDF_ARRAY: |
254 | 2.29k | if (Value->object_num != 0) { |
255 | 116 | code = pdfi_loop_detector_add_object(ctx, Value->object_num); |
256 | 116 | if (code < 0) |
257 | 0 | goto error; |
258 | 116 | } |
259 | 2.29k | code = Info_check_array(ctx, (pdf_array *)Value); |
260 | 2.29k | if (code < 0) |
261 | 243 | goto error; |
262 | 2.05k | break; |
263 | 7.41k | default: |
264 | 7.41k | break; |
265 | 11.4k | } |
266 | 10.8k | pdfi_countdown(Key); |
267 | 10.8k | Key = NULL; |
268 | 10.8k | pdfi_countdown(Value); |
269 | 10.8k | Value = NULL; |
270 | | |
271 | 10.8k | code = pdfi_dict_next(ctx, d, (pdf_obj **)&Key, &Value, &index); |
272 | 10.8k | if (code == gs_error_undefined) { |
273 | 2.14k | code = 0; |
274 | 2.14k | break; |
275 | 2.14k | } |
276 | 10.8k | } |
277 | 3.36k | error: |
278 | 3.36k | pdfi_countdown(Key); |
279 | 3.36k | pdfi_countdown(Value); |
280 | 3.36k | pdfi_loop_detector_cleartomark(ctx); |
281 | 3.36k | return code; |
282 | 2.99k | } |
283 | | |
284 | | static int pdfi_sanitize_Info_references(pdf_context *ctx, pdf_dict *Info) |
285 | 22.2k | { |
286 | 22.2k | int code = 0; |
287 | 22.2k | uint64_t index = 0; |
288 | 22.2k | pdf_name *Key = NULL; |
289 | 22.2k | pdf_obj *Value = NULL; |
290 | | |
291 | 22.6k | restart_scan: |
292 | 22.6k | code = pdfi_loop_detector_mark(ctx); |
293 | 22.6k | if (code < 0) |
294 | 0 | return code; |
295 | | |
296 | 22.6k | code = pdfi_dict_first(ctx, Info, (pdf_obj **)&Key, &Value, &index); |
297 | 22.6k | if (code == gs_error_undefined) { |
298 | 488 | code = 0; |
299 | 488 | goto error; |
300 | 488 | } |
301 | | |
302 | 108k | while (code >= 0) { |
303 | 108k | switch(pdfi_type_of(Value)) { |
304 | 713 | case PDF_DICT: |
305 | 713 | code = Info_check_dict(ctx, (pdf_dict *)Value); |
306 | 713 | break; |
307 | 1.28k | case PDF_ARRAY: |
308 | 1.28k | code = Info_check_array(ctx, (pdf_array *)Value); |
309 | 1.28k | break; |
310 | 106k | default: |
311 | 106k | code = 0; |
312 | 106k | break; |
313 | 108k | } |
314 | 108k | pdfi_countdown(Value); |
315 | 108k | Value = NULL; |
316 | 108k | if (code < 0) { |
317 | 359 | code = pdfi_dict_delete_pair(ctx, Info, Key); |
318 | 359 | if (code < 0) |
319 | 0 | goto error; |
320 | 359 | pdfi_countdown(Key); |
321 | 359 | Key = NULL; |
322 | | |
323 | 359 | pdfi_loop_detector_cleartomark(ctx); |
324 | 359 | goto restart_scan; |
325 | 359 | } |
326 | 107k | pdfi_countdown(Key); |
327 | 107k | Key = NULL; |
328 | | |
329 | 107k | pdfi_loop_detector_cleartomark(ctx); |
330 | 107k | code = pdfi_loop_detector_mark(ctx); |
331 | 107k | if (code < 0) { |
332 | 0 | pdfi_countdown(Key); |
333 | 0 | pdfi_countdown(Value); |
334 | 0 | return code; |
335 | 0 | } |
336 | | |
337 | 107k | code = pdfi_dict_next(ctx, Info, (pdf_obj **)&Key, &Value, &index); |
338 | 107k | if (code == gs_error_undefined) { |
339 | 21.6k | code = 0; |
340 | 21.6k | break; |
341 | 21.6k | } |
342 | 107k | } |
343 | 22.2k | error: |
344 | 22.2k | pdfi_countdown(Key); |
345 | 22.2k | pdfi_countdown(Value); |
346 | 22.2k | pdfi_loop_detector_cleartomark(ctx); |
347 | 22.2k | return code; |
348 | 22.1k | } |
349 | | |
350 | | int pdfi_read_Info(pdf_context *ctx) |
351 | 35.9k | { |
352 | 35.9k | pdf_dict *Info; |
353 | 35.9k | int code; |
354 | 35.9k | pdf_dict *d; |
355 | | |
356 | 35.9k | if (ctx->args.pdfdebug) |
357 | 0 | outprintf(ctx->memory, "%% Reading Info dictionary\n"); |
358 | | |
359 | | /* See comment in pdfi_read_Root() for details */ |
360 | 35.9k | d = ctx->Trailer; |
361 | 35.9k | pdfi_countup(d); |
362 | 35.9k | code = pdfi_dict_get_type(ctx, ctx->Trailer, "Info", PDF_DICT, (pdf_obj **)&Info); |
363 | 35.9k | pdfi_countdown(d); |
364 | 35.9k | if (code < 0) |
365 | 13.7k | return code; |
366 | | |
367 | 22.2k | if (ctx->args.pdfdebug) |
368 | 0 | outprintf(ctx->memory, "\n"); |
369 | | |
370 | 22.2k | code = pdfi_loop_detector_mark(ctx); |
371 | 22.2k | if (code < 0) |
372 | 0 | goto error; |
373 | 22.2k | if (Info->object_num != 0) { |
374 | 22.2k | code = pdfi_loop_detector_add_object(ctx, Info->object_num); |
375 | 22.2k | if (code < 0) |
376 | 0 | goto error1; |
377 | 22.2k | } else { |
378 | 0 | if ((code = pdfi_set_warning_stop(ctx, 0, NULL, W_PDF_INFO_NOT_INDIRECT, "pdfi_read_Info", "")) < 0) |
379 | 0 | goto error1; |
380 | 0 | } |
381 | | |
382 | | /* sanitize Info for circular references */ |
383 | 22.2k | code = pdfi_sanitize_Info_references(ctx, Info); |
384 | 22.2k | if (code < 0) |
385 | 68 | goto error1; |
386 | | |
387 | 22.1k | (void)pdfi_loop_detector_cleartomark(ctx); |
388 | | |
389 | 22.1k | pdfi_pdfmark_write_docinfo(ctx, Info); |
390 | | |
391 | | /* We don't pdfi_countdown(Info) now, because we've transferred our |
392 | | * reference to the pointer in the pdf_context structure. |
393 | | */ |
394 | 22.1k | ctx->Info = Info; |
395 | 22.1k | return 0; |
396 | | |
397 | 68 | error1: |
398 | 68 | pdfi_loop_detector_cleartomark(ctx); |
399 | 68 | error: |
400 | 68 | pdfi_countdown(Info); |
401 | 68 | return code; |
402 | 68 | } |
403 | | |
404 | | int pdfi_read_Pages(pdf_context *ctx) |
405 | 77.3k | { |
406 | 77.3k | pdf_obj *o, *o1; |
407 | 77.3k | pdf_array *a = NULL; |
408 | 77.3k | int code, pagecount = 0; |
409 | 77.3k | double d; |
410 | | |
411 | 77.3k | if (ctx->args.pdfdebug) |
412 | 0 | outprintf(ctx->memory, "%% Reading Pages dictionary\n"); |
413 | | |
414 | 77.3k | code = pdfi_dict_get(ctx, ctx->Root, "Pages", &o1); |
415 | 77.3k | if (code < 0) |
416 | 2.41k | return code; |
417 | | |
418 | 74.9k | if (pdfi_type_of(o1) == PDF_INDIRECT) { |
419 | 0 | code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num, ((pdf_indirect_ref *)o1)->ref_generation_num, &o); |
420 | 0 | pdfi_countdown(o1); |
421 | 0 | if (code < 0) |
422 | 0 | return code; |
423 | | |
424 | 0 | if (pdfi_type_of(o) != PDF_DICT) { |
425 | 0 | pdfi_countdown(o); |
426 | 0 | if (pdfi_type_of(o) == PDF_INDIRECT) |
427 | 0 | pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary. Giving up."); |
428 | 0 | else |
429 | 0 | pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary. Giving up.\n Double indirect reference. Loop in Pages tree?"); |
430 | 0 | return_error(gs_error_typecheck); |
431 | 0 | } |
432 | | |
433 | 0 | code = pdfi_dict_put(ctx, ctx->Root, "Pages", o); |
434 | 0 | if (code < 0) { |
435 | 0 | pdfi_countdown(o); |
436 | 0 | return code; |
437 | 0 | } |
438 | 0 | o1 = o; |
439 | 74.9k | } else { |
440 | 74.9k | if (pdfi_type_of(o1) != PDF_DICT) { |
441 | 128 | pdfi_countdown(o1); |
442 | 128 | return_error(gs_error_typecheck); |
443 | 128 | } |
444 | 74.9k | } |
445 | | |
446 | 74.7k | if (ctx->args.pdfdebug) |
447 | 0 | outprintf(ctx->memory, "\n"); |
448 | | |
449 | | /* Acrobat allows the Pages Count to be a floating point number (!) */ |
450 | | /* sample w_a.PDF from Bug688419 (not on the cluster, maybe it should be?) has no /Count entry because |
451 | | * The Root dictionary Pages key points directly to a single dictionary of type /Page. This is plainly |
452 | | * illegal but Acrobat can deal with it. We do so by ignoring the error her, and adding logic in |
453 | | * pdfi_get_page_dict() which notes that ctx->PagesTree is NULL and tries to get the single Page |
454 | | * dictionary from the Root instead of using the PagesTree. |
455 | | */ |
456 | 74.7k | code = pdfi_dict_get_number(ctx, (pdf_dict *)o1, "Count", &d); |
457 | 74.7k | if (code < 0) { |
458 | 741 | if (code == gs_error_undefined) { |
459 | 708 | pdf_name *n = NULL; |
460 | | /* It may be that the Root dictionary Pages entry points directly to a sinlge Page dictionary |
461 | | * See if the dictionary has a Type of /Page, if so don't throw an error and the pdf_page.c |
462 | | * logic in pdfi_get_page_dict() logic will take care of it. |
463 | | */ |
464 | 708 | code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Type", PDF_NAME, (pdf_obj **)&n); |
465 | 708 | if (code == 0) { |
466 | 667 | if(pdfi_name_is(n, "Page")) { |
467 | 542 | ctx->num_pages = 1; |
468 | 542 | code = 0; |
469 | 542 | } |
470 | 125 | else |
471 | 125 | code = gs_error_undefined; |
472 | 667 | pdfi_countdown(n); |
473 | 667 | } |
474 | 708 | } |
475 | 741 | pdfi_countdown(o1); |
476 | 741 | return code; |
477 | 741 | } |
478 | | |
479 | 74.0k | if (floor(d) != d) { |
480 | 0 | pdfi_countdown(o1); |
481 | 0 | return_error(gs_error_rangecheck); |
482 | 74.0k | } else { |
483 | 74.0k | ctx->num_pages = (int)floor(d); |
484 | 74.0k | } |
485 | | |
486 | | /* A simple confidence check in the value of Count. We only do this because |
487 | | * the OSS-fuzz tool keeps on coming up with files that time out because the |
488 | | * initial Count is insanely huge, and we spend much time trying to find |
489 | | * millions of pages which don't exist. |
490 | | */ |
491 | 74.0k | code = pdfi_dict_knownget_type(ctx, (pdf_dict *)o1, "Kids", PDF_ARRAY, (pdf_obj **)&a); |
492 | 74.0k | if (code == 0) |
493 | 32 | code = gs_note_error(gs_error_undefined); |
494 | 74.0k | if (code < 0) { |
495 | 32 | pdfi_countdown(o1); |
496 | 32 | return code; |
497 | 32 | } |
498 | | |
499 | | /* Firstly check if the Kids array has enough nodes, in which case it's |
500 | | * probably flat (the common case) |
501 | | */ |
502 | 74.0k | if (a->size != ctx->num_pages) { |
503 | 1.80k | int i = 0; |
504 | 1.80k | pdf_obj *p = NULL, *p1 = NULL; |
505 | 1.80k | pdf_num *c = NULL; |
506 | | |
507 | | /* Either its not a flat tree, or the top node /Count is incorrect. |
508 | | * Get each entry in the Kids array in turn and total the /Count of |
509 | | * each node and add any leaf nodes. |
510 | | */ |
511 | 10.6k | for (i=0;i < a->size; i++) { |
512 | | /* We must not allow the entry in the array to be replaced, in case it's a circular reference */ |
513 | 8.80k | code = pdfi_array_get_no_store_R(ctx, a, i, &p); |
514 | 8.80k | if (code < 0) |
515 | 2.70k | continue; |
516 | 6.09k | if (pdfi_type_of(p) != PDF_DICT) { |
517 | 1.15k | pdfi_countdown(p); |
518 | 1.15k | p = NULL; |
519 | 1.15k | continue; |
520 | 1.15k | } |
521 | | /* Explicit check that the root node Kids array entry is not a self-reference |
522 | | * back to the root node. We only check one level of the Kids array. so we don't |
523 | | * need a full loop detection setup here. |
524 | | */ |
525 | 4.93k | if (p->object_num != 0 && p->object_num == o1->object_num) { |
526 | 6 | pdfi_countdown(p); |
527 | 6 | p = NULL; |
528 | 6 | pdfi_countdown(a); |
529 | 6 | pdfi_countdown(o1); |
530 | 6 | ctx->num_pages = 0; |
531 | 6 | return_error(gs_error_circular_reference); |
532 | 6 | } |
533 | | /* Now we've checked for circular reference we can replace the entry in the |
534 | | * array, and add the object to the cache. |
535 | | * These are optimisations, so we don't especially care whether they succeed |
536 | | */ |
537 | 4.93k | (void)pdfi_array_put(ctx, a, i, p); |
538 | 4.93k | (void)replace_cache_entry(ctx, p); |
539 | 4.93k | code = pdfi_dict_knownget_type(ctx, (pdf_dict *)p, "Type", PDF_NAME, (pdf_obj **)&p1); |
540 | 4.93k | if (code <= 0) { |
541 | 34 | pdfi_countdown(p); |
542 | 34 | p = NULL; |
543 | 34 | continue; |
544 | 34 | } |
545 | 4.89k | if (pdfi_name_is((pdf_name *)p1, "Page")) { |
546 | 1.72k | pagecount++; |
547 | 3.16k | } else { |
548 | 3.16k | if (pdfi_name_is((pdf_name *)p1, "Pages")) { |
549 | 3.08k | code = pdfi_dict_knownget(ctx, (pdf_dict *)p, "Count", (pdf_obj **)&c); |
550 | 3.08k | if (code >= 0) { |
551 | 3.08k | if (pdfi_type_of(c) == PDF_INT) |
552 | 3.07k | pagecount += c->value.i; |
553 | 3.08k | if (pdfi_type_of(c) == PDF_REAL) |
554 | 0 | pagecount += (int)c->value.d; |
555 | 3.08k | pdfi_countdown(c); |
556 | 3.08k | c = NULL; |
557 | 3.08k | } |
558 | 3.08k | } |
559 | 3.16k | } |
560 | 4.89k | pdfi_countdown(p1); |
561 | 4.89k | p1 = NULL; |
562 | 4.89k | pdfi_countdown(p); |
563 | 4.89k | p = NULL; |
564 | 4.89k | } |
565 | 1.80k | } else |
566 | 72.1k | pagecount = a->size; |
567 | | |
568 | 73.9k | pdfi_countdown(a); |
569 | | |
570 | | /* If the count of the top level of the tree doesn't match the /Count |
571 | | * of the root node then something is wrong. We could abort right now |
572 | | * and will if this continues to be a problem, but initially let's assume |
573 | | * the count of the top level is correct and the root node /Count is wrong. |
574 | | * This will allow us to recover if only the root /Count gets corrupted. |
575 | | * In future we could also try validating the entire tree at this point, |
576 | | * though I suspect that's pointless; if the tree is corrupted we aren't |
577 | | * likely to get much that's usable from it. |
578 | | */ |
579 | 73.9k | if (pagecount != ctx->num_pages) { |
580 | 732 | ctx->num_pages = pagecount; |
581 | 732 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_BADPAGECOUNT, "pdfi_read_Pages", NULL)) < 0) |
582 | 0 | return code; |
583 | 732 | code = pdfi_dict_put_int(ctx, (pdf_dict *)o1, "Count", ctx->num_pages); |
584 | 732 | if (code < 0) { |
585 | 0 | pdfi_countdown(o1); |
586 | 0 | return code; |
587 | 0 | } |
588 | 732 | } |
589 | | |
590 | | /* We don't pdfi_countdown(o1) now, because we've transferred our |
591 | | * reference to the pointer in the pdf_context structure. |
592 | | */ |
593 | 73.9k | ctx->PagesTree = (pdf_dict *)o1; |
594 | 73.9k | return 0; |
595 | 73.9k | } |
596 | | |
597 | | /* Read optional things in from Root */ |
598 | | void pdfi_read_OptionalRoot(pdf_context *ctx) |
599 | 74.5k | { |
600 | 74.5k | pdf_obj *obj = NULL; |
601 | 74.5k | int code; |
602 | 74.5k | bool known; |
603 | | |
604 | 74.5k | if (ctx->args.pdfdebug) |
605 | 0 | outprintf(ctx->memory, "%% Reading other Root contents\n"); |
606 | | |
607 | 74.5k | if (ctx->args.pdfdebug) |
608 | 0 | outprintf(ctx->memory, "%% OCProperties\n"); |
609 | 74.5k | code = pdfi_dict_get_type(ctx, ctx->Root, "OCProperties", PDF_DICT, &obj); |
610 | 74.5k | if (code == 0) { |
611 | 3.78k | ctx->OCProperties = (pdf_dict *)obj; |
612 | 70.7k | } else { |
613 | 70.7k | ctx->OCProperties = NULL; |
614 | 70.7k | if (ctx->args.pdfdebug) |
615 | 0 | outprintf(ctx->memory, "%% (None)\n"); |
616 | 70.7k | } |
617 | | |
618 | 74.5k | (void)pdfi_dict_known(ctx, ctx->Root, "Collection", &known); |
619 | | |
620 | 74.5k | if (known) { |
621 | 0 | if (ctx->args.pdfdebug) |
622 | 0 | outprintf(ctx->memory, "%% Collection\n"); |
623 | 0 | code = pdfi_dict_get(ctx, ctx->Root, "Collection", (pdf_obj **)&ctx->Collection); |
624 | 0 | if (code < 0) { |
625 | 0 | (void)pdfi_set_warning_stop(ctx, 0, NULL, W_PDF_BAD_COLLECTION, "pdfi_read_OptionalRoot", ""); |
626 | 0 | } |
627 | 0 | } |
628 | 74.5k | } |
629 | | |
630 | | void pdfi_free_OptionalRoot(pdf_context *ctx) |
631 | 94.6k | { |
632 | 94.6k | if (ctx->OCProperties) { |
633 | 3.78k | pdfi_countdown(ctx->OCProperties); |
634 | 3.78k | ctx->OCProperties = NULL; |
635 | 3.78k | } |
636 | 94.6k | if (ctx->Collection) { |
637 | 0 | pdfi_countdown(ctx->Collection); |
638 | 0 | ctx->Collection = NULL; |
639 | 0 | } |
640 | 94.6k | } |
641 | | |
642 | | /* Handle child node processing for page_dict */ |
643 | | static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **pchild) |
644 | 712k | { |
645 | 712k | pdf_indirect_ref *node = NULL; |
646 | 712k | pdf_dict *child = NULL; |
647 | 712k | pdf_name *Type = NULL; |
648 | 712k | pdf_dict *leaf_dict = NULL; |
649 | 712k | pdf_name *Key = NULL; |
650 | 712k | int code = 0; |
651 | | |
652 | 712k | code = pdfi_array_get_no_deref(ctx, Kids, i, (pdf_obj **)&node); |
653 | 712k | if (code < 0) |
654 | 0 | goto errorExit; |
655 | | |
656 | 712k | if (pdfi_type_of(node) != PDF_INDIRECT && pdfi_type_of(node) != PDF_DICT) { |
657 | 32 | code = gs_note_error(gs_error_typecheck); |
658 | 32 | goto errorExit; |
659 | 32 | } |
660 | | |
661 | 712k | if (pdfi_type_of(node) == PDF_INDIRECT) { |
662 | 144k | code = pdfi_dereference(ctx, node->ref_object_num, node->ref_generation_num, |
663 | 144k | (pdf_obj **)&child); |
664 | 144k | if (code < 0) { |
665 | 37.8k | int code1 = pdfi_repair_file(ctx); |
666 | 37.8k | if (code1 < 0) |
667 | 37.8k | goto errorExit; |
668 | 28 | code = pdfi_dereference(ctx, node->ref_object_num, |
669 | 28 | node->ref_generation_num, (pdf_obj **)&child); |
670 | 28 | if (code < 0) |
671 | 15 | goto errorExit; |
672 | 28 | } |
673 | | /* It makes no sense for the Page dictionary to be a stream, but safedocs/DialectDictIsStream.pdf |
674 | | * has this (stream length is 0, not that it matters) and Acrobat happily opens it.... |
675 | | */ |
676 | 106k | if (pdfi_type_of(child) != PDF_DICT) { |
677 | 871 | pdf_dict *d1 = NULL; |
678 | | |
679 | 871 | if (pdfi_type_of(child) != PDF_STREAM) { |
680 | 670 | code = gs_note_error(gs_error_typecheck); |
681 | 670 | goto errorExit; |
682 | 670 | } |
683 | 201 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_DICT_IS_STREAM, "pdfi_get_child", NULL)) < 0) |
684 | 0 | goto errorExit; |
685 | | |
686 | 201 | code = pdfi_get_stream_dict(ctx, (pdf_stream *)child, &d1); |
687 | 201 | if (code < 0) |
688 | 0 | goto errorExit; |
689 | 201 | pdfi_countdown(child); |
690 | 201 | child = d1; |
691 | 201 | code = replace_cache_entry(ctx, (pdf_obj *)d1); |
692 | 201 | if (code < 0) |
693 | 0 | goto errorExit; |
694 | 201 | } |
695 | | /* If its an intermediate node, store it in the page_table, if its a leaf node |
696 | | * then don't store it. Instead we create a special dictionary of our own which |
697 | | * has a /Type of /PageRef and a /PageRef key which is the indirect reference |
698 | | * to the page. However in this case we pass on the actual page dictionary to |
699 | | * the Kids processing below. If we didn't then we'd fall foul of the loop |
700 | | * detection by dereferencing the same object twice. |
701 | | * This is tedious, but it means we don't store all the page dictionaries in |
702 | | * the Pages tree, because page dictionaries can be large and we generally |
703 | | * only use them once. If processed in order we only dereference each page |
704 | | * dictionary once, any other order will dereference each page twice. (or more |
705 | | * if we render the same page multiple times). |
706 | | */ |
707 | 105k | code = pdfi_dict_get_type(ctx, child, "Type", PDF_NAME, (pdf_obj **)&Type); |
708 | 105k | if (code < 0) |
709 | 893 | goto errorExit; |
710 | 104k | if (pdfi_name_is(Type, "Pages")) { |
711 | 795 | code = pdfi_array_put(ctx, Kids, i, (pdf_obj *)child); |
712 | 795 | if (code < 0) |
713 | 0 | goto errorExit; |
714 | 104k | } else { |
715 | | /* Bizarrely, one of the QL FTS files (FTS_07_0704.pdf) has a page diciotnary with a /Type of /Template */ |
716 | 104k | if (!pdfi_name_is(Type, "Page")) |
717 | 747 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BADPAGETYPE, "pdfi_get_child", NULL)) < 0) |
718 | 0 | goto errorExit; |
719 | | /* Make a 'PageRef' entry (just stores an indirect reference to the actual page) |
720 | | * and store that in the Kids array for future reference. But pass on the |
721 | | * dereferenced Page dictionary, in case this is the target page. |
722 | | */ |
723 | | |
724 | 104k | code = pdfi_dict_alloc(ctx, 0, &leaf_dict); |
725 | 104k | if (code < 0) |
726 | 0 | goto errorExit; |
727 | 104k | code = pdfi_name_alloc(ctx, (byte *)"PageRef", 7, (pdf_obj **)&Key); |
728 | 104k | if (code < 0) |
729 | 0 | goto errorExit; |
730 | 104k | pdfi_countup(Key); |
731 | | |
732 | 104k | code = pdfi_dict_put_obj(ctx, leaf_dict, (pdf_obj *)Key, (pdf_obj *)node, true); |
733 | 104k | if (code < 0) |
734 | 0 | goto errorExit; |
735 | 104k | code = pdfi_dict_put(ctx, leaf_dict, "Type", (pdf_obj *)Key); |
736 | 104k | if (code < 0) |
737 | 0 | goto errorExit; |
738 | 104k | code = pdfi_array_put(ctx, Kids, i, (pdf_obj *)leaf_dict); |
739 | 104k | if (code < 0) |
740 | 0 | goto errorExit; |
741 | 104k | leaf_dict = NULL; |
742 | 104k | } |
743 | 568k | } else { |
744 | 568k | if (ctx->loop_detection != NULL) { |
745 | 568k | if (node->object_num != 0 && pdfi_loop_detector_check_object(ctx, node->object_num)) { |
746 | 86 | code = gs_note_error(gs_error_circular_reference); |
747 | 86 | goto errorExit; |
748 | 86 | } |
749 | 568k | if (node->object_num > 0) { |
750 | 54.1k | code = pdfi_loop_detector_add_object(ctx, node->object_num); |
751 | 54.1k | if (code < 0) |
752 | 0 | goto errorExit; |
753 | 54.1k | } |
754 | 568k | } |
755 | 568k | child = (pdf_dict *)node; |
756 | 568k | pdfi_countup(child); |
757 | 568k | } |
758 | | |
759 | 673k | *pchild = child; |
760 | 673k | child = NULL; |
761 | | |
762 | 712k | errorExit: |
763 | 712k | pdfi_free_object((pdf_obj *)leaf_dict); |
764 | 712k | pdfi_countdown(child); |
765 | 712k | pdfi_countdown(node); |
766 | 712k | pdfi_countdown(Type); |
767 | 712k | pdfi_countdown(Key); |
768 | 712k | return code; |
769 | 673k | } |
770 | | |
771 | | /* Check if key is in the dictionary, and if so, copy it into the inheritable dict. |
772 | | */ |
773 | | static int pdfi_check_inherited_key(pdf_context *ctx, pdf_dict *d, const char *keyname, pdf_dict *inheritable) |
774 | 1.09M | { |
775 | 1.09M | int code = 0; |
776 | 1.09M | pdf_obj *object = NULL; |
777 | 1.09M | bool known; |
778 | | |
779 | | /* Check for inheritable keys, if we find any copy them to the 'inheritable' dictionary at this level */ |
780 | 1.09M | code = pdfi_dict_known(ctx, d, keyname, &known); |
781 | 1.09M | if (code < 0) |
782 | 0 | goto exit; |
783 | 1.09M | if (known) { |
784 | 59.1k | code = pdfi_loop_detector_mark(ctx); |
785 | 59.1k | if (code < 0){ |
786 | 0 | goto exit; |
787 | 0 | } |
788 | 59.1k | code = pdfi_dict_get(ctx, d, keyname, &object); |
789 | 59.1k | if (code < 0) { |
790 | 55 | (void)pdfi_loop_detector_cleartomark(ctx); |
791 | 55 | goto exit; |
792 | 55 | } |
793 | 59.0k | code = pdfi_loop_detector_cleartomark(ctx); |
794 | 59.0k | if (code < 0) { |
795 | 0 | goto exit; |
796 | 0 | } |
797 | 59.0k | code = pdfi_dict_put(ctx, inheritable, keyname, object); |
798 | 59.0k | } |
799 | | |
800 | 1.09M | exit: |
801 | 1.09M | pdfi_countdown(object); |
802 | 1.09M | return code; |
803 | 1.09M | } |
804 | | |
805 | | int pdfi_get_page_dict(pdf_context *ctx, pdf_dict *d, uint64_t page_num, uint64_t *page_offset, |
806 | | pdf_dict **target, pdf_dict *inherited) |
807 | 272k | { |
808 | 272k | int i, code = 0; |
809 | 272k | pdf_array *Kids = NULL; |
810 | 272k | pdf_dict *child = NULL; |
811 | 272k | pdf_name *Type = NULL; |
812 | 272k | pdf_dict *inheritable = NULL; |
813 | 272k | int64_t num; |
814 | 272k | double dbl; |
815 | | |
816 | 272k | if (ctx->args.pdfdebug) |
817 | 0 | outprintf(ctx->memory, "%% Finding page dictionary for page %"PRIi64"\n", page_num + 1); |
818 | | |
819 | | /* Allocated inheritable dict (it might stay empty) */ |
820 | 272k | code = pdfi_dict_alloc(ctx, 0, &inheritable); |
821 | 272k | if (code < 0) |
822 | 0 | return code; |
823 | 272k | pdfi_countup(inheritable); |
824 | | |
825 | 272k | code = pdfi_loop_detector_mark(ctx); |
826 | 272k | if (code < 0) |
827 | 0 | return code; |
828 | | |
829 | | /* if we are being passed any inherited values from our parent, copy them now */ |
830 | 272k | if (inherited != NULL) { |
831 | 23.1k | code = pdfi_dict_copy(ctx, inheritable, inherited); |
832 | 23.1k | if (code < 0) |
833 | 0 | goto exit; |
834 | 23.1k | } |
835 | | |
836 | 272k | code = pdfi_dict_get_number(ctx, d, "Count", &dbl); |
837 | 272k | if (code < 0) |
838 | 0 | goto exit; |
839 | 272k | if (dbl != floor(dbl)) { |
840 | 0 | code = gs_note_error(gs_error_rangecheck); |
841 | 0 | goto exit; |
842 | 0 | } |
843 | 272k | num = (int)dbl; |
844 | | |
845 | 272k | if (num < 0 || (num + *page_offset) > ctx->num_pages) { |
846 | 0 | code = gs_note_error(gs_error_rangecheck); |
847 | 0 | goto exit; |
848 | 0 | } |
849 | 272k | if (num + *page_offset < page_num) { |
850 | 0 | *page_offset += num; |
851 | 0 | code = 1; |
852 | 0 | goto exit; |
853 | 0 | } |
854 | | /* The requested page is a descendant of this node */ |
855 | | |
856 | | /* Check for inheritable keys, if we find any copy them to the 'inheritable' dictionary at this level */ |
857 | 272k | code = pdfi_check_inherited_key(ctx, d, "Resources", inheritable); |
858 | 272k | if (code < 0) |
859 | 55 | goto exit; |
860 | 272k | code = pdfi_check_inherited_key(ctx, d, "MediaBox", inheritable); |
861 | 272k | if (code < 0) |
862 | 0 | goto exit; |
863 | 272k | code = pdfi_check_inherited_key(ctx, d, "CropBox", inheritable); |
864 | 272k | if (code < 0) |
865 | 0 | goto exit; |
866 | 272k | code = pdfi_check_inherited_key(ctx, d, "Rotate", inheritable); |
867 | 272k | if (code < 0) { |
868 | 0 | goto exit; |
869 | 0 | } |
870 | | |
871 | | /* Get the Kids array */ |
872 | 272k | code = pdfi_dict_get_type(ctx, d, "Kids", PDF_ARRAY, (pdf_obj **)&Kids); |
873 | 272k | if (code < 0) { |
874 | 27 | goto exit; |
875 | 27 | } |
876 | | |
877 | | /* Check each entry in the Kids array */ |
878 | 712k | for (i = 0;i < pdfi_array_size(Kids);i++) { |
879 | 712k | pdfi_countdown(child); |
880 | 712k | child = NULL; |
881 | 712k | pdfi_countdown(Type); |
882 | 712k | Type = NULL; |
883 | | |
884 | 712k | code = pdfi_get_child(ctx, Kids, i, &child); |
885 | 712k | if (code < 0) { |
886 | 39.5k | goto exit; |
887 | 39.5k | } |
888 | | |
889 | | /* Check the type, if its a Pages entry, then recurse. If its a Page entry, is it the one we want */ |
890 | 673k | code = pdfi_dict_get_type(ctx, child, "Type", PDF_NAME, (pdf_obj **)&Type); |
891 | 673k | if (code == 0) { |
892 | 673k | if (pdfi_name_is(Type, "Pages")) { |
893 | 37.8k | code = pdfi_dict_get_number(ctx, child, "Count", &dbl); |
894 | 37.8k | if (code == 0) { |
895 | 37.7k | if (dbl != floor(dbl)) { |
896 | 0 | code = gs_note_error(gs_error_rangecheck); |
897 | 0 | goto exit; |
898 | 0 | } |
899 | 37.7k | num = (int)dbl; |
900 | 37.7k | if (num < 0 || (num + *page_offset) > ctx->num_pages) { |
901 | 7 | code = gs_note_error(gs_error_rangecheck); |
902 | 7 | goto exit; |
903 | 37.7k | } else { |
904 | 37.7k | if (num + *page_offset <= page_num) { |
905 | 14.5k | *page_offset += num; |
906 | 23.1k | } else { |
907 | 23.1k | code = pdfi_get_page_dict(ctx, child, page_num, page_offset, target, inheritable); |
908 | 23.1k | goto exit; |
909 | 23.1k | } |
910 | 37.7k | } |
911 | 37.7k | } |
912 | 635k | } else { |
913 | 635k | if (pdfi_name_is(Type, "PageRef")) { |
914 | 514k | if ((*page_offset) == page_num) { |
915 | 103k | pdf_dict *page_dict = NULL; |
916 | | |
917 | 103k | code = pdfi_dict_get_no_store_R(ctx, child, "PageRef", (pdf_obj **)&page_dict); |
918 | 103k | if (code < 0) |
919 | 6 | goto exit; |
920 | 103k | code = pdfi_merge_dicts(ctx, page_dict, inheritable); |
921 | 103k | *target = page_dict; |
922 | 103k | pdfi_countup(*target); |
923 | 103k | pdfi_countdown(page_dict); |
924 | 103k | goto exit; |
925 | 410k | } else { |
926 | 410k | *page_offset += 1; |
927 | 410k | } |
928 | 514k | } else { |
929 | 121k | if (!pdfi_name_is(Type, "Page")) { |
930 | 967 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BADPAGETYPE, "pdfi_get_page_dict", NULL)) < 0) |
931 | 0 | goto exit; |
932 | 967 | } |
933 | 121k | if ((*page_offset) == page_num) { |
934 | 106k | code = pdfi_merge_dicts(ctx, child, inheritable); |
935 | 106k | *target = child; |
936 | 106k | pdfi_countup(*target); |
937 | 106k | goto exit; |
938 | 106k | } else { |
939 | 15.0k | *page_offset += 1; |
940 | 15.0k | } |
941 | 121k | } |
942 | 635k | } |
943 | 673k | } |
944 | 440k | if (code < 0) |
945 | 141 | goto exit; |
946 | 440k | } |
947 | | /* Positive return value indicates we did not find the target below this node, try the next one */ |
948 | 31 | code = 1; |
949 | | |
950 | 272k | exit: |
951 | 272k | pdfi_loop_detector_cleartomark(ctx); |
952 | 272k | pdfi_countdown(inheritable); |
953 | 272k | pdfi_countdown(Kids); |
954 | 272k | pdfi_countdown(child); |
955 | 272k | pdfi_countdown(Type); |
956 | 272k | return code; |
957 | 31 | } |
958 | | |
959 | | int pdfi_doc_page_array_init(pdf_context *ctx) |
960 | 74.5k | { |
961 | 74.5k | size_t size = ctx->num_pages*sizeof(uint32_t); |
962 | | |
963 | 74.5k | ctx->page_array = (uint32_t *)gs_alloc_bytes(ctx->memory, size, |
964 | 74.5k | "pdfi_doc_page_array_init(page_array)"); |
965 | 74.5k | if (ctx->page_array == NULL) |
966 | 2 | return_error(gs_error_VMerror); |
967 | | |
968 | 74.5k | memset(ctx->page_array, 0, size); |
969 | 74.5k | return 0; |
970 | 74.5k | } |
971 | | |
972 | | void pdfi_doc_page_array_free(pdf_context *ctx) |
973 | 94.6k | { |
974 | 94.6k | if (!ctx->page_array) |
975 | 20.0k | return; |
976 | 74.5k | gs_free_object(ctx->memory, ctx->page_array, "pdfi_doc_page_array_free(page_array)"); |
977 | 74.5k | ctx->page_array = NULL; |
978 | 74.5k | } |
979 | | |
980 | | /* |
981 | | * Checks for both "Resource" and "RD" in the specified dict. |
982 | | * And then gets the typedict of Type (e.g. Font or XObject). |
983 | | * Returns 0 if undefined, >0 if found, <0 if error |
984 | | */ |
985 | | static int pdfi_resource_knownget_typedict(pdf_context *ctx, unsigned char *Type, |
986 | | pdf_dict *dict, pdf_dict **typedict) |
987 | 8.38M | { |
988 | 8.38M | int code; |
989 | 8.38M | pdf_dict *Resources = NULL; |
990 | | |
991 | 8.38M | code = pdfi_dict_knownget_type(ctx, dict, "Resources", PDF_DICT, (pdf_obj **)&Resources); |
992 | 8.38M | if (code == 0) |
993 | 3.41M | code = pdfi_dict_knownget_type(ctx, dict, "DR", PDF_DICT, (pdf_obj **)&Resources); |
994 | 8.38M | if (code < 0) |
995 | 307k | goto exit; |
996 | 8.07M | if (code > 0) |
997 | 4.65M | code = pdfi_dict_knownget_type(ctx, Resources, (const char *)Type, PDF_DICT, (pdf_obj **)typedict); |
998 | 8.38M | exit: |
999 | 8.38M | pdfi_countdown(Resources); |
1000 | 8.38M | return code; |
1001 | 8.07M | } |
1002 | | |
1003 | | int pdfi_find_resource(pdf_context *ctx, unsigned char *Type, pdf_name *name, |
1004 | | pdf_dict *dict, pdf_dict *page_dict, pdf_obj **o) |
1005 | 3.64M | { |
1006 | 3.64M | pdf_dict *typedict = NULL; |
1007 | 3.64M | pdf_dict *Parent = NULL; |
1008 | 3.64M | pdf_name *n = NULL; |
1009 | 3.64M | int code; |
1010 | 3.64M | bool known = false; |
1011 | | |
1012 | 3.64M | *o = NULL; |
1013 | | |
1014 | | /* Check the provided dict, stream_dict can be NULL if we are trying to find a Default* ColorSpace */ |
1015 | 3.64M | if (dict != NULL) { |
1016 | 3.64M | bool deref_parent = true, dict_is_XObject = false; |
1017 | | |
1018 | 3.64M | code = pdfi_resource_knownget_typedict(ctx, Type, dict, &typedict); |
1019 | 3.64M | if (code < 0) |
1020 | 3.96k | goto exit; |
1021 | 3.64M | if (code > 0) { |
1022 | 762k | code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); |
1023 | 762k | if (code != gs_error_undefined) |
1024 | 586k | goto exit; |
1025 | 762k | } |
1026 | | |
1027 | | /* Check the Parents, if any */ |
1028 | | /* If the current dictionary is a Page dictionary, do NOT dereference it's Parent, as that |
1029 | | * will be the Pages tree, and we will end up with circular references, causing a memory leak. |
1030 | | */ |
1031 | 3.05M | if (pdfi_dict_knownget_type(ctx, dict, "Type", PDF_NAME, (pdf_obj **)&n) > 0) { |
1032 | 217k | if (pdfi_name_is(n, "Page")) |
1033 | 8 | deref_parent = false; |
1034 | 217k | if (pdfi_name_is(n, "XObject")) |
1035 | 170k | dict_is_XObject = true; |
1036 | 217k | pdfi_countdown(n); |
1037 | 217k | } |
1038 | | |
1039 | 3.05M | if (deref_parent) { |
1040 | 3.05M | code = pdfi_dict_known(ctx, dict, "Parent", &known); |
1041 | 3.05M | if (code >= 0 && known == true) { |
1042 | 213k | code = pdfi_dict_get_no_store_R(ctx, dict, "Parent", (pdf_obj **)&Parent); |
1043 | | |
1044 | 213k | if (code >= 0) { |
1045 | 200k | if (pdfi_type_of(Parent) != PDF_DICT) { |
1046 | 74 | if (pdfi_type_of(Parent) == PDF_INDIRECT) { |
1047 | 0 | pdf_indirect_ref *o = (pdf_indirect_ref *)Parent; |
1048 | |
|
1049 | 0 | Parent = NULL; |
1050 | 0 | code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent); |
1051 | 0 | pdfi_countdown(o); |
1052 | 0 | if (code >= 0 && pdfi_type_of(Parent) != PDF_DICT) { |
1053 | 0 | pdfi_countdown(Parent); |
1054 | 0 | Parent = NULL; |
1055 | 0 | } |
1056 | 74 | } else { |
1057 | 74 | pdfi_countdown(Parent); |
1058 | 74 | Parent = NULL; |
1059 | 74 | } |
1060 | 74 | } |
1061 | 200k | } else |
1062 | 12.6k | Parent = NULL; |
1063 | 213k | } |
1064 | | |
1065 | 3.05M | if (Parent != NULL) { |
1066 | 200k | if (ctx->page.CurrentPageDict != NULL && Parent->object_num != ctx->page.CurrentPageDict->object_num) { |
1067 | 200k | if (pdfi_loop_detector_check_object(ctx, Parent->object_num) == true) { |
1068 | 32.4k | code = gs_note_error(gs_error_circular_reference); |
1069 | 32.4k | goto exit; |
1070 | 32.4k | } |
1071 | | |
1072 | 168k | code = pdfi_loop_detector_mark(ctx); |
1073 | 168k | if (code < 0) |
1074 | 0 | goto exit; |
1075 | | |
1076 | 168k | code = pdfi_loop_detector_add_object(ctx, dict->object_num); |
1077 | 168k | if (code < 0) { |
1078 | 0 | (void)pdfi_loop_detector_cleartomark(ctx); |
1079 | 0 | goto exit; |
1080 | 0 | } |
1081 | 168k | code = pdfi_find_resource(ctx, Type, name, Parent, page_dict, o); |
1082 | 168k | (void)pdfi_loop_detector_cleartomark(ctx); |
1083 | 168k | if (code != gs_error_undefined) { |
1084 | 545 | if (dict_is_XObject) |
1085 | 490 | pdfi_set_warning(ctx, 0, NULL, W_PDF_INHERITED_STREAM_RESOURCE, "pdfi_find_resource", (char *)"Couldn't find named resource in supplied dictionary, matching name located in Page Resource"); |
1086 | 545 | goto exit; |
1087 | 545 | } |
1088 | 168k | } |
1089 | 200k | } |
1090 | 3.02M | code = 0; |
1091 | 3.02M | } |
1092 | 3.02M | pdfi_countdown(typedict); |
1093 | 3.02M | typedict = NULL; |
1094 | 3.02M | } |
1095 | | |
1096 | | /* Normally page_dict can't be (or shouldn't be) NULL. However, if we are processing |
1097 | | * a TYpe 3 font, then the 'page dict' is the Resources dictionary of that font. If |
1098 | | * the font inherits Resources from its page (which it should not) then its possible |
1099 | | * that the 'page dict' could be NULL here. We need to guard against that. Its possible |
1100 | | * there may be other, similar, cases (eg Patterns within Patterns). In addition we |
1101 | | * do need to be able to check the real page dictionary for inhereited resources, and |
1102 | | * in the case of a type 3 font BuildChar at least there is no easy way to do that. |
1103 | | * So we'll store the page dictionary for the current page in the context as a |
1104 | | * last-ditch resource to check. |
1105 | | */ |
1106 | 3.02M | if (page_dict != NULL) { |
1107 | 3.02M | code = pdfi_resource_knownget_typedict(ctx, Type, page_dict, &typedict); |
1108 | 3.02M | if (code < 0) |
1109 | 386k | goto exit; |
1110 | | |
1111 | 2.63M | if (code > 0) { |
1112 | 2.32M | code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); |
1113 | 2.32M | if (code != gs_error_undefined) |
1114 | 1.77M | goto exit; |
1115 | 2.32M | } |
1116 | 2.63M | } |
1117 | | |
1118 | 863k | pdfi_countdown(typedict); |
1119 | 863k | typedict = NULL; |
1120 | | |
1121 | 863k | if (ctx->page.CurrentPageDict != NULL) { |
1122 | 863k | code = pdfi_resource_knownget_typedict(ctx, Type, ctx->page.CurrentPageDict, &typedict); |
1123 | 863k | if (code < 0) |
1124 | 180 | goto exit; |
1125 | | |
1126 | 863k | if (code > 0) { |
1127 | 566k | code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); |
1128 | 566k | if (code != gs_error_undefined) |
1129 | 3.09k | goto exit; |
1130 | 566k | } |
1131 | 863k | } |
1132 | | |
1133 | 860k | pdfi_countdown(typedict); |
1134 | 860k | typedict = NULL; |
1135 | | |
1136 | 860k | if (ctx->current_stream != NULL) { |
1137 | 517k | pdf_dict *stream_dict = NULL; |
1138 | 517k | pdf_stream *stream = ctx->current_stream; |
1139 | | |
1140 | 851k | do { |
1141 | 851k | code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream, &stream_dict); |
1142 | 851k | if (code < 0) |
1143 | 0 | goto exit; |
1144 | 851k | code = pdfi_resource_knownget_typedict(ctx, Type, stream_dict, &typedict); |
1145 | 851k | if (code < 0) |
1146 | 0 | goto exit; |
1147 | 851k | if (code > 0) { |
1148 | 334k | code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o); |
1149 | 334k | if (code == 0) { |
1150 | 83 | pdfi_set_error(ctx, 0, NULL, E_PDF_INHERITED_STREAM_RESOURCE, "pdfi_find_resource", (char *)"Couldn't find named resource in supplied dictionary, or Parents, or Pages, matching name located in earlier stream Resource"); |
1151 | 83 | goto exit; |
1152 | 83 | } |
1153 | 334k | } |
1154 | 851k | pdfi_countdown(typedict); |
1155 | 851k | typedict = NULL; |
1156 | 851k | stream = pdfi_stream_parent(ctx, stream); |
1157 | 851k | }while(stream != NULL); |
1158 | 517k | } |
1159 | | |
1160 | | /* If we got all the way down there, we didn't find it */ |
1161 | 860k | pdfi_set_warning(ctx, 0, NULL, W_PDF_MISSING_NAMED_RESOURCE, "pdfi_find_resource", NULL); |
1162 | 860k | code = gs_error_undefined; |
1163 | | |
1164 | 3.64M | exit: |
1165 | 3.64M | pdfi_countdown(typedict); |
1166 | 3.64M | pdfi_countdown(Parent); |
1167 | 3.64M | return code; |
1168 | 860k | } |
1169 | | |
1170 | | /* Mark the actual outline */ |
1171 | | static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline) |
1172 | 246 | { |
1173 | 246 | int code = 0; |
1174 | 246 | pdf_dict *tempdict = NULL; |
1175 | 246 | uint64_t dictsize; |
1176 | 246 | uint64_t index; |
1177 | 246 | pdf_name *Key = NULL; |
1178 | 246 | double num = 0; |
1179 | | |
1180 | | /* Basically we only do /Count, /Title, /A, /C, /F |
1181 | | * The /First, /Last, /Next, /Parent get written magically by pdfwrite |
1182 | | */ |
1183 | | |
1184 | | /* Make a temporary copy of the outline dict */ |
1185 | 246 | dictsize = pdfi_dict_entries(outline); |
1186 | 246 | code = pdfi_dict_alloc(ctx, dictsize, &tempdict); |
1187 | 246 | if (code < 0) goto exit; |
1188 | 246 | pdfi_countup(tempdict); |
1189 | 246 | code = pdfi_dict_copy(ctx, tempdict, outline); |
1190 | 246 | if (code < 0) goto exit; |
1191 | | |
1192 | | /* Due to some craziness on the part of Adobe, the /Count in an Outline entry |
1193 | | * in a PDF file, and the /Count value in an /OUT pdfmark mean different things(!) |
1194 | | * In a PDF file it is the number of outline entries beneath the current entry |
1195 | | * (all child descsndants) whereas in a pdfmark it is the number of entries |
1196 | | * in just the next level. So we cannot use the /Count from the PDF file, we |
1197 | | * need to go to the /First entry of the next level, and then count all |
1198 | | * the entries at that level by following each /Next. |
1199 | | */ |
1200 | 246 | code = pdfi_dict_knownget_number(ctx, outline, "Count", &num); |
1201 | 246 | if (code < 0) |
1202 | 0 | goto exit; |
1203 | | |
1204 | | /* We can't rely on Count being present to indicate that the Outline has descendants |
1205 | | * see bug #707228. Instead, look for the presence of a /First key. If there is no count |
1206 | | * key, or it is 0, then assume the entry is closed. |
1207 | | */ |
1208 | 246 | { |
1209 | 246 | pdf_dict *current = NULL, *next = NULL; |
1210 | 246 | int count = 0, code1; |
1211 | | |
1212 | 246 | code1 = pdfi_dict_knownget_type(ctx, outline, "First", PDF_DICT, (pdf_obj **)¤t); |
1213 | 246 | if (code1 > 0) { |
1214 | 37 | if (code <= 0) { |
1215 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_OUTLINECHILD_NO_COUNT, "pdfi_doc_mark_the_outline", NULL)) < 0) |
1216 | 0 | goto exit; |
1217 | 0 | } |
1218 | 37 | count++; |
1219 | 66 | do { |
1220 | 66 | code1 = pdfi_dict_knownget_type(ctx, current, "Next", PDF_DICT, (pdf_obj **)&next); |
1221 | 66 | if (code1 > 0) { |
1222 | 29 | pdfi_countdown(current); |
1223 | 29 | current = next; |
1224 | 29 | next = NULL; |
1225 | 29 | count++; |
1226 | 29 | } else |
1227 | 37 | break; |
1228 | 66 | } while (1); |
1229 | 37 | pdfi_countdown(current); |
1230 | 37 | } |
1231 | 246 | if (num <= 0) |
1232 | 212 | count *= -1; |
1233 | 246 | pdfi_dict_put_int(ctx, tempdict, "Count", count); |
1234 | 246 | } |
1235 | | |
1236 | | /* Go through the dict, removing some keys and doing special handling for others. |
1237 | | */ |
1238 | 0 | code = pdfi_dict_key_first(ctx, outline, (pdf_obj **)&Key, &index); |
1239 | 995 | while (code >= 0) { |
1240 | 995 | if (pdfi_name_is(Key, "Last") || pdfi_name_is(Key, "Next") || pdfi_name_is(Key, "First") || |
1241 | 995 | pdfi_name_is(Key, "Prev") || pdfi_name_is(Key, "Parent")) { |
1242 | | /* Delete some keys |
1243 | | * These are handled in pdfwrite and can lead to circular refs |
1244 | | */ |
1245 | 413 | code = pdfi_dict_delete_pair(ctx, tempdict, Key); |
1246 | 582 | } else if (pdfi_name_is(Key, "SE")) { |
1247 | | /* TODO: Not sure what to do with SE, delete for now */ |
1248 | | /* Seems to be okay to just delete it, since there should also be a /Dest |
1249 | | * See sample fts_29_2903.pdf |
1250 | | * Currently we are same as gs |
1251 | | */ |
1252 | 0 | code = pdfi_dict_delete_pair(ctx, tempdict, Key); |
1253 | 582 | } else if (pdfi_name_is(Key, "A")) { |
1254 | 120 | code = pdfi_pdfmark_modA(ctx, tempdict); |
1255 | 462 | } else if (pdfi_name_is(Key, "Dest")) { |
1256 | 124 | code = pdfi_pdfmark_modDest(ctx, tempdict); |
1257 | 124 | } |
1258 | 995 | if (code < 0) |
1259 | 28 | goto exit; |
1260 | | |
1261 | 967 | pdfi_countdown(Key); |
1262 | 967 | Key = NULL; |
1263 | | |
1264 | 967 | code = pdfi_dict_key_next(ctx, outline, (pdf_obj **)&Key, &index); |
1265 | 967 | if (code == gs_error_undefined) { |
1266 | 218 | code = 0; |
1267 | 218 | break; |
1268 | 218 | } |
1269 | 967 | } |
1270 | 218 | if (code < 0) goto exit; |
1271 | | |
1272 | | /* Write the pdfmark */ |
1273 | 218 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "OUT"); |
1274 | 218 | if (code < 0) |
1275 | 1 | goto exit; |
1276 | | |
1277 | 246 | exit: |
1278 | 246 | pdfi_countdown(tempdict); |
1279 | 246 | pdfi_countdown(Key); |
1280 | 246 | return code; |
1281 | 218 | } |
1282 | | |
1283 | | /* Do pdfmark on an outline entry (recursive) |
1284 | | * Note: the logic here is wonky. It is relying on the behavior of the pdfwrite driver. |
1285 | | * See pdf_main.ps/writeoutline() |
1286 | | */ |
1287 | | static int pdfi_doc_mark_outline(pdf_context *ctx, pdf_dict *outline) |
1288 | 246 | { |
1289 | 246 | int code = 0; |
1290 | 246 | pdf_dict *child = NULL; |
1291 | 246 | pdf_dict *Next = NULL; |
1292 | | |
1293 | 246 | if (outline == (pdf_dict *)PDF_NULL_OBJ) |
1294 | 0 | return 0; |
1295 | | |
1296 | | /* Mark the outline */ |
1297 | | /* NOTE: I think the pdfmark for this needs to be written before the children |
1298 | | * because I think pdfwrite relies on the order of things. |
1299 | | */ |
1300 | 246 | code = pdfi_doc_mark_the_outline(ctx, outline); |
1301 | 246 | if (code < 0) |
1302 | 29 | goto exit1; |
1303 | | |
1304 | | /* Handle the children */ |
1305 | 217 | code = pdfi_loop_detector_mark(ctx); |
1306 | 217 | if (code < 0) |
1307 | 0 | goto exit1; |
1308 | | |
1309 | | /* Handle any children (don't deref them, we don't want to leave them hanging around) */ |
1310 | 217 | code = pdfi_dict_get_no_store_R(ctx, outline, "First", (pdf_obj **)&child); |
1311 | 217 | if (code < 0 || pdfi_type_of(child) != PDF_DICT) { |
1312 | | /* TODO: flag a warning? */ |
1313 | 182 | code = 0; |
1314 | 182 | goto exit; |
1315 | 182 | } |
1316 | | |
1317 | 35 | if (child->object_num != 0) { |
1318 | 35 | code = pdfi_loop_detector_add_object(ctx, child->object_num); |
1319 | 35 | if (code < 0) |
1320 | 0 | goto exit; |
1321 | 35 | } |
1322 | | |
1323 | 62 | do { |
1324 | 62 | code = pdfi_doc_mark_outline(ctx, child); |
1325 | 62 | if (code < 0) goto exit; |
1326 | | |
1327 | | |
1328 | 57 | code = pdfi_dict_get_no_store_R(ctx, child, "Next", (pdf_obj **)&Next); |
1329 | 57 | if (code == gs_error_undefined) { |
1330 | 29 | code = 0; |
1331 | 29 | break; |
1332 | 29 | } |
1333 | 28 | if (code == gs_error_circular_reference) { |
1334 | 0 | code = 0; |
1335 | 0 | goto exit; |
1336 | 0 | } |
1337 | 28 | if (code < 0 || pdfi_type_of(Next) != PDF_DICT) |
1338 | 1 | goto exit; |
1339 | | |
1340 | 27 | pdfi_countdown(child); |
1341 | 27 | child = Next; |
1342 | 27 | Next = NULL; |
1343 | 27 | } while (true); |
1344 | | |
1345 | 217 | exit: |
1346 | 217 | (void)pdfi_loop_detector_cleartomark(ctx); |
1347 | 246 | exit1: |
1348 | 246 | pdfi_countdown(child); |
1349 | 246 | pdfi_countdown(Next); |
1350 | 246 | return code; |
1351 | 217 | } |
1352 | | |
1353 | | /* Do pdfmark for Outlines */ |
1354 | | static int pdfi_doc_Outlines(pdf_context *ctx) |
1355 | 3.35k | { |
1356 | 3.35k | int code = 0; |
1357 | 3.35k | pdf_dict *Outlines = NULL; |
1358 | 3.35k | pdf_dict *outline = NULL; |
1359 | 3.35k | pdf_dict *Next = NULL; |
1360 | | |
1361 | 3.35k | if (ctx->args.no_pdfmark_outlines) |
1362 | 0 | goto exit1; |
1363 | | |
1364 | 3.35k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "Outlines", PDF_DICT, (pdf_obj **)&Outlines); |
1365 | 3.35k | if (code <= 0) { |
1366 | | /* TODO: flag a warning */ |
1367 | 3.12k | code = 0; |
1368 | 3.12k | goto exit1; |
1369 | 3.12k | } |
1370 | | |
1371 | 222 | code = pdfi_loop_detector_mark(ctx); |
1372 | 222 | if (code < 0) |
1373 | 0 | goto exit1; |
1374 | | |
1375 | | /* Handle any children (don't deref them, we don't want to leave them hanging around) */ |
1376 | 222 | code = pdfi_dict_get_no_store_R(ctx, Outlines, "First", (pdf_obj **)&outline); |
1377 | 222 | if (code < 0 || pdfi_type_of(outline) != PDF_DICT) { |
1378 | | /* TODO: flag a warning? */ |
1379 | 59 | code = 0; |
1380 | 59 | goto exit; |
1381 | 59 | } |
1382 | | |
1383 | 163 | if (pdfi_type_of(outline) != PDF_DICT) |
1384 | 0 | goto exit; /* Exit with no error? */ |
1385 | | |
1386 | 163 | if (outline->object_num != 0) { |
1387 | 163 | code = pdfi_loop_detector_add_object(ctx, outline->object_num); |
1388 | 163 | if (code < 0) |
1389 | 0 | goto exit; |
1390 | 163 | } |
1391 | | |
1392 | | /* Loop through all the top-level outline entries |
1393 | | * First one is in Outlines, and if there are more, they are the Next of the |
1394 | | * current outline item. (see spec) |
1395 | | * (basically we are walking a linked list) |
1396 | | */ |
1397 | 184 | do { |
1398 | 184 | code = pdfi_doc_mark_outline(ctx, outline); |
1399 | 184 | if (code < 0) goto exit; |
1400 | | |
1401 | | |
1402 | 154 | code = pdfi_dict_get_no_store_R(ctx, outline, "Next", (pdf_obj **)&Next); |
1403 | 154 | if (code == gs_error_undefined) { |
1404 | 132 | code = 0; |
1405 | 132 | break; |
1406 | 132 | } |
1407 | 22 | if (code == gs_error_circular_reference) { |
1408 | 0 | code = 0; |
1409 | 0 | goto exit; |
1410 | 0 | } |
1411 | 22 | if (code < 0 || pdfi_type_of(Next) != PDF_DICT) |
1412 | 1 | goto exit; |
1413 | | |
1414 | 21 | pdfi_countdown(outline); |
1415 | 21 | outline = Next; |
1416 | 21 | Next = NULL; |
1417 | 21 | } while (true); |
1418 | | |
1419 | 222 | exit: |
1420 | 222 | (void)pdfi_loop_detector_cleartomark(ctx); |
1421 | 3.35k | exit1: |
1422 | 3.35k | pdfi_countdown(Outlines); |
1423 | 3.35k | pdfi_countdown(outline); |
1424 | 3.35k | pdfi_countdown(Next); |
1425 | 3.35k | return code; |
1426 | 222 | } |
1427 | | |
1428 | | /* Do pdfmark for Info */ |
1429 | | static int pdfi_doc_Info(pdf_context *ctx) |
1430 | 3.35k | { |
1431 | 3.35k | int code = 0; |
1432 | 3.35k | pdf_dict *Info = NULL, *d = NULL; |
1433 | 3.35k | pdf_dict *tempdict = NULL; |
1434 | 3.35k | uint64_t dictsize; |
1435 | 3.35k | uint64_t index; |
1436 | 3.35k | pdf_name *Key = NULL; |
1437 | 3.35k | pdf_obj *Value = NULL; |
1438 | | |
1439 | | /* See comment in pdfi_read_Root() for details */ |
1440 | 3.35k | d = ctx->Trailer; |
1441 | 3.35k | pdfi_countup(d); |
1442 | 3.35k | code = pdfi_dict_knownget_type(ctx, d, "Info", PDF_DICT, (pdf_obj **)&Info); |
1443 | 3.35k | pdfi_countdown(d); |
1444 | 3.35k | if (code <= 0) { |
1445 | | /* TODO: flag a warning */ |
1446 | 2.87k | goto exit; |
1447 | 2.87k | } |
1448 | | |
1449 | | /* Make a temporary copy of the Info dict */ |
1450 | 474 | dictsize = pdfi_dict_entries(Info); |
1451 | 474 | code = pdfi_dict_alloc(ctx, dictsize, &tempdict); |
1452 | 474 | if (code < 0) goto exit; |
1453 | 474 | pdfi_countup(tempdict); |
1454 | | |
1455 | | /* Copy only certain keys from Info to tempdict |
1456 | | * NOTE: pdfwrite will set /Producer, /CreationDate and /ModDate |
1457 | | */ |
1458 | 474 | code = pdfi_dict_first(ctx, Info, (pdf_obj **)&Key, &Value, &index); |
1459 | 2.39k | while (code >= 0) { |
1460 | 2.38k | if (pdfi_name_is(Key, "Author") || pdfi_name_is(Key, "Creator") || |
1461 | 2.38k | pdfi_name_is(Key, "Title") || pdfi_name_is(Key, "Subject") || |
1462 | 2.38k | pdfi_name_is(Key, "Keywords")) { |
1463 | | |
1464 | 835 | code = pdfi_dict_put_obj(ctx, tempdict, (pdf_obj *)Key, Value, true); |
1465 | 835 | if (code < 0) |
1466 | 0 | goto exit; |
1467 | 835 | } |
1468 | 2.38k | pdfi_countdown(Key); |
1469 | 2.38k | Key = NULL; |
1470 | 2.38k | pdfi_countdown(Value); |
1471 | 2.38k | Value = NULL; |
1472 | | |
1473 | 2.38k | code = pdfi_dict_next(ctx, Info, (pdf_obj **)&Key, &Value, &index); |
1474 | 2.38k | if (code == gs_error_undefined) { |
1475 | 461 | code = 0; |
1476 | 461 | break; |
1477 | 461 | } |
1478 | 2.38k | } |
1479 | 474 | if (code < 0) goto exit; |
1480 | | |
1481 | | /* Write the pdfmark */ |
1482 | 461 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCINFO"); |
1483 | | |
1484 | 3.35k | exit: |
1485 | 3.35k | pdfi_countdown(Key); |
1486 | 3.35k | pdfi_countdown(Value); |
1487 | 3.35k | pdfi_countdown(Info); |
1488 | 3.35k | pdfi_countdown(tempdict); |
1489 | 3.35k | return code; |
1490 | 461 | } |
1491 | | |
1492 | | /* Handle PageLabels for pdfwrite device */ |
1493 | | static int pdfi_doc_PageLabels(pdf_context *ctx) |
1494 | 74.5k | { |
1495 | 74.5k | int code; |
1496 | 74.5k | pdf_dict *PageLabels = NULL; |
1497 | | |
1498 | 74.5k | if (ctx->loop_detection) { |
1499 | 0 | code = pdfi_loop_detector_mark(ctx); |
1500 | 0 | if (code < 0) |
1501 | 0 | return code; |
1502 | 0 | } |
1503 | | |
1504 | 74.5k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageLabels", PDF_DICT, (pdf_obj **)&PageLabels); |
1505 | 74.5k | if (code <= 0) { |
1506 | 71.6k | if (ctx->loop_detection) |
1507 | 0 | (void)pdfi_loop_detector_cleartomark(ctx); |
1508 | | /* TODO: flag a warning */ |
1509 | 71.6k | goto exit; |
1510 | 71.6k | } |
1511 | | |
1512 | 2.88k | if (ctx->loop_detection) { |
1513 | 0 | code = pdfi_loop_detector_cleartomark(ctx); |
1514 | 0 | if (code < 0) |
1515 | 0 | goto exit; |
1516 | 0 | } |
1517 | | |
1518 | 2.88k | if (ctx->device_state.WantsPageLabels) { |
1519 | | /* This will send the PageLabels object as a 'pdfpagelabels' setdeviceparams */ |
1520 | 224 | code = pdfi_pdfmark_object(ctx, (pdf_obj *)PageLabels, "pdfpagelabels"); |
1521 | 224 | if (code < 0) |
1522 | 15 | goto exit; |
1523 | 224 | } |
1524 | | |
1525 | 74.5k | exit: |
1526 | 74.5k | pdfi_countdown(PageLabels); |
1527 | 74.5k | return code; |
1528 | 2.88k | } |
1529 | | |
1530 | | /* Handle OutputIntents stuff |
1531 | | * (bottom of pdf_main.ps/process_trailer_attrs) |
1532 | | */ |
1533 | | static int pdfi_doc_OutputIntents(pdf_context *ctx) |
1534 | 74.5k | { |
1535 | 74.5k | int code; |
1536 | 74.5k | pdf_array *OutputIntents = NULL; |
1537 | 74.5k | pdf_dict *intent = NULL; |
1538 | 74.5k | pdf_string *name = NULL; |
1539 | 74.5k | pdf_stream *DestOutputProfile = NULL; |
1540 | 74.5k | uint64_t index; |
1541 | | |
1542 | | /* NOTE: subtle difference in error handling -- we are checking for OutputIntents first, |
1543 | | * so this will just ignore UsePDFX3Profile or UseOutputIntent params without warning, |
1544 | | * if OutputIntents doesn't exist. Seems fine to me. |
1545 | | */ |
1546 | 74.5k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "OutputIntents", PDF_ARRAY, |
1547 | 74.5k | (pdf_obj **)&OutputIntents); |
1548 | 74.5k | if (code <= 0) { |
1549 | 74.0k | goto exit; |
1550 | 74.0k | } |
1551 | | |
1552 | | /* TODO: Implement writeoutputintents if somebody ever complains... |
1553 | | * See pdf_main.ps/writeoutputintents |
1554 | | * I am not aware of a device that supports "/OutputIntent" so |
1555 | | * couldn't figure out what to do for this. |
1556 | | */ |
1557 | | |
1558 | | /* Handle UsePDFX3Profile and UseOutputIntent command line options */ |
1559 | 436 | if (ctx->args.UsePDFX3Profile) { |
1560 | | /* This is an index into the array */ |
1561 | 0 | code = pdfi_array_get_type(ctx, OutputIntents, ctx->args.PDFX3Profile_num, |
1562 | 0 | PDF_DICT, (pdf_obj **)&intent); |
1563 | 0 | if (code < 0) { |
1564 | 0 | code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTPUTINTENT_INDEX, "pdfi_doc_OutputIntents", ""); |
1565 | 0 | goto exit; |
1566 | 0 | } |
1567 | 436 | } else if (ctx->args.UseOutputIntent != NULL) { |
1568 | | /* This is a name to look up in the array */ |
1569 | 0 | for (index=0; index<pdfi_array_size(OutputIntents); index ++) { |
1570 | 0 | code = pdfi_array_get_type(ctx, OutputIntents, index, PDF_DICT, (pdf_obj **)&intent); |
1571 | 0 | if (code < 0) goto exit; |
1572 | | |
1573 | 0 | code = pdfi_dict_knownget_type(ctx, intent, "OutputConditionIdentifier", PDF_STRING, |
1574 | 0 | (pdf_obj **)&name); |
1575 | 0 | if (code < 0) goto exit; |
1576 | 0 | if (code == 0) |
1577 | 0 | continue; |
1578 | | |
1579 | | /* If the ID is "Custom" then check "Info" instead */ |
1580 | 0 | if (pdfi_string_is(name, "Custom")) { |
1581 | 0 | pdfi_countdown(name); |
1582 | 0 | name = NULL; |
1583 | 0 | code = pdfi_dict_knownget_type(ctx, intent, "Info", PDF_STRING, (pdf_obj **)&name); |
1584 | 0 | if (code < 0) goto exit; |
1585 | 0 | if (code == 0) |
1586 | 0 | continue; |
1587 | 0 | } |
1588 | | |
1589 | | /* Check for a match */ |
1590 | 0 | if (pdfi_string_is(name, ctx->args.UseOutputIntent)) |
1591 | 0 | break; |
1592 | | |
1593 | 0 | pdfi_countdown(intent); |
1594 | 0 | intent = NULL; |
1595 | 0 | pdfi_countdown(name); |
1596 | 0 | name = NULL; |
1597 | 0 | } |
1598 | 0 | code = 0; |
1599 | 436 | } else { |
1600 | | /* No command line arg was specified, so nothing to do */ |
1601 | 436 | code = 0; |
1602 | 436 | goto exit; |
1603 | 436 | } |
1604 | | |
1605 | | /* Now if intent is non-null, we found the selected intent dictionary */ |
1606 | 0 | if (intent == NULL) |
1607 | 0 | goto exit; |
1608 | | |
1609 | | /* Load the profile, if it exists */ |
1610 | 0 | code = pdfi_dict_knownget_type(ctx, intent, "DestOutputProfile", PDF_STREAM, (pdf_obj **)&DestOutputProfile); |
1611 | | /* TODO: Flag an error if it doesn't exist? Only required in some cases */ |
1612 | 0 | if (code <= 0) goto exit; |
1613 | | |
1614 | | /* Set the intent to the profile */ |
1615 | 0 | code = pdfi_color_setoutputintent(ctx, intent, DestOutputProfile); |
1616 | |
|
1617 | 74.5k | exit: |
1618 | 74.5k | pdfi_countdown(OutputIntents); |
1619 | 74.5k | pdfi_countdown(intent); |
1620 | 74.5k | pdfi_countdown(name); |
1621 | 74.5k | pdfi_countdown(DestOutputProfile); |
1622 | 74.5k | return code; |
1623 | 0 | } |
1624 | | |
1625 | | /* Handled an embedded files Names array for pdfwrite device */ |
1626 | | static int pdfi_doc_EmbeddedFiles_Names(pdf_context *ctx, pdf_array *names) |
1627 | 25 | { |
1628 | 25 | int code; |
1629 | 25 | uint64_t arraysize; |
1630 | 25 | uint64_t index; |
1631 | 25 | pdf_string *name = NULL; |
1632 | 25 | pdf_dict *filespec = NULL; |
1633 | | |
1634 | 25 | arraysize = pdfi_array_size(names); |
1635 | 25 | if ((arraysize % 2) != 0) { |
1636 | 1 | code = gs_note_error(gs_error_syntaxerror); |
1637 | 1 | goto exit; |
1638 | 1 | } |
1639 | | |
1640 | | /* This is supposed to be an array of |
1641 | | * [ (filename1) (filespec1) (filename2) (filespec2) ... ] |
1642 | | */ |
1643 | 41 | for (index = 0; index < arraysize; index += 2) { |
1644 | 24 | code = pdfi_array_get_type(ctx, names, index, PDF_STRING, (pdf_obj **)&name); |
1645 | 24 | if (code < 0) goto exit; |
1646 | | |
1647 | 24 | code = pdfi_array_get_type(ctx, names, index+1, PDF_DICT, (pdf_obj **)&filespec); |
1648 | 24 | if (code < 0) goto exit; |
1649 | | |
1650 | 23 | code = pdfi_pdfmark_embed_filespec(ctx, name, filespec); |
1651 | 23 | if (code < 0) goto exit; |
1652 | | |
1653 | 17 | pdfi_countdown(name); |
1654 | 17 | name = NULL; |
1655 | 17 | pdfi_countdown(filespec); |
1656 | 17 | filespec = NULL; |
1657 | 17 | } |
1658 | | |
1659 | | |
1660 | 25 | exit: |
1661 | 25 | pdfi_countdown(name); |
1662 | 25 | pdfi_countdown(filespec); |
1663 | 25 | return code; |
1664 | 24 | } |
1665 | | |
1666 | | /* Handle PageLabels for pdfwrite device */ |
1667 | | static int pdfi_doc_EmbeddedFiles(pdf_context *ctx) |
1668 | 3.35k | { |
1669 | 3.35k | int code; |
1670 | 3.35k | pdf_dict *Names = NULL; |
1671 | 3.35k | pdf_dict *EmbeddedFiles = NULL; |
1672 | 3.35k | pdf_array *Names_array = NULL; |
1673 | 3.35k | pdf_array *Kids = NULL; |
1674 | | |
1675 | 3.35k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "Collection", PDF_DICT, (pdf_obj **)&Names); |
1676 | 3.35k | if (code < 0) goto exit; |
1677 | 3.35k | if (code > 0) { |
1678 | 0 | code = 0; |
1679 | 0 | goto exit; |
1680 | 0 | } |
1681 | | |
1682 | 3.35k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names); |
1683 | 3.35k | if (code <= 0) goto exit; |
1684 | | |
1685 | 211 | code = pdfi_dict_knownget_type(ctx, Names, "EmbeddedFiles", PDF_DICT, (pdf_obj **)&EmbeddedFiles); |
1686 | 211 | if (code <= 0) goto exit; |
1687 | | |
1688 | 25 | code = pdfi_dict_knownget_type(ctx, Names, "Kids", PDF_ARRAY, (pdf_obj **)&Kids); |
1689 | 25 | if (code < 0) goto exit; |
1690 | 25 | if (code > 0) { |
1691 | | /* TODO: Need to implement */ |
1692 | 0 | errprintf(ctx->memory, "*** WARNING Kids array in EmbeddedFiles not implemented\n"); |
1693 | 0 | } |
1694 | | |
1695 | | /* TODO: This is a name tree. |
1696 | | * Can contain a Names array, or some complicated Kids. |
1697 | | * Just handling Names array for now |
1698 | | */ |
1699 | 25 | code = pdfi_dict_knownget_type(ctx, EmbeddedFiles, "Names", PDF_ARRAY, (pdf_obj **)&Names_array); |
1700 | 25 | if (code <= 0) goto exit; |
1701 | | |
1702 | 25 | code = pdfi_doc_EmbeddedFiles_Names(ctx, Names_array); |
1703 | 25 | if (code <= 0) goto exit; |
1704 | | |
1705 | 3.35k | exit: |
1706 | 3.35k | pdfi_countdown(Kids); |
1707 | 3.35k | pdfi_countdown(Names); |
1708 | 3.35k | pdfi_countdown(EmbeddedFiles); |
1709 | 3.35k | pdfi_countdown(Names_array); |
1710 | 3.35k | return code; |
1711 | 25 | } |
1712 | | |
1713 | | /* Handle some bookkeeping related to AcroForm (and annotations) |
1714 | | * See pdf_main.ps/process_trailer_attrs/AcroForm |
1715 | | * |
1716 | | * Mainly we preload AcroForm and NeedAppearances in the context |
1717 | | * |
1718 | | * TODO: gs code also seems to do something to link up parents in fields/annotations (ParentField) |
1719 | | * We are going to avoid doing that for now. |
1720 | | */ |
1721 | | static int pdfi_doc_AcroForm(pdf_context *ctx) |
1722 | 74.5k | { |
1723 | 74.5k | int code = 0; |
1724 | 74.5k | pdf_dict *AcroForm = NULL; |
1725 | 74.5k | bool boolval = false; |
1726 | | |
1727 | 74.5k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "AcroForm", PDF_DICT, (pdf_obj **)&AcroForm); |
1728 | 74.5k | if (code <= 0) goto exit; |
1729 | | |
1730 | 5.95k | code = pdfi_dict_get_bool(ctx, AcroForm, "NeedAppearances", &boolval); |
1731 | 5.95k | if (code < 0) { |
1732 | 5.46k | if (code == gs_error_undefined) { |
1733 | 5.46k | boolval = true; |
1734 | 5.46k | code = 0; |
1735 | 5.46k | } |
1736 | 3 | else |
1737 | 3 | goto exit; |
1738 | 5.46k | } |
1739 | 5.95k | ctx->NeedAppearances = boolval; |
1740 | | |
1741 | | /* Save this for efficiency later */ |
1742 | 5.95k | ctx->AcroForm = AcroForm; |
1743 | 5.95k | pdfi_countup(AcroForm); |
1744 | | |
1745 | | /* TODO: Link up ParentField (but hopefully we can avoid doing this hacky mess). |
1746 | | * Also: Something to do with Bug692447.pdf? |
1747 | | */ |
1748 | | |
1749 | | |
1750 | 74.5k | exit: |
1751 | 74.5k | pdfi_countdown(AcroForm); |
1752 | 74.5k | return code; |
1753 | 5.95k | } |
1754 | | |
1755 | | |
1756 | | static int pdfi_doc_view(pdf_context *ctx) |
1757 | 3.35k | { |
1758 | 3.35k | int code = 0; |
1759 | 3.35k | pdf_dict *tempdict = NULL; |
1760 | 3.35k | pdf_name *Mode = NULL, *Layout = NULL; |
1761 | 3.35k | pdf_obj *ActionDest = NULL; |
1762 | | |
1763 | 3.35k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageMode", PDF_NAME, (pdf_obj **)&Mode); |
1764 | 3.35k | if (code < 0) |
1765 | 0 | return code; |
1766 | | |
1767 | 3.35k | if (code != 0) { |
1768 | 247 | code = pdfi_dict_alloc(ctx, 1, &tempdict); |
1769 | 247 | if (code < 0) |
1770 | 0 | goto exit; |
1771 | | |
1772 | 247 | pdfi_countup(tempdict); |
1773 | | |
1774 | 247 | code = pdfi_dict_put(ctx, tempdict, "PageMode", (pdf_obj *)Mode); |
1775 | 247 | if (code < 0) |
1776 | 0 | goto exit; |
1777 | | |
1778 | 247 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW"); |
1779 | 247 | if (code < 0) |
1780 | 0 | goto exit; |
1781 | 247 | pdfi_countdown(tempdict); |
1782 | 247 | tempdict = NULL; |
1783 | 247 | } |
1784 | | |
1785 | 3.35k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageLayout", PDF_NAME, (pdf_obj **)&Layout); |
1786 | 3.35k | if (code < 0) |
1787 | 0 | goto exit; |
1788 | | |
1789 | 3.35k | if (code != 0) { |
1790 | 240 | code = pdfi_dict_alloc(ctx, 1, &tempdict); |
1791 | 240 | if (code < 0) |
1792 | 0 | goto exit; |
1793 | | |
1794 | 240 | pdfi_countup(tempdict); |
1795 | | |
1796 | 240 | code = pdfi_dict_put(ctx, tempdict, "PageLayout", (pdf_obj *)Layout); |
1797 | 240 | if (code < 0) |
1798 | 0 | goto exit; |
1799 | | |
1800 | 240 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW"); |
1801 | 240 | if (code < 0) |
1802 | 0 | goto exit; |
1803 | 240 | pdfi_countdown(tempdict); |
1804 | 240 | tempdict = NULL; |
1805 | 240 | } |
1806 | | |
1807 | 3.35k | code = pdfi_dict_knownget(ctx, ctx->Root, "OpenAction", &ActionDest); |
1808 | 3.35k | if (code < 0) |
1809 | 12 | goto exit; |
1810 | | |
1811 | 3.33k | if (code != 0) { |
1812 | 171 | if (pdfi_type_of(ActionDest) == PDF_DICT) { |
1813 | | /* Dictionary means this is an action */ |
1814 | 4 | code = pdfi_dict_alloc(ctx, 1, &tempdict); |
1815 | 4 | if (code < 0) |
1816 | 0 | goto exit; |
1817 | | |
1818 | 4 | pdfi_countup(tempdict); |
1819 | | |
1820 | 4 | code = pdfi_dict_put(ctx, tempdict, "A", (pdf_obj *)ActionDest); |
1821 | 4 | if (code < 0) |
1822 | 0 | goto exit; |
1823 | | |
1824 | 4 | code = pdfi_pdfmark_modA(ctx, tempdict); |
1825 | 4 | if (code < 0) goto exit; |
1826 | | |
1827 | 4 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW"); |
1828 | 167 | } else { |
1829 | 167 | if (pdfi_type_of(ActionDest) == PDF_ARRAY) { |
1830 | | /* Array means this is a destination */ |
1831 | 167 | code = pdfi_dict_alloc(ctx, 1, &tempdict); |
1832 | 167 | if (code < 0) |
1833 | 0 | goto exit; |
1834 | | |
1835 | 167 | pdfi_countup(tempdict); |
1836 | | |
1837 | 167 | code = pdfi_dict_put(ctx, tempdict, "Dest", (pdf_obj *)ActionDest); |
1838 | 167 | if (code < 0) |
1839 | 0 | goto exit; |
1840 | 167 | code = pdfi_pdfmark_modDest(ctx, tempdict); |
1841 | 167 | if (code < 0) |
1842 | 33 | goto exit; |
1843 | 134 | code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW"); |
1844 | 134 | if (code < 0) |
1845 | 0 | goto exit; |
1846 | 134 | } else { |
1847 | 0 | code = gs_note_error(gs_error_typecheck); |
1848 | 0 | goto exit; |
1849 | 0 | } |
1850 | 167 | } |
1851 | 171 | } |
1852 | | |
1853 | 3.35k | exit: |
1854 | 3.35k | pdfi_countdown(ActionDest); |
1855 | 3.35k | pdfi_countdown(Layout); |
1856 | 3.35k | pdfi_countdown(Mode); |
1857 | 3.35k | pdfi_countdown(tempdict); |
1858 | 3.35k | return code; |
1859 | 3.33k | } |
1860 | | |
1861 | | |
1862 | | /* See pdf_main.ps/process_trailer_attrs() |
1863 | | * Some of this stuff is about pdfmarks, and some of it is just handling |
1864 | | * random things in the trailer. |
1865 | | */ |
1866 | | int pdfi_doc_trailer(pdf_context *ctx) |
1867 | 74.5k | { |
1868 | 74.5k | int code = 0; |
1869 | | |
1870 | | /* Can't do this stuff with no Trailer */ |
1871 | 74.5k | if (!ctx->Trailer) { |
1872 | 39.3k | if ((code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_undefined), NULL, W_PDF_BAD_TRAILER, "pdfi_doc_trailer", NULL)) < 0) |
1873 | 0 | goto exit; |
1874 | 39.3k | } |
1875 | | |
1876 | 74.5k | if (ctx->device_state.writepdfmarks) { |
1877 | | /* Handle Outlines */ |
1878 | 3.35k | code = pdfi_doc_Outlines(ctx); |
1879 | 3.35k | if (code < 0) { |
1880 | 30 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTLINES, "pdfi_doc_trailer", NULL)) < 0) |
1881 | 0 | goto exit; |
1882 | 30 | } |
1883 | | |
1884 | | /* Handle Docview pdfmark stuff */ |
1885 | 3.35k | if (ctx->args.preservedocview) { |
1886 | 3.35k | code = pdfi_doc_view(ctx); |
1887 | 3.35k | if (code < 0) { |
1888 | 45 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_VIEW, "pdfi_doc_view", NULL)) < 0) |
1889 | 0 | goto exit; |
1890 | 45 | } |
1891 | 3.35k | } |
1892 | | |
1893 | | /* Handle Info */ |
1894 | 3.35k | code = pdfi_doc_Info(ctx); |
1895 | 3.35k | if (code < 0) { |
1896 | | /* pdfwrite will set a Fatal error when processing the DOCINFO if it has been |
1897 | | * told to create a PDF/A. the PDFA compatibility is 2, and the file info |
1898 | | * cannot be handled. In that case, abort immediately. |
1899 | | */ |
1900 | 2.57k | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_INFO, "pdfi_doc_trailer", NULL)) < 0) |
1901 | 0 | goto exit; |
1902 | 2.57k | } |
1903 | | |
1904 | | /* Handle EmbeddedFiles */ |
1905 | | /* TODO: add a configuration option to embed or omit */ |
1906 | 3.35k | if (ctx->args.preserveembeddedfiles) { |
1907 | 3.35k | code = pdfi_doc_EmbeddedFiles(ctx); |
1908 | 3.35k | if (code < 0) { |
1909 | 37 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_EMBEDDEDFILES, "pdfi_doc_trailer", NULL)) < 0) |
1910 | 0 | goto exit; |
1911 | 37 | } |
1912 | 3.35k | } |
1913 | 3.35k | } |
1914 | | |
1915 | | /* Handle OCProperties */ |
1916 | | /* NOTE: Apparently already handled by pdfi_read_OptionalRoot() */ |
1917 | | |
1918 | | /* Handle AcroForm -- this is some bookkeeping once per doc, not rendering them yet */ |
1919 | 74.5k | code = pdfi_doc_AcroForm(ctx); |
1920 | 74.5k | if (code < 0) { |
1921 | 899 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_ACROFORM, "pdfi_doc_trailer", NULL)) < 0) |
1922 | 0 | goto exit; |
1923 | 899 | } |
1924 | | |
1925 | | /* Handle OutputIntent ICC Profile */ |
1926 | 74.5k | code = pdfi_doc_OutputIntents(ctx); |
1927 | 74.5k | if (code < 0) { |
1928 | 0 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTPUTINTENTS, "pdfi_doc_trailer", NULL)) < 0) |
1929 | 0 | goto exit; |
1930 | 0 | } |
1931 | | |
1932 | | /* Handle PageLabels */ |
1933 | 74.5k | code = pdfi_doc_PageLabels(ctx); |
1934 | 74.5k | if (code < 0) { |
1935 | 111 | if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_PAGELABELS, "pdfi_doc_trailer", NULL)) < 0) |
1936 | 0 | goto exit; |
1937 | 111 | } |
1938 | | |
1939 | 74.5k | exit: |
1940 | 74.5k | return code; |
1941 | 74.5k | } |