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