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