/src/ghostpdl/pdf/ghostpdf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2018-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 | | /* Top level PDF access routines */ |
17 | | #include "ghostpdf.h" |
18 | | #include "pdf_types.h" |
19 | | #include "pdf_dict.h" |
20 | | #include "pdf_array.h" |
21 | | #include "pdf_int.h" |
22 | | #include "pdf_misc.h" |
23 | | #include "pdf_stack.h" |
24 | | #include "pdf_file.h" |
25 | | #include "pdf_loop_detect.h" |
26 | | #include "pdf_trans.h" |
27 | | #include "pdf_font_types.h" |
28 | | #include "pdf_gstate.h" |
29 | | #include "stream.h" |
30 | | #include "strmio.h" |
31 | | #include "pdf_colour.h" |
32 | | #include "pdf_font.h" |
33 | | #include "pdf_text.h" |
34 | | #include "pdf_page.h" |
35 | | #include "pdf_check.h" |
36 | | #include "pdf_optcontent.h" |
37 | | #include "pdf_sec.h" |
38 | | #include "pdf_doc.h" |
39 | | #include "pdf_repair.h" |
40 | | #include "pdf_xref.h" |
41 | | #include "pdf_device.h" |
42 | | #include "pdf_mark.h" |
43 | | |
44 | | #include "gsstate.h" /* For gs_gstate */ |
45 | | #include "gsicc_manage.h" /* For gsicc_init_iccmanager() */ |
46 | | |
47 | | #if PDFI_LEAK_CHECK |
48 | | #include "gsmchunk.h" |
49 | | #endif |
50 | | |
51 | | #ifndef USE_PDF_PERMISSIONS |
52 | | #define USE_PDF_PERMISSIONS 0 |
53 | | #endif |
54 | | |
55 | | extern const char gp_file_name_list_separator; |
56 | | /* |
57 | | * Convenience routine to check if a given string exists in a dictionary |
58 | | * verify its contents and print it in a particular fashion to stdout. This |
59 | | * is used to display information about the PDF in response to -dPDFINFO |
60 | | */ |
61 | | static int dump_info_string(pdf_context *ctx, pdf_dict *source_dict, const char *Key) |
62 | 0 | { |
63 | 0 | int code; |
64 | 0 | pdf_string *s = NULL; |
65 | 0 | char *Cstr; |
66 | |
|
67 | 0 | code = pdfi_dict_knownget_type(ctx, source_dict, Key, PDF_STRING, (pdf_obj **)&s); |
68 | 0 | if (code > 0) { |
69 | 0 | Cstr = (char *)gs_alloc_bytes(ctx->memory, s->length + 1, "Working memory for string dumping"); |
70 | 0 | if (Cstr) { |
71 | 0 | memcpy(Cstr, s->data, s->length); |
72 | 0 | Cstr[s->length] = 0x00; |
73 | 0 | outprintf(ctx->memory, "%s: %s\n", Key, Cstr); |
74 | 0 | gs_free_object(ctx->memory, Cstr, "Working memory for string dumping"); |
75 | 0 | } |
76 | 0 | code = 0; |
77 | 0 | } |
78 | 0 | pdfi_countdown(s); |
79 | |
|
80 | 0 | return code; |
81 | 0 | } |
82 | | |
83 | | static int pdfi_output_metadata(pdf_context *ctx) |
84 | 0 | { |
85 | 0 | int code = 0; |
86 | |
|
87 | 0 | if (ctx->filename != NULL) |
88 | 0 | outprintf(ctx->memory, "\n %s has %"PRIi64" ", ctx->filename, ctx->num_pages); |
89 | 0 | else |
90 | 0 | outprintf(ctx->memory, "\n File has %"PRIi64" ", ctx->num_pages); |
91 | |
|
92 | 0 | if (ctx->num_pages > 1) |
93 | 0 | outprintf(ctx->memory, "pages\n\n"); |
94 | 0 | else |
95 | 0 | outprintf(ctx->memory, "page.\n\n"); |
96 | |
|
97 | 0 | if (ctx->Info != NULL) { |
98 | 0 | pdf_name *n = NULL; |
99 | 0 | char *Cstr; |
100 | |
|
101 | 0 | code = dump_info_string(ctx, ctx->Info, "Title"); |
102 | 0 | if (code < 0) { |
103 | 0 | if (ctx->args.pdfstoponerror) |
104 | 0 | return code; |
105 | 0 | } |
106 | | |
107 | 0 | code = dump_info_string(ctx, ctx->Info, "Author"); |
108 | 0 | if (code < 0) { |
109 | 0 | if (ctx->args.pdfstoponerror) |
110 | 0 | return code; |
111 | 0 | } |
112 | | |
113 | 0 | code = dump_info_string(ctx, ctx->Info, "Subject"); |
114 | 0 | if (code < 0) { |
115 | 0 | if (ctx->args.pdfstoponerror) |
116 | 0 | return code; |
117 | 0 | } |
118 | | |
119 | 0 | code = dump_info_string(ctx, ctx->Info, "Keywords"); |
120 | 0 | if (code < 0) { |
121 | 0 | if (ctx->args.pdfstoponerror) |
122 | 0 | return code; |
123 | 0 | } |
124 | | |
125 | 0 | code = dump_info_string(ctx, ctx->Info, "Creator"); |
126 | 0 | if (code < 0) { |
127 | 0 | if (ctx->args.pdfstoponerror) |
128 | 0 | return code; |
129 | 0 | } |
130 | | |
131 | 0 | code = dump_info_string(ctx, ctx->Info, "Producer"); |
132 | 0 | if (code < 0) { |
133 | 0 | if (ctx->args.pdfstoponerror) |
134 | 0 | return code; |
135 | 0 | } |
136 | | |
137 | 0 | code = dump_info_string(ctx, ctx->Info, "CreationDate"); |
138 | 0 | if (code < 0) { |
139 | 0 | if (ctx->args.pdfstoponerror) |
140 | 0 | return code; |
141 | 0 | } |
142 | | |
143 | 0 | code = dump_info_string(ctx, ctx->Info, "ModDate"); |
144 | 0 | if (code < 0) { |
145 | 0 | if (ctx->args.pdfstoponerror) |
146 | 0 | return code; |
147 | 0 | } |
148 | | |
149 | | |
150 | 0 | code = pdfi_dict_knownget_type(ctx, ctx->Info, "Trapped", PDF_NAME, (pdf_obj **)&n); |
151 | 0 | if (code > 0) { |
152 | 0 | Cstr = (char *)gs_alloc_bytes(ctx->memory, n->length + 1, "Working memory for string dumping"); |
153 | 0 | if (Cstr) { |
154 | 0 | memcpy(Cstr, n->data, n->length); |
155 | 0 | Cstr[n->length] = 0x00; |
156 | 0 | outprintf(ctx->memory, "Trapped: %s\n\n", Cstr); |
157 | 0 | gs_free_object(ctx->memory, Cstr, "Working memory for string dumping"); |
158 | 0 | } |
159 | 0 | code = 0; |
160 | 0 | } |
161 | 0 | pdfi_countdown(n); |
162 | 0 | n = NULL; |
163 | 0 | } |
164 | 0 | outprintf(ctx->memory, "\n"); |
165 | 0 | return code; |
166 | 0 | } |
167 | | |
168 | | /* |
169 | | * Convenience routine to check if a given *Box exists in a page dictionary |
170 | | * verify its contents and print it in a particular fashion to stdout. This |
171 | | * is used to display information about the PDF in response to -dPDFINFO |
172 | | */ |
173 | | static int pdfi_dump_box(pdf_context *ctx, pdf_dict *page_dict, const char *Key) |
174 | 0 | { |
175 | 0 | int code, i; |
176 | 0 | pdf_array *a = NULL; |
177 | 0 | double f; |
178 | |
|
179 | 0 | code = pdfi_dict_knownget_type(ctx, page_dict, Key, PDF_ARRAY, (pdf_obj **)&a); |
180 | 0 | if (code > 0) { |
181 | 0 | if (pdfi_array_size(a) != 4) { |
182 | 0 | outprintf(ctx->memory, "Error - %s does not contain 4 values.\n", Key); |
183 | 0 | code = gs_note_error(gs_error_rangecheck); |
184 | 0 | } else { |
185 | 0 | outprintf(ctx->memory, " %s: [", Key); |
186 | 0 | for (i = 0; i < pdfi_array_size(a); i++) { |
187 | 0 | code = pdfi_array_get_number(ctx, a, (uint64_t)i, &f); |
188 | 0 | if (i != 0) |
189 | 0 | outprintf(ctx->memory, " "); |
190 | 0 | if (code == 0) { |
191 | 0 | if (pdfi_type_of(a->values[i]) == PDF_INT) |
192 | 0 | outprintf(ctx->memory, "%"PRIi64"", ((pdf_num *)a->values[i])->value.i); |
193 | 0 | else |
194 | 0 | outprintf(ctx->memory, "%f", ((pdf_num *)a->values[i])->value.d); |
195 | 0 | } else { |
196 | 0 | outprintf(ctx->memory, "NAN"); |
197 | 0 | } |
198 | 0 | } |
199 | 0 | outprintf(ctx->memory, "]"); |
200 | 0 | } |
201 | 0 | } |
202 | 0 | pdfi_countdown(a); |
203 | 0 | return code; |
204 | 0 | } |
205 | | |
206 | | static int dump_font(pdf_context *ctx, pdf_dict *font_dict, bool space_name) |
207 | 0 | { |
208 | 0 | pdf_obj *obj = NULL; |
209 | 0 | char *str = NULL; |
210 | 0 | int len = 0, code = 0, i; |
211 | 0 | bool known = false, type0 = false; |
212 | |
|
213 | 0 | code = pdfi_dict_get_type(ctx, font_dict, "BaseFont", PDF_NAME, &obj); |
214 | 0 | if (code >= 0) { |
215 | 0 | code = pdfi_string_from_name(ctx, (pdf_name *)obj, &str, &len); |
216 | 0 | if (code >= 0) { |
217 | 0 | outprintf(ctx->memory, "%s", str); |
218 | 0 | if (len < 32 && space_name) { |
219 | 0 | for (i = 0; i < 32 - len;i++) |
220 | 0 | outprintf(ctx->memory, " "); |
221 | 0 | } else |
222 | 0 | outprintf(ctx->memory, " "); |
223 | 0 | (void)pdfi_free_string_from_name(ctx, str); |
224 | 0 | } |
225 | 0 | pdfi_countdown(obj); |
226 | 0 | obj = NULL; |
227 | 0 | } |
228 | |
|
229 | 0 | code = pdfi_dict_get_type(ctx, font_dict, "Subtype", PDF_NAME, &obj); |
230 | 0 | if (code >= 0) { |
231 | 0 | code = pdfi_string_from_name(ctx, (pdf_name *)obj, &str, &len); |
232 | 0 | if (code >= 0) { |
233 | 0 | outprintf(ctx->memory, "%s", str); |
234 | 0 | for (i = 0; i < 16 - len;i++) |
235 | 0 | outprintf(ctx->memory, " "); |
236 | 0 | (void)pdfi_free_string_from_name(ctx, str); |
237 | 0 | } |
238 | 0 | if (pdfi_name_is((pdf_name *)obj, "Type0")) |
239 | 0 | type0 = true; |
240 | 0 | pdfi_countdown(obj); |
241 | 0 | obj = NULL; |
242 | 0 | } |
243 | |
|
244 | 0 | if (!type0) { |
245 | 0 | code = pdfi_dict_get_type(ctx, font_dict, "Embedded", PDF_BOOL, &obj); |
246 | 0 | if (code >= 0) { |
247 | 0 | if (obj == PDF_FALSE_OBJ) |
248 | 0 | outprintf(ctx->memory, "Not embedded "); |
249 | 0 | else |
250 | 0 | outprintf(ctx->memory, "Embedded "); |
251 | 0 | pdfi_countdown(obj); |
252 | 0 | obj = NULL; |
253 | 0 | } |
254 | 0 | else |
255 | 0 | outprintf(ctx->memory, "Not embedded "); |
256 | 0 | } else |
257 | 0 | outprintf(ctx->memory, " "); |
258 | |
|
259 | 0 | code = pdfi_dict_get_type(ctx, font_dict, "ToUnicode", PDF_BOOL, &obj); |
260 | 0 | if (code >= 0) { |
261 | 0 | if (obj == PDF_TRUE_OBJ) |
262 | 0 | outprintf(ctx->memory, "Has ToUnicode "); |
263 | 0 | else |
264 | 0 | outprintf(ctx->memory, "No ToUnicode "); |
265 | 0 | pdfi_countdown(obj); |
266 | 0 | obj = NULL; |
267 | 0 | } |
268 | 0 | else |
269 | 0 | outprintf(ctx->memory, "No ToUnicode "); |
270 | |
|
271 | 0 | code = pdfi_dict_known(ctx, font_dict, "Descendants", &known); |
272 | 0 | if (code >= 0 && known) { |
273 | 0 | code = pdfi_dict_get_type(ctx, font_dict, "Descendants", PDF_ARRAY, &obj); |
274 | 0 | if (code >= 0) { |
275 | 0 | pdf_obj *desc = NULL; |
276 | |
|
277 | 0 | code = pdfi_array_get_type(ctx, (pdf_array *)obj, 0, PDF_DICT, &desc); |
278 | 0 | if (code >= 0) { |
279 | 0 | outprintf(ctx->memory, "\n Descendants: ["); |
280 | 0 | (void)dump_font(ctx, (pdf_dict *)desc, false); |
281 | 0 | outprintf(ctx->memory, "]"); |
282 | 0 | } |
283 | 0 | pdfi_countdown(obj); |
284 | 0 | obj = NULL; |
285 | 0 | } |
286 | 0 | } |
287 | 0 | return 0; |
288 | 0 | } |
289 | | |
290 | | /* |
291 | | * This routine along with pdfi_output_metadtaa above, dumps certain kinds |
292 | | * of metadata from the PDF file, and from each page in the PDF file. It is |
293 | | * intended to duplicate the pdf_info.ps functionality of the PostScript-based |
294 | | * PDF interpreter in Ghostscript. |
295 | | * |
296 | | * It is not yet complete, we don't allow an option for dumping media sizes |
297 | | * we always emit them, and the switches -dDumpFontsNeeded, -dDumpXML, |
298 | | * -dDumpFontsUsed and -dShowEmbeddedFonts are not implemented at all yet. |
299 | | */ |
300 | | int pdfi_output_page_info(pdf_context *ctx, uint64_t page_num) |
301 | 0 | { |
302 | 0 | int code; |
303 | 0 | bool known = false; |
304 | 0 | double f; |
305 | 0 | pdf_dict *page_dict = NULL; |
306 | 0 | pdf_array *fonts_array = NULL, *spots_array = NULL; |
307 | |
|
308 | 0 | code = pdfi_page_get_dict(ctx, page_num, &page_dict); |
309 | 0 | if (code < 0) |
310 | 0 | return code; |
311 | | |
312 | 0 | outprintf(ctx->memory, "Page %"PRIi64"", page_num + 1); |
313 | |
|
314 | 0 | code = pdfi_dict_knownget_number(ctx, page_dict, "UserUnit", &f); |
315 | 0 | if (code > 0) |
316 | 0 | outprintf(ctx->memory, " UserUnit: %f ", f); |
317 | 0 | if (code < 0) { |
318 | 0 | pdfi_countdown(page_dict); |
319 | 0 | return code; |
320 | 0 | } |
321 | | |
322 | 0 | code = pdfi_dump_box(ctx, page_dict, "MediaBox"); |
323 | 0 | if (code < 0) { |
324 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) { |
325 | 0 | pdfi_countdown(page_dict); |
326 | 0 | return code; |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | 0 | code = pdfi_dump_box(ctx, page_dict, "CropBox"); |
331 | 0 | if (code < 0) { |
332 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) { |
333 | 0 | pdfi_countdown(page_dict); |
334 | 0 | return code; |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | 0 | code = pdfi_dump_box(ctx, page_dict, "BleedBox"); |
339 | 0 | if (code < 0) { |
340 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) { |
341 | 0 | pdfi_countdown(page_dict); |
342 | 0 | return code; |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | 0 | code = pdfi_dump_box(ctx, page_dict, "TrimBox"); |
347 | 0 | if (code < 0) { |
348 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) { |
349 | 0 | pdfi_countdown(page_dict); |
350 | 0 | return code; |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | 0 | code = pdfi_dump_box(ctx, page_dict, "ArtBox"); |
355 | 0 | if (code < 0) { |
356 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) { |
357 | 0 | pdfi_countdown(page_dict); |
358 | 0 | return code; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | 0 | code = pdfi_dict_knownget_number(ctx, page_dict, "Rotate", &f); |
363 | 0 | if (code > 0) |
364 | 0 | outprintf(ctx->memory, " Rotate = %d ", (int)f); |
365 | 0 | if (code < 0) { |
366 | 0 | pdfi_countdown(page_dict); |
367 | 0 | return code; |
368 | 0 | } |
369 | | |
370 | 0 | code = pdfi_check_page(ctx, page_dict, &fonts_array, &spots_array, false); |
371 | 0 | if (code < 0) { |
372 | 0 | if (ctx->args.pdfstoponerror) |
373 | 0 | return code; |
374 | 0 | } else { |
375 | 0 | if (ctx->page.has_transparency) |
376 | 0 | outprintf(ctx->memory, " Page uses transparency features"); |
377 | 0 | } |
378 | | |
379 | 0 | code = pdfi_dict_known(ctx, page_dict, "Annots", &known); |
380 | 0 | if (code < 0) { |
381 | 0 | if (code != gs_error_undefined && ctx->args.pdfstoponerror) |
382 | 0 | goto error; |
383 | 0 | } else { |
384 | 0 | if (known == true) |
385 | 0 | outprintf(ctx->memory, " Page contains Annotations"); |
386 | 0 | code = 0; |
387 | 0 | } |
388 | | |
389 | 0 | if (spots_array != NULL) { |
390 | 0 | uint64_t index = 0; |
391 | 0 | pdf_name *spot = NULL; |
392 | 0 | char *str = NULL; |
393 | 0 | int len; |
394 | |
|
395 | 0 | outprintf(ctx->memory, "\n Page Spot colors: \n"); |
396 | 0 | for (index = 0;index < pdfi_array_size(spots_array);index++) { |
397 | 0 | code = pdfi_array_get(ctx, spots_array, index, (pdf_obj **)&spot); |
398 | 0 | if (code >= 0) { |
399 | 0 | if (pdfi_type_of(spot) == PDF_NAME) { |
400 | 0 | code = pdfi_string_from_name(ctx, spot, &str, &len); |
401 | 0 | if (code >= 0) { |
402 | 0 | outprintf(ctx->memory, " '%s'\n", str); |
403 | 0 | (void)pdfi_free_string_from_name(ctx, str); |
404 | 0 | } |
405 | 0 | } |
406 | 0 | pdfi_countdown(spot); |
407 | 0 | spot = NULL; |
408 | 0 | } |
409 | 0 | } |
410 | 0 | code = 0; |
411 | 0 | } |
412 | |
|
413 | 0 | if (fonts_array != NULL && pdfi_array_size(fonts_array) != 0) { |
414 | 0 | uint64_t index = 0; |
415 | 0 | pdf_dict *font_dict = NULL; |
416 | |
|
417 | 0 | outprintf(ctx->memory, "\n Fonts used: \n"); |
418 | 0 | for (index = 0;index < pdfi_array_size(fonts_array);index++) { |
419 | 0 | code = pdfi_array_get_type(ctx, fonts_array, index, PDF_DICT, (pdf_obj **)&font_dict); |
420 | 0 | if (code >= 0) { |
421 | 0 | outprintf(ctx->memory, " "); |
422 | 0 | (void)dump_font(ctx, font_dict, true); |
423 | 0 | outprintf(ctx->memory, "\n"); |
424 | 0 | pdfi_countdown(font_dict); |
425 | 0 | font_dict = NULL; |
426 | 0 | } |
427 | 0 | } |
428 | 0 | code = 0; |
429 | 0 | } |
430 | |
|
431 | 0 | error: |
432 | 0 | pdfi_countdown(fonts_array); |
433 | 0 | pdfi_countdown(spots_array); |
434 | 0 | outprintf(ctx->memory, "\n\n"); |
435 | 0 | pdfi_countdown(page_dict); |
436 | |
|
437 | 0 | return code; |
438 | 0 | } |
439 | | |
440 | | /* Error and warning string tables. There should be a string for each error and warning |
441 | | * defined in the error (pdf_error_e) and warning (pdf_warning_e) enumerators. Having |
442 | | * more strings is harmless, having too few may cause crashes. These need to be kept in sync. |
443 | | * |
444 | | * The Ghostscript graphics library errors should be kept up to date, but this is less critical |
445 | | * if the error code is greater than the last error we know about we just print a generic |
446 | | * 'unknown' message. |
447 | | */ |
448 | | const char *pdf_error_strings[] = { |
449 | | #define PARAM(A,B) B |
450 | | #include "pdf_errors.h" |
451 | | "" /* last error, should not be used */ |
452 | | }; |
453 | | |
454 | | const char *pdf_warning_strings[] = { |
455 | | #define PARAM(A,B) B |
456 | | #include "pdf_warnings.h" |
457 | | "" /* Last warning should not be used */ |
458 | | }; |
459 | | |
460 | | const char *gs_error_strings[] = { |
461 | | "no error", |
462 | | "unknownerror", |
463 | | "dictfull", |
464 | | "dictstackoverflow", |
465 | | "dictstackunderflow", |
466 | | "execstackoverflow", |
467 | | "interrupt", |
468 | | "invalidaccess", |
469 | | "invalidexit", |
470 | | "invalidfileaccess", |
471 | | "invalidfont", |
472 | | "invalidrestore", |
473 | | "ioerror", |
474 | | "limitcheck", |
475 | | "nocurrentpoint", |
476 | | "rangecheck", |
477 | | "stackoverflow", |
478 | | "stackunderflow", |
479 | | "syntaxerror", |
480 | | "timeout", |
481 | | "typecheck", |
482 | | "undefined", |
483 | | "undefinedfilename", |
484 | | "undefinedresult", |
485 | | "unmatchedmark", |
486 | | "VMerror", |
487 | | "configurationerror", |
488 | | "undefinedresource", |
489 | | "unregistered", |
490 | | "invalidcontext", |
491 | | "invalidid", |
492 | | "pdf_stackoverflow", |
493 | | "circular reference" |
494 | | }; |
495 | | |
496 | | const char *gs_internal_error_strings[] = { |
497 | | "error hit", |
498 | | "fatal error", |
499 | | "quit", |
500 | | "interpreter exit", |
501 | | "remap color", |
502 | | "exec stack underflow", |
503 | | "VMreclaim", |
504 | | "Need input", |
505 | | "need file", |
506 | | "No defined error", |
507 | | "No defined error (2)", |
508 | | "error info", |
509 | | "handled", |
510 | | }; |
511 | 0 | #define LASTNORMALGSERROR gs_error_circular_reference * -1 |
512 | 0 | #define FIRSTINTERNALERROR gs_error_hit_detected * -1 |
513 | 0 | #define LASTGSERROR gs_error_handled * -1 |
514 | | |
515 | | void pdfi_verbose_error(pdf_context *ctx, int gs_error, const char *gs_lib_function, int pdfi_error, const char *pdfi_function_name, const char *extra_info, const char *file_line) |
516 | 0 | { |
517 | 0 | char fallback[] = "unknown graphics library error"; |
518 | |
|
519 | 0 | if (ctx->args.verbose_errors && !ctx->args.QUIET) { |
520 | 0 | if (gs_error != 0) { |
521 | 0 | char *error_string; |
522 | 0 | unsigned int code = gs_error * -1; |
523 | |
|
524 | 0 | if (code > LASTGSERROR) |
525 | 0 | error_string = fallback; |
526 | 0 | else { |
527 | 0 | if (code > LASTNORMALGSERROR) { |
528 | 0 | if (code < FIRSTINTERNALERROR) |
529 | 0 | error_string = fallback; |
530 | 0 | else |
531 | 0 | error_string = (char *)gs_internal_error_strings[code - FIRSTINTERNALERROR]; |
532 | 0 | } else |
533 | 0 | error_string = (char *)gs_error_strings[code]; |
534 | 0 | } |
535 | 0 | errprintf(ctx->memory, "Graphics library error %d (%s) in function '%s'", gs_error, error_string, pdfi_function_name); |
536 | 0 | if (gs_lib_function != NULL) |
537 | 0 | errprintf(ctx->memory, " from lib routine '%s'", gs_lib_function); |
538 | |
|
539 | | #if DEBUG_FILE_LINE==1 |
540 | | if (file_line != NULL) |
541 | | errprintf(ctx->memory, "%s'.\n", file_line); |
542 | | else |
543 | | errprintf(ctx->memory, ".\n"); |
544 | | #else |
545 | 0 | errprintf(ctx->memory, ".\n"); |
546 | 0 | #endif |
547 | |
|
548 | 0 | if (pdfi_error != 0) |
549 | 0 | errprintf(ctx->memory, "\tSetting pdfi error %d - %s.\n", pdfi_error, pdf_error_strings[pdfi_error]); |
550 | 0 | if (extra_info != NULL) |
551 | 0 | errprintf(ctx->memory, "\t%s\n", extra_info); |
552 | 0 | } else { |
553 | 0 | if (pdfi_error != 0) { |
554 | | #if DEBUG_FILE_LINE==1 |
555 | | if (file_line != NULL) |
556 | | errprintf(ctx->memory, "Function '%s' %s set pdfi error %d - %s.\n", pdfi_function_name, file_line, pdfi_error, pdf_error_strings[pdfi_error]); |
557 | | else |
558 | | errprintf(ctx->memory, "Function '%s' set pdfi error %d - %s.\n", pdfi_function_name, pdfi_error, pdf_error_strings[pdfi_error]); |
559 | | #else |
560 | 0 | errprintf(ctx->memory, "Function '%s' set pdfi error %d - %s.\n", pdfi_function_name, pdfi_error, pdf_error_strings[pdfi_error]); |
561 | 0 | #endif |
562 | 0 | if (extra_info != NULL) |
563 | 0 | errprintf(ctx->memory, "\t%s\n", extra_info); |
564 | 0 | } else { |
565 | 0 | if (extra_info != NULL) |
566 | 0 | errprintf(ctx->memory, "%s\n", extra_info); |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | void pdfi_verbose_warning(pdf_context *ctx, int gs_error, const char *gs_lib_function, int pdfi_warning, const char *pdfi_function_name, const char *extra_info, const char *file_line) |
573 | 0 | { |
574 | 0 | char fallback[] = "unknown graphics library error"; |
575 | |
|
576 | 0 | if (ctx->args.verbose_warnings && !ctx->args.QUIET) { |
577 | 0 | if (gs_error != 0) { |
578 | 0 | char *error_string; |
579 | 0 | unsigned int code = gs_error * -1; |
580 | |
|
581 | 0 | if (code > LASTGSERROR) |
582 | 0 | error_string = fallback; |
583 | 0 | else { |
584 | 0 | if (code > LASTNORMALGSERROR) { |
585 | 0 | if (code < FIRSTINTERNALERROR) |
586 | 0 | error_string = fallback; |
587 | 0 | else |
588 | 0 | error_string = (char *)gs_internal_error_strings[code - FIRSTINTERNALERROR]; |
589 | 0 | } else |
590 | 0 | error_string = (char *)gs_error_strings[code]; |
591 | 0 | } |
592 | 0 | outprintf(ctx->memory, "Graphics library error %d (%s) in function '%s'", gs_error, error_string, pdfi_function_name); |
593 | 0 | if (gs_lib_function != NULL) |
594 | 0 | outprintf(ctx->memory, " from lib routine '%s'.\n", gs_lib_function); |
595 | |
|
596 | | #if DEBUG_FILE_LINE==1 |
597 | | if (file_line != NULL) |
598 | | errprintf(ctx->memory, "%s'.\n", file_line); |
599 | | else |
600 | | errprintf(ctx->memory, ".\n"); |
601 | | #else |
602 | 0 | errprintf(ctx->memory, ".\n"); |
603 | 0 | #endif |
604 | 0 | if (pdfi_warning != 0) |
605 | 0 | outprintf(ctx->memory, "\tsetting pdfi warning %d - %s.\n", pdfi_warning, pdf_warning_strings[pdfi_warning]); |
606 | 0 | if (extra_info != NULL) |
607 | 0 | outprintf(ctx->memory, "\t%s\n", extra_info); |
608 | 0 | } else { |
609 | 0 | if (pdfi_warning != 0) { |
610 | | #if DEBUG_FILE_LINE==1 |
611 | | if (file_line != NULL) |
612 | | outprintf(ctx->memory, "Function '%s' %s set pdfi warning %d - %s.\n", pdfi_function_name, file_line, pdfi_warning, pdf_warning_strings[pdfi_warning]); |
613 | | else |
614 | | outprintf(ctx->memory, "Function '%s' set pdfi warning %d - %s.\n", pdfi_function_name, pdfi_warning, pdf_warning_strings[pdfi_warning]); |
615 | | #else |
616 | 0 | outprintf(ctx->memory, "Function '%s' set pdfi warning %d - %s.\n", pdfi_function_name, pdfi_warning, pdf_warning_strings[pdfi_warning]); |
617 | 0 | #endif |
618 | 0 | if (extra_info != NULL) |
619 | 0 | errprintf(ctx->memory, "\t%s\n", extra_info); |
620 | 0 | } else { |
621 | 0 | if (extra_info != NULL) |
622 | 0 | errprintf(ctx->memory, "\t%s\n", extra_info); |
623 | 0 | } |
624 | 0 | } |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | | int pdfi_set_error_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_error pdfi_error, const char *pdfi_function_name, const char *fmt, ...) |
629 | 45.8k | { |
630 | 45.8k | if (pdfi_error != 0) |
631 | 45.8k | ctx->pdf_errors[pdfi_error / (sizeof(char) * 8)] |= 1 << pdfi_error % (sizeof(char) * 8); |
632 | 45.8k | if (ctx->args.verbose_errors) { |
633 | 0 | char extra_info[gp_file_name_sizeof]; |
634 | 0 | va_list args; |
635 | |
|
636 | 0 | va_start(args, fmt); |
637 | 0 | (void)vsnprintf(extra_info, sizeof(extra_info), fmt, args); |
638 | 0 | va_end(args); |
639 | |
|
640 | 0 | pdfi_verbose_error(ctx, gs_error, gs_lib_function, pdfi_error, pdfi_function_name, extra_info, NULL); |
641 | 0 | } |
642 | 45.8k | if (ctx->args.pdfstoponerror) { |
643 | 0 | if (gs_error < 0) |
644 | 0 | return gs_error; |
645 | 0 | else |
646 | 0 | return gs_error_unknownerror; |
647 | 0 | } |
648 | 45.8k | return 0; |
649 | 45.8k | } |
650 | | |
651 | | int pdfi_set_warning_var(pdf_context *ctx, int gs_error, const char *gs_lib_function, pdf_warning pdfi_warning, const char *pdfi_function_name, const char *fmt, ...) |
652 | 2.10M | { |
653 | 2.10M | ctx->pdf_warnings[pdfi_warning / (sizeof(char) * 8)] |= 1 << pdfi_warning % (sizeof(char) * 8); |
654 | 2.10M | if (ctx->args.verbose_warnings) { |
655 | 0 | char extra_info[gp_file_name_sizeof]; |
656 | 0 | va_list args; |
657 | |
|
658 | 0 | va_start(args, fmt); |
659 | 0 | (void)vsnprintf(extra_info, sizeof(extra_info), fmt, args); |
660 | 0 | va_end(args); |
661 | |
|
662 | 0 | pdfi_verbose_warning(ctx, gs_error, gs_lib_function, pdfi_warning, pdfi_function_name, extra_info, NULL); |
663 | 0 | } |
664 | 2.10M | if (ctx->args.pdfstoponwarning) { |
665 | 0 | if (gs_error < 0) |
666 | 0 | return gs_error; |
667 | 0 | else |
668 | 0 | return gs_error_unknownerror; |
669 | 0 | } |
670 | 2.10M | return 0; |
671 | 2.10M | } |
672 | | |
673 | | void |
674 | | pdfi_report_errors(pdf_context *ctx) |
675 | 74.1k | { |
676 | 74.1k | int code, i, j; |
677 | 74.1k | bool warnings_exist = false, errors_exist = false; |
678 | | |
679 | 74.1k | if (ctx->args.QUIET) |
680 | 74.1k | return; |
681 | | |
682 | 0 | for (i = 0; i < PDF_ERROR_BYTE_SIZE; i++) { |
683 | 0 | if (ctx->pdf_errors[i] != 0) |
684 | 0 | errors_exist = true; |
685 | 0 | } |
686 | |
|
687 | 0 | for (i = 0; i < PDF_WARNING_BYTE_SIZE; i++) { |
688 | 0 | if (ctx->pdf_warnings[i] != 0) |
689 | 0 | warnings_exist = true; |
690 | 0 | } |
691 | |
|
692 | 0 | if (!errors_exist && !warnings_exist) |
693 | 0 | return; |
694 | | |
695 | 0 | if (errors_exist) |
696 | 0 | { |
697 | 0 | errprintf(ctx->memory, "\nThe following errors were encountered at least once while processing this file:\n"); |
698 | 0 | for (i = 0; i < PDF_ERROR_BYTE_SIZE; i++) { |
699 | 0 | if (ctx->pdf_errors[i] != 0) { |
700 | 0 | for (j=0;j < sizeof(char) * 8; j++) { |
701 | 0 | if (ctx->pdf_errors[i] & 1 << j) { |
702 | 0 | int error_num = (i * sizeof(char) * 8) + j; |
703 | |
|
704 | 0 | errprintf(ctx->memory, "\t%s\n", pdf_error_strings[error_num]); |
705 | 0 | } |
706 | 0 | } |
707 | 0 | } |
708 | 0 | } |
709 | 0 | } |
710 | |
|
711 | 0 | if (warnings_exist) |
712 | 0 | { |
713 | 0 | outprintf(ctx->memory, "\nThe following warnings were encountered at least once while processing this file:\n"); |
714 | 0 | for (i = 0; i < PDF_WARNING_BYTE_SIZE; i++) { |
715 | 0 | if (ctx->pdf_warnings[i] != 0) { |
716 | 0 | for (j=0;j < sizeof(char) * 8; j++) { |
717 | 0 | if (ctx->pdf_warnings[i] & 1 << j) { |
718 | 0 | int warning_num = (i * sizeof(char) * 8) + j; |
719 | |
|
720 | 0 | outprintf(ctx->memory, "\t%s\n", pdf_warning_strings[warning_num]); |
721 | 0 | } |
722 | 0 | } |
723 | 0 | } |
724 | 0 | } |
725 | 0 | } |
726 | |
|
727 | 0 | if (errors_exist) |
728 | 0 | errprintf(ctx->memory, "\n **** This file had errors that were repaired or ignored.\n"); |
729 | 0 | else { |
730 | 0 | if (warnings_exist) |
731 | 0 | outprintf(ctx->memory, "\n **** This file had errors that were repaired or ignored.\n"); |
732 | 0 | } |
733 | 0 | if (ctx->Info) { |
734 | 0 | pdf_string *s = NULL; |
735 | |
|
736 | 0 | code = pdfi_dict_knownget_type(ctx, ctx->Info, "Producer", PDF_STRING, (pdf_obj **)&s); |
737 | 0 | if (code > 0) { |
738 | 0 | char *cs; |
739 | |
|
740 | 0 | cs = (char *)gs_alloc_bytes(ctx->memory, s->length + 1, "temporary string for error report"); |
741 | 0 | if (cs == NULL) { |
742 | 0 | errprintf(ctx->memory, " **** Out of memory while trying to display Producer ****\n"); |
743 | 0 | } else { |
744 | 0 | memcpy(cs, s->data, s->length); |
745 | 0 | cs[s->length] = 0x00; |
746 | 0 | if (errors_exist) |
747 | 0 | errprintf(ctx->memory, " **** The file was produced by: \n **** >>>> %s <<<<\n", cs); |
748 | 0 | else { |
749 | 0 | if (warnings_exist) |
750 | 0 | outprintf(ctx->memory, " **** The file was produced by: \n **** >>>> %s <<<<\n", cs); |
751 | 0 | } |
752 | 0 | gs_free_object(ctx->memory, cs, "temporary string for error report"); |
753 | 0 | } |
754 | 0 | } |
755 | 0 | pdfi_countdown(s); |
756 | 0 | } |
757 | 0 | if (errors_exist) { |
758 | 0 | errprintf(ctx->memory, " **** Please notify the author of the software that produced this\n"); |
759 | 0 | errprintf(ctx->memory, " **** file that it does not conform to Adobe's published PDF\n"); |
760 | 0 | errprintf(ctx->memory, " **** specification.\n\n"); |
761 | 0 | } else { |
762 | 0 | outprintf(ctx->memory, " **** Please notify the author of the software that produced this\n"); |
763 | 0 | outprintf(ctx->memory, " **** file that it does not conform to Adobe's published PDF\n"); |
764 | 0 | outprintf(ctx->memory, " **** specification.\n\n"); |
765 | 0 | } |
766 | 0 | } |
767 | | |
768 | | /* Name table |
769 | | * I've been trying to avoid this for as long as possible, but it seems it cannot |
770 | | * be evaded. We need functions to get an index for a given string (which will |
771 | | * add the string to the table if its not present) and to cleear up the table |
772 | | * on finishing a PDF file. |
773 | | */ |
774 | | |
775 | | int pdfi_get_name_index(pdf_context *ctx, char *name, int len, unsigned int *returned) |
776 | 19.2k | { |
777 | 19.2k | pdfi_name_entry_t *e = NULL, *last_entry = NULL, *new_entry = NULL; |
778 | 19.2k | int index = 0; |
779 | | |
780 | 19.2k | if (ctx->name_table == NULL) { |
781 | 1.07k | e = NULL; |
782 | 18.2k | } else { |
783 | 18.2k | e = ctx->name_table; |
784 | 18.2k | } |
785 | | |
786 | 19.2k | while(e != NULL) { |
787 | 18.2k | if (e->len == len) { |
788 | 18.2k | if (memcmp(e->name, name, e->len) == 0) { |
789 | 18.2k | *returned = e->index; |
790 | 18.2k | return 0; |
791 | 18.2k | } |
792 | 18.2k | } |
793 | 0 | last_entry = e; |
794 | 0 | index = e->index; |
795 | 0 | e = e->next; |
796 | 0 | } |
797 | | |
798 | 1.07k | new_entry = (pdfi_name_entry_t *)gs_alloc_bytes(ctx->memory, sizeof(pdfi_name_entry_t), "Alloc name table entry"); |
799 | 1.07k | if (new_entry == NULL) |
800 | 0 | return_error(gs_error_VMerror); |
801 | 1.07k | memset(new_entry, 0x00, sizeof(pdfi_name_entry_t)); |
802 | 1.07k | new_entry->name = (char *)gs_alloc_bytes(ctx->memory, len+1, "Alloc name table name"); |
803 | 1.07k | if (new_entry->name == NULL) { |
804 | 0 | gs_free_object(ctx->memory, new_entry, "Failed to allocate name entry"); |
805 | 0 | return_error(gs_error_VMerror); |
806 | 0 | } |
807 | 1.07k | memset(new_entry->name, 0x00, len+1); |
808 | 1.07k | memcpy(new_entry->name, name, len); |
809 | 1.07k | new_entry->len = len; |
810 | 1.07k | new_entry->index = ++index; |
811 | | |
812 | 1.07k | if (last_entry) |
813 | 0 | last_entry->next = new_entry; |
814 | 1.07k | else |
815 | 1.07k | ctx->name_table = new_entry; |
816 | | |
817 | 1.07k | *returned = new_entry->index; |
818 | 1.07k | return 0; |
819 | 1.07k | } |
820 | | |
821 | | static int pdfi_free_name_table(pdf_context *ctx) |
822 | 94.6k | { |
823 | 94.6k | if (ctx->name_table) { |
824 | 1.07k | pdfi_name_entry_t *next = NULL, *e = (pdfi_name_entry_t *)ctx->name_table; |
825 | | |
826 | 2.15k | while (e != NULL) { |
827 | 1.07k | next = (pdfi_name_entry_t *)e->next; |
828 | 1.07k | gs_free_object(ctx->memory, e->name, "free name table entries"); |
829 | 1.07k | gs_free_object(ctx->memory, e, "free name table entries"); |
830 | 1.07k | e = next; |
831 | 1.07k | } |
832 | 1.07k | } |
833 | 94.6k | ctx->name_table = NULL; |
834 | 94.6k | return 0; |
835 | 94.6k | } |
836 | | |
837 | | int pdfi_name_from_index(pdf_context *ctx, int index, unsigned char **name, unsigned int *len) |
838 | 0 | { |
839 | 0 | pdfi_name_entry_t *e = (pdfi_name_entry_t *)ctx->name_table; |
840 | |
|
841 | 0 | while (e != NULL) { |
842 | 0 | if (e->index == index) { |
843 | 0 | *name = (unsigned char *)e->name; |
844 | 0 | *len = e->len; |
845 | 0 | return 0; |
846 | 0 | } |
847 | 0 | e = e->next; |
848 | 0 | } |
849 | | |
850 | 0 | return_error(gs_error_undefined); |
851 | 0 | } |
852 | | |
853 | | int pdfi_separation_name_from_index(gs_gstate *pgs, gs_separation_name index, unsigned char **name, unsigned int *len) |
854 | 5.38k | { |
855 | 5.38k | pdfi_int_gstate *igs = (pdfi_int_gstate *)pgs->client_data; |
856 | 5.38k | pdf_context *ctx = NULL; |
857 | 5.38k | pdfi_name_entry_t *e = NULL; |
858 | | |
859 | 5.38k | if (igs == NULL) |
860 | 0 | return_error(gs_error_undefined); |
861 | | |
862 | 5.38k | ctx = igs->ctx; |
863 | 5.38k | if (ctx == NULL) |
864 | 0 | return_error(gs_error_undefined); |
865 | | |
866 | 5.38k | e = (pdfi_name_entry_t *)ctx->name_table; |
867 | | |
868 | 5.38k | while (e != NULL) { |
869 | 5.38k | if (e->index == index) { |
870 | 5.38k | *name = (unsigned char *)e->name; |
871 | 5.38k | *len = e->len; |
872 | 5.38k | return 0; |
873 | 5.38k | } |
874 | 0 | e = e->next; |
875 | 0 | } |
876 | | |
877 | 5.38k | return_error(gs_error_undefined); |
878 | 5.38k | } |
879 | | |
880 | | int pdfi_finish_pdf_file(pdf_context *ctx) |
881 | 74.1k | { |
882 | 74.1k | if (ctx->Root) { |
883 | 74.1k | if (ctx->device_state.writepdfmarks && ctx->device_state.WantsOptionalContent) { |
884 | 3.33k | pdf_obj *o = NULL; |
885 | 3.33k | int code = 0; |
886 | | |
887 | 3.33k | code = pdfi_dict_knownget_type(ctx, ctx->Root, "OCProperties", PDF_DICT, &o); |
888 | 3.33k | if (code > 0) { |
889 | | /* Build and send the OCProperties structure */ |
890 | 94 | code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "OCProperties"); |
891 | 94 | pdfi_countdown(o); |
892 | 94 | if (code < 0) |
893 | | /* Error message ? */ |
894 | 34 | ; |
895 | 94 | } |
896 | 3.33k | } |
897 | 74.1k | } |
898 | 74.1k | return 0; |
899 | 74.1k | } |
900 | | |
901 | | int pdfi_close_pdf_file(pdf_context *ctx) |
902 | 0 | { |
903 | 0 | if (ctx->Root) { |
904 | 0 | if (ctx->device_state.writepdfmarks && ctx->device_state.WantsOptionalContent) { |
905 | 0 | pdf_obj *o = NULL; |
906 | 0 | int code = 0; |
907 | |
|
908 | 0 | code = pdfi_dict_knownget(ctx, ctx->Root, "OCProperties", &o); |
909 | 0 | if (code > 0) { |
910 | | /* Build and send the OCProperties structure */ |
911 | 0 | code = pdfi_pdfmark_from_objarray(ctx, &o, 1, NULL, "OCProperties"); |
912 | 0 | pdfi_countdown(o); |
913 | 0 | if (code < 0) |
914 | | /* Error message ? */ |
915 | 0 | ; |
916 | 0 | } |
917 | 0 | } |
918 | 0 | } |
919 | |
|
920 | 0 | if (ctx->main_stream) { |
921 | 0 | if (ctx->main_stream->s) { |
922 | 0 | sfclose(ctx->main_stream->s); |
923 | 0 | } |
924 | 0 | gs_free_object(ctx->memory, ctx->main_stream, "Closing main PDF file"); |
925 | 0 | ctx->main_stream = NULL; |
926 | 0 | } |
927 | 0 | ctx->main_stream_length = 0; |
928 | |
|
929 | 0 | if (ctx->filename) { |
930 | 0 | gs_free_object(ctx->memory, ctx->filename, "pdfi_close_pdf_file, free copy of filename"); |
931 | 0 | ctx->filename = NULL; |
932 | 0 | } |
933 | |
|
934 | 0 | pdfi_clear_context(ctx); |
935 | 0 | return 0; |
936 | 0 | } |
937 | | |
938 | | static int pdfi_process(pdf_context *ctx) |
939 | 0 | { |
940 | 0 | int code = 0, i; |
941 | | |
942 | | /* Loop over each page and either render it or output the |
943 | | * required information. |
944 | | */ |
945 | 0 | for (i=0;i < ctx->num_pages;i++) { |
946 | 0 | if (ctx->args.first_page != 0) { |
947 | 0 | if (i < ctx->args.first_page - 1) |
948 | 0 | continue; |
949 | 0 | } |
950 | 0 | if (ctx->args.last_page != 0) { |
951 | 0 | if (i > ctx->args.last_page - 1) |
952 | 0 | break; |
953 | 0 | } |
954 | 0 | if (ctx->args.pdfinfo) |
955 | 0 | code = pdfi_output_page_info(ctx, i); |
956 | 0 | else |
957 | 0 | code = pdfi_page_render(ctx, i, true); |
958 | |
|
959 | 0 | if (code < 0) |
960 | 0 | code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_process", NULL); |
961 | 0 | } |
962 | 0 | pdfi_report_errors(ctx); |
963 | |
|
964 | 0 | return code; |
965 | 0 | } |
966 | | |
967 | | /* This works by reading each embedded file referenced by the collection. If it |
968 | | * has a MIME type indicating it's a PDF file, or somewhere in the first 2KB it |
969 | | * has the PDF header (%PDF-) then we treat it as a PDF file. We read the contents |
970 | | * of the refrenced stream and write them to disk in a scratch file. |
971 | | * |
972 | | * We then process each scratch file in turn. Note that we actually return an |
973 | | * array of strings; the first string is the temporary filename, the second is |
974 | | * the entry from the names tree. Since this can be in UTF16-BE format, it can |
975 | | * contain embedded single byte NULL characters, so we can't use a regular C |
976 | | * string. Instead we use a triple byte NULL termination. |
977 | | * |
978 | | * It ought to be possible to do all the processing without creating scratch files, by saving the |
979 | | * current file state, and opening a new 'file' on the stream in the original PDF |
980 | | * file. But I couldn't immediately get that to work. |
981 | | * So this is a FIXME future enhancement. |
982 | | */ |
983 | | int pdfi_prep_collection(pdf_context *ctx, uint64_t *TotalFiles, char ***names_array) |
984 | 0 | { |
985 | 0 | int code = 0, i, NumEmbeddedFiles = 0; |
986 | 0 | pdf_obj *Names = NULL, *EmbeddedFiles = NULL; |
987 | 0 | pdf_array *FileNames = NULL; |
988 | 0 | pdf_obj *EF = NULL, *F = NULL; |
989 | 0 | char **working_array = NULL; |
990 | |
|
991 | 0 | if (pdfi_dict_knownget_type(ctx, ctx->Root, "Names", PDF_DICT, &Names) > 0) { |
992 | 0 | if(pdfi_dict_knownget_type(ctx, (pdf_dict *)Names, "EmbeddedFiles", PDF_DICT, &EmbeddedFiles) > 0) { |
993 | 0 | if (pdfi_dict_knownget_type(ctx, (pdf_dict *)EmbeddedFiles, "Names", PDF_ARRAY, (pdf_obj **)&FileNames) > 0) { |
994 | 0 | int ix = 0, index = 0; |
995 | 0 | gp_file *scratch_file = NULL; |
996 | 0 | char scratch_name[gp_file_name_sizeof]; |
997 | |
|
998 | 0 | NumEmbeddedFiles = pdfi_array_size(FileNames) / 2; |
999 | |
|
1000 | 0 | working_array = (char **)gs_alloc_bytes(ctx->memory, NumEmbeddedFiles * 2 * sizeof(char *), "Collection file working names array"); |
1001 | 0 | if (working_array == NULL) { |
1002 | 0 | code = gs_note_error(gs_error_VMerror); |
1003 | 0 | goto exit; |
1004 | 0 | } |
1005 | 0 | memset(working_array, 0x00, NumEmbeddedFiles * 2 * sizeof(char *)); |
1006 | |
|
1007 | 0 | for (ix = 0;ix < NumEmbeddedFiles;ix++) { |
1008 | 0 | pdf_obj *File = NULL; |
1009 | 0 | pdf_obj *Subtype = NULL; |
1010 | |
|
1011 | 0 | code = pdfi_array_get(ctx, FileNames, (ix * 2) + 1, &File); |
1012 | 0 | if (code < 0) |
1013 | 0 | goto exit; |
1014 | | |
1015 | 0 | if (pdfi_type_of(File) == PDF_DICT) { |
1016 | 0 | if (pdfi_dict_knownget_type(ctx, (pdf_dict *)File, "EF", PDF_DICT, &EF) > 0) { |
1017 | 0 | if (pdfi_dict_knownget_type(ctx, (pdf_dict *)EF, "F", PDF_STREAM, &F) > 0) { |
1018 | 0 | pdf_dict *stream_dict = NULL; |
1019 | 0 | pdf_c_stream *s = NULL; |
1020 | | |
1021 | | /* pdfi_dict_from_object does not increment the reference count of the stream dictionary |
1022 | | * so we do not need to count it down later. |
1023 | | */ |
1024 | 0 | code = pdfi_dict_from_obj(ctx, F, &stream_dict); |
1025 | 0 | if (code >= 0) { |
1026 | 0 | if (pdfi_dict_knownget_type(ctx, stream_dict, "Subtype", PDF_NAME, &Subtype) <= 0) { |
1027 | | /* No Subtype, (or not a name) we can't check the Mime type, so try to read the first 2Kb |
1028 | | * and look for a %PDF- in that. If not present, assume its not a PDF |
1029 | | */ |
1030 | 0 | code = pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, (pdf_stream *)F), SEEK_SET); |
1031 | 0 | if (code >= 0) { |
1032 | 0 | code = pdfi_filter(ctx, (pdf_stream *)F, ctx->main_stream, &s, false); |
1033 | 0 | if (code >= 0) { |
1034 | 0 | char Buffer[2048]; |
1035 | 0 | int bytes; |
1036 | |
|
1037 | 0 | bytes = pdfi_read_bytes(ctx, (byte *)Buffer, 1, 2048, s); |
1038 | 0 | pdfi_close_file(ctx, s); |
1039 | 0 | s = NULL; |
1040 | | /* Assertion; the smallest real PDF file is at least 400 bytes */ |
1041 | 0 | if (bytes >= 400) { |
1042 | 0 | if (strstr(Buffer, "%PDF-") == NULL) |
1043 | 0 | code = -1; |
1044 | 0 | } else |
1045 | 0 | code = -1; |
1046 | 0 | } |
1047 | 0 | } |
1048 | 0 | } else { |
1049 | 0 | if (!pdfi_name_is((const pdf_name *)Subtype, "application/pdf")) |
1050 | 0 | code = -1; |
1051 | 0 | } |
1052 | |
|
1053 | 0 | if (code >= 0) { |
1054 | | /* Appears to be a PDF file. Create a scratch file to hold it, and then |
1055 | | * read the file from the PDF, and write it to the scratch file. Record |
1056 | | * the scratch filename in the working_array for later processing. |
1057 | | */ |
1058 | 0 | scratch_file = gp_open_scratch_file(ctx->memory, "gpdf-collection-", scratch_name, "wb"); |
1059 | 0 | if (scratch_file != NULL) { |
1060 | 0 | code = pdfi_seek(ctx, ctx->main_stream, pdfi_stream_offset(ctx, (pdf_stream *)F), SEEK_SET); |
1061 | 0 | if (code >= 0) { |
1062 | 0 | double L; |
1063 | 0 | pdf_c_stream *SubFile_stream = NULL; |
1064 | | |
1065 | | /* Start by setting up the file to be read. Apply a SubFileDecode so that, if the input stream |
1066 | | * is not compressed we will stop reading when we get to the end of the stream. |
1067 | | */ |
1068 | 0 | if (pdfi_dict_knownget_number(ctx, stream_dict, "Length", &L) > 0) { |
1069 | |
|
1070 | 0 | code = pdfi_apply_SubFileDecode_filter(ctx, (int)L, NULL, ctx->main_stream, &SubFile_stream, false); |
1071 | 0 | if (code >= 0) |
1072 | 0 | code = pdfi_filter(ctx, (pdf_stream *)F, SubFile_stream, &s, false); |
1073 | 0 | } else |
1074 | 0 | code = pdfi_filter(ctx, (pdf_stream *)F, ctx->main_stream, &s, false); |
1075 | |
|
1076 | 0 | if (code >= 0) { |
1077 | 0 | char Buffer[2048]; |
1078 | 0 | int bytes; |
1079 | 0 | pdf_string *Name = NULL; |
1080 | | |
1081 | | /* Read the stream contents and write them to the scratch file */ |
1082 | 0 | do { |
1083 | 0 | bytes = pdfi_read_bytes(ctx, (byte *)Buffer, 1, 2048, s); |
1084 | 0 | (void)gp_fwrite(Buffer, 1, bytes, scratch_file); |
1085 | 0 | } while (bytes > 0); |
1086 | | |
1087 | | /* Create an entry for the Description in the names array */ |
1088 | 0 | code = pdfi_array_get(ctx, FileNames, ix * 2, (pdf_obj **)&Name); |
1089 | 0 | if (code >= 0) { |
1090 | 0 | if (pdfi_type_of((pdf_obj *)Name) == PDF_STRING) { |
1091 | 0 | working_array[(index * 2) + 1] = (char *)gs_alloc_bytes(ctx->memory, Name->length + 3, "Collection file names array entry"); |
1092 | 0 | if (working_array[(index * 2) + 1] != NULL) { |
1093 | 0 | memset(working_array[(index * 2) + 1], 0x00, Name->length + 3); |
1094 | 0 | memcpy(working_array[(index * 2) + 1], Name->data, Name->length); |
1095 | 0 | } |
1096 | 0 | } |
1097 | 0 | pdfi_countdown(Name); |
1098 | 0 | Name = NULL; |
1099 | 0 | } |
1100 | | |
1101 | | /* And now the scratch file name */ |
1102 | 0 | working_array[index * 2] = (char *)gs_alloc_bytes(ctx->memory, strlen(scratch_name) + 3, "Collection file names array entry"); |
1103 | 0 | if (working_array[index * 2] != NULL) { |
1104 | 0 | memset(working_array[index * 2], 0x00, strlen(scratch_name) + 3); |
1105 | 0 | strcpy(working_array[index * 2], scratch_name); |
1106 | 0 | } |
1107 | |
|
1108 | 0 | index++; |
1109 | 0 | (*TotalFiles)++; |
1110 | 0 | pdfi_close_file(ctx, s); |
1111 | 0 | s = NULL; |
1112 | 0 | } |
1113 | 0 | if (SubFile_stream != NULL) |
1114 | 0 | pdfi_close_file(ctx, SubFile_stream); |
1115 | 0 | SubFile_stream = NULL; |
1116 | 0 | } |
1117 | 0 | gp_fclose(scratch_file); |
1118 | 0 | } else |
1119 | 0 | errprintf(ctx->memory, "\n **** Warning: Failed to open a scratch file.\n"); |
1120 | 0 | } |
1121 | 0 | } |
1122 | 0 | } |
1123 | 0 | } |
1124 | 0 | } |
1125 | 0 | pdfi_countdown(Subtype); |
1126 | 0 | Subtype = NULL; |
1127 | 0 | pdfi_countdown(F); |
1128 | 0 | F = NULL; |
1129 | 0 | pdfi_countdown(EF); |
1130 | 0 | EF = NULL; |
1131 | 0 | pdfi_countdown(File); |
1132 | 0 | File = NULL; |
1133 | 0 | } |
1134 | 0 | } else { |
1135 | 0 | errprintf(ctx->memory, "\n **** Warning: Failed to read EmbeededFiles Names tree.\n"); |
1136 | 0 | } |
1137 | 0 | } else { |
1138 | 0 | errprintf(ctx->memory, "\n **** Warning: Failed to read EmbeddedFiles.\n"); |
1139 | 0 | } |
1140 | 0 | } else { |
1141 | 0 | errprintf(ctx->memory, "\n **** Warning: Failed to find Names tree.\n"); |
1142 | 0 | } |
1143 | 0 | code = 0; |
1144 | |
|
1145 | 0 | exit: |
1146 | 0 | if (code >= 0) { |
1147 | 0 | uint64_t ix = 0; |
1148 | |
|
1149 | 0 | (*names_array) = (char **)gs_alloc_bytes(ctx->memory, *TotalFiles * 2 * sizeof(char *), "Collection file namesarray"); |
1150 | 0 | if (*names_array == NULL) |
1151 | 0 | code = gs_note_error(gs_error_VMerror); |
1152 | 0 | else { |
1153 | 0 | for (i = 0; i < NumEmbeddedFiles;i++) { |
1154 | 0 | if (working_array[i * 2] != NULL && working_array[(i * 2) + 1] != NULL) { |
1155 | 0 | (*names_array)[ix * 2] = working_array[i * 2]; |
1156 | 0 | working_array[i * 2] = NULL; |
1157 | 0 | (*names_array)[(ix * 2) + 1] = working_array[(i * 2) + 1]; |
1158 | 0 | working_array[(i * 2) + 1] = NULL; |
1159 | 0 | ix++; |
1160 | 0 | } |
1161 | 0 | } |
1162 | 0 | } |
1163 | 0 | } |
1164 | |
|
1165 | 0 | if (working_array != NULL) { |
1166 | 0 | for (i = 0; i < NumEmbeddedFiles;i++) |
1167 | 0 | gs_free_object(ctx->memory, working_array[i], "free collection temporary filenames"); |
1168 | 0 | gs_free_object(ctx->memory, working_array, "free collection working array"); |
1169 | 0 | } |
1170 | 0 | pdfi_countdown(F); |
1171 | 0 | pdfi_countdown(EF); |
1172 | 0 | pdfi_countdown(FileNames); |
1173 | 0 | pdfi_countdown(EmbeddedFiles); |
1174 | 0 | pdfi_countdown(Names); |
1175 | 0 | return code; |
1176 | 0 | } |
1177 | | |
1178 | | static int pdfi_process_collection(pdf_context *ctx) |
1179 | 0 | { |
1180 | 0 | int code, i; |
1181 | 0 | uint64_t TotalFiles = 0, ix = 0; |
1182 | 0 | char **names_array = NULL; |
1183 | |
|
1184 | 0 | code = pdfi_prep_collection(ctx, &TotalFiles, &names_array); |
1185 | 0 | if (code >= 0 && TotalFiles > 0) |
1186 | 0 | { |
1187 | | /* names_array is full of pointers to the scratch file names containing PDF files. |
1188 | | * Now we need to run each PDF file. First we close down the current file. |
1189 | | */ |
1190 | 0 | (void)pdfi_close_pdf_file(ctx); |
1191 | |
|
1192 | 0 | for (ix = 0;ix < TotalFiles * 2;ix+=2) { |
1193 | 0 | if (names_array[ix] != NULL) { |
1194 | 0 | (void)pdfi_process_pdf_file(ctx, names_array[ix]); |
1195 | 0 | (void)pdfi_close_pdf_file(ctx); |
1196 | 0 | } |
1197 | 0 | } |
1198 | 0 | } else |
1199 | | /* We didn't find any PDF files in the Embedded Files. So just run the |
1200 | | * pages in the container file (the original PDF file) |
1201 | | */ |
1202 | 0 | pdfi_process(ctx); |
1203 | |
|
1204 | 0 | for (i = 0; i < TotalFiles * 2;i++) |
1205 | 0 | gs_free_object(ctx->memory, names_array[i], "free collection temporary filenames"); |
1206 | 0 | gs_free_object(ctx->memory, names_array, "free collection names array"); |
1207 | |
|
1208 | 0 | return 0; |
1209 | 0 | } |
1210 | | |
1211 | | int pdfi_process_pdf_file(pdf_context *ctx, char *filename) |
1212 | 0 | { |
1213 | 0 | int code = 0; |
1214 | |
|
1215 | 0 | code = pdfi_open_pdf_file(ctx, filename); |
1216 | 0 | if (code < 0) { |
1217 | 0 | pdfi_report_errors(ctx); |
1218 | 0 | return code; |
1219 | 0 | } |
1220 | | |
1221 | | /* Do any custom device configuration */ |
1222 | 0 | pdfi_device_misc_config(ctx); |
1223 | |
|
1224 | 0 | if (ctx->Collection != NULL) |
1225 | 0 | code = pdfi_process_collection(ctx); |
1226 | 0 | else |
1227 | 0 | code = pdfi_process(ctx); |
1228 | | |
1229 | | /* Pdfmark_InitialPage is the offset for Page Dests in |
1230 | | * /Outlines and Link annotations. It is the count of pages |
1231 | | * processed so far. Update it by the number of pages in |
1232 | | * this file. |
1233 | | */ |
1234 | 0 | ctx->Pdfmark_InitialPage += ctx->num_pages; |
1235 | |
|
1236 | 0 | pdfi_close_pdf_file(ctx); |
1237 | 0 | return code; |
1238 | 0 | } |
1239 | | |
1240 | | static int pdfi_init_file(pdf_context *ctx) |
1241 | 94.5k | { |
1242 | 94.5k | int code = 0; |
1243 | 94.5k | pdf_obj *o = NULL; |
1244 | | |
1245 | 94.5k | code = pdfi_read_xref(ctx); |
1246 | 94.5k | if (code < 0) { |
1247 | 8.48k | if (ctx->is_hybrid) { |
1248 | 5 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BADXREFSTREAM, "pdfi_init_file", NULL)) < 0) { |
1249 | 0 | goto exit; |
1250 | 0 | } |
1251 | | /* If its a hybrid file, and we failed to read the XrefStm, try |
1252 | | * again, but this time read the xref table instead. |
1253 | | */ |
1254 | 5 | pdfi_countdown(ctx->xref_table); |
1255 | 5 | ctx->xref_table = NULL; |
1256 | 5 | ctx->prefer_xrefstm = false; |
1257 | 5 | code = pdfi_read_xref(ctx); |
1258 | 5 | if (code < 0) |
1259 | 0 | goto exit; |
1260 | 8.47k | } else { |
1261 | 8.47k | pdfi_set_error(ctx, code, NULL, E_PDF_BADXREFSTREAM, "pdfi_init_file", NULL); |
1262 | 8.47k | goto exit; |
1263 | 8.47k | } |
1264 | 8.48k | } |
1265 | | |
1266 | 86.0k | pdfi_device_set_flags(ctx); |
1267 | | |
1268 | 86.0k | if (ctx->Trailer) { |
1269 | | /* See comment in pdfi_read_Root() (pdf_doc.c) for details */ |
1270 | 37.1k | pdf_dict *d = ctx->Trailer; |
1271 | | |
1272 | 37.1k | pdfi_countup(d); |
1273 | 37.1k | code = pdfi_dict_get(ctx, d, "Encrypt", &o); |
1274 | 37.1k | pdfi_countdown(d); |
1275 | 37.1k | if (code < 0 && code != gs_error_undefined) |
1276 | 43 | goto exit; |
1277 | 37.1k | if (code == 0) { |
1278 | 1.21k | if (pdfi_type_of(o) == PDF_DICT) { |
1279 | | #if USE_PDF_PERMISSIONS |
1280 | | double Permissions = 0; |
1281 | | uint32_t P = 0; |
1282 | | #endif |
1283 | | |
1284 | 1.19k | code = pdfi_initialise_Decryption(ctx); |
1285 | 1.19k | if (code < 0) |
1286 | 258 | goto exit; |
1287 | | |
1288 | | /* This section is commetned out but deliberately retained. This code |
1289 | | * prevents us from processing any PDF file where the 'print' permission |
1290 | | * is not set. Additionally, if the 'print' permission is set, but bit 12 |
1291 | | * 'faitful digital copy' is not set, *and* we are writing to a high level |
1292 | | * device, then we will throw an error.. |
1293 | | */ |
1294 | | #if USE_PDF_PERMISSIONS |
1295 | | code = pdfi_dict_knownget_number(ctx, (pdf_dict *)o, "P", &Permissions); |
1296 | | if (code < 0) |
1297 | | goto exit; |
1298 | | if (Permissions > ((unsigned long)1 << 31)) { |
1299 | | code = gs_note_error(gs_error_rangecheck); |
1300 | | goto exit; |
1301 | | } |
1302 | | P = (uint32_t)Permissions; |
1303 | | if ((P & 0x04) == 0) { |
1304 | | errprintf(ctx->memory, " ****The owner of this file has requested you do not print it.\n"); |
1305 | | code = gs_note_error(gs_error_invalidfileaccess); |
1306 | | goto exit; |
1307 | | } |
1308 | | if (ctx->device_state.HighLevelDevice) { |
1309 | | if ((P & 0x800) == 0) { |
1310 | | errprintf(ctx->memory, " ****The owner of this file has requested you do not make a digital copy of it.\n"); |
1311 | | code = gs_note_error(gs_error_invalidfileaccess); |
1312 | | goto exit; |
1313 | | } |
1314 | | } |
1315 | | #endif |
1316 | 1.19k | } else { |
1317 | 16 | if (pdfi_type_of(o) != PDF_NULL) |
1318 | 10 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BADENCRYPT, "pdfi_init_file", NULL)) < 0) |
1319 | 0 | goto exit; |
1320 | 16 | } |
1321 | 1.21k | } |
1322 | 37.1k | } |
1323 | | |
1324 | 85.9k | read_root: |
1325 | 85.9k | if (ctx->Trailer) { |
1326 | 37.0k | code = pdfi_read_Root(ctx); |
1327 | 37.0k | if (code < 0) { |
1328 | | /* If we couldn#'t find the Root object, and we were using the XrefStm |
1329 | | * from a hybrid file, then try again, but this time use the xref table |
1330 | | */ |
1331 | 1.08k | if (code == gs_error_undefined && ctx->is_hybrid && ctx->prefer_xrefstm) { |
1332 | 0 | pdfi_countdown(ctx->xref_table); |
1333 | 0 | ctx->xref_table = NULL; |
1334 | 0 | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_BADXREFSTREAM, "pdfi_init_file", NULL)) < 0) |
1335 | 0 | goto exit; |
1336 | | |
1337 | 0 | ctx->prefer_xrefstm = false; |
1338 | 0 | code = pdfi_read_xref(ctx); |
1339 | 0 | if (code < 0) { |
1340 | 0 | pdfi_set_error(ctx, 0, NULL, E_PDF_BADXREF, "pdfi_init_file", NULL); |
1341 | 0 | goto exit; |
1342 | 0 | } |
1343 | 0 | code = pdfi_read_Root(ctx); |
1344 | 0 | if (code < 0) |
1345 | 0 | goto exit; |
1346 | 1.08k | } else { |
1347 | 1.08k | int code1 = pdfi_repair_file(ctx); |
1348 | 1.08k | if (code1 < 0) |
1349 | 880 | goto exit; |
1350 | 207 | goto read_root; |
1351 | 1.08k | } |
1352 | 1.08k | } |
1353 | 37.0k | } |
1354 | | |
1355 | 84.8k | if (ctx->Trailer) { |
1356 | 35.9k | code = pdfi_read_Info(ctx); |
1357 | 35.9k | if (code < 0 && code != gs_error_undefined) { |
1358 | 1.12k | if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_init_file", NULL)) < 0) |
1359 | 0 | goto exit; |
1360 | 1.12k | pdfi_clearstack(ctx); |
1361 | 1.12k | } |
1362 | 35.9k | } |
1363 | | |
1364 | 84.8k | if (!ctx->Root) { |
1365 | 7.54k | errprintf(ctx->memory, "Catalog dictionary not located in file, unable to proceed\n"); |
1366 | 7.54k | return_error(gs_error_syntaxerror); |
1367 | 7.54k | } |
1368 | | |
1369 | 77.3k | code = pdfi_read_Pages(ctx); |
1370 | 77.3k | if (code < 0) |
1371 | 2.77k | goto exit; |
1372 | | |
1373 | 74.5k | code = pdfi_doc_page_array_init(ctx); |
1374 | 74.5k | if (code < 0) |
1375 | 2 | goto exit; |
1376 | | |
1377 | 74.5k | if (ctx->num_pages == 0) |
1378 | 193 | errprintf(ctx->memory, "\n **** Warning: PDF document has no pages.\n"); |
1379 | | |
1380 | 74.5k | code = pdfi_doc_trailer(ctx); |
1381 | 74.5k | if (code < 0) |
1382 | 0 | goto exit; |
1383 | | |
1384 | 74.5k | pdfi_read_OptionalRoot(ctx); |
1385 | | |
1386 | 74.5k | if (ctx->args.pdfinfo) { |
1387 | 0 | code = pdfi_output_metadata(ctx); |
1388 | 0 | if (code < 0 && (code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_init_file", NULL)) < 0) |
1389 | 0 | goto exit; |
1390 | 0 | } |
1391 | | |
1392 | 86.9k | exit: |
1393 | 86.9k | if (code < 0) |
1394 | 12.4k | pdfi_set_error(ctx, code, NULL, E_PDF_GS_LIB_ERROR, "pdfi_init_file", NULL); |
1395 | 86.9k | pdfi_countdown(o); |
1396 | 86.9k | return code; |
1397 | 74.5k | } |
1398 | | |
1399 | | int pdfi_set_input_stream(pdf_context *ctx, stream *stm) |
1400 | 94.5k | { |
1401 | 94.5k | byte *Buffer = NULL; |
1402 | 94.5k | char *s = NULL, *test = NULL; |
1403 | 94.5k | float version = 0.0; |
1404 | 94.5k | gs_offset_t Offset = 0; |
1405 | 94.5k | int64_t bytes = 0, leftover = 0, bytes_left = 0; |
1406 | 94.5k | bool found = false; |
1407 | 94.5k | int code; |
1408 | | |
1409 | | /* In case of broken PDF files, the repair could run off the end of the |
1410 | | * file, so make sure that doing so does *not* automagically close the file |
1411 | | */ |
1412 | 94.5k | stm->close_at_eod = false; |
1413 | | |
1414 | 94.5k | ctx->main_stream = (pdf_c_stream *)gs_alloc_bytes(ctx->memory, sizeof(pdf_c_stream), "PDF interpreter allocate main PDF stream"); |
1415 | 94.5k | if (ctx->main_stream == NULL) |
1416 | 0 | return_error(gs_error_VMerror); |
1417 | 94.5k | memset(ctx->main_stream, 0x00, sizeof(pdf_c_stream)); |
1418 | 94.5k | ctx->main_stream->s = stm; |
1419 | | |
1420 | 94.5k | Buffer = gs_alloc_bytes(ctx->memory, BUF_SIZE, "PDF interpreter - allocate working buffer for file validation"); |
1421 | 94.5k | if (Buffer == NULL) { |
1422 | 0 | code = gs_error_VMerror; |
1423 | 0 | goto error; |
1424 | 0 | } |
1425 | | |
1426 | | /* Determine file size */ |
1427 | 94.5k | pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET); |
1428 | 94.5k | pdfi_seek(ctx, ctx->main_stream, 0, SEEK_END); |
1429 | 94.5k | ctx->main_stream_length = pdfi_tell(ctx->main_stream); |
1430 | 94.5k | Offset = BUF_SIZE; |
1431 | 94.5k | bytes = BUF_SIZE; |
1432 | 94.5k | pdfi_seek(ctx, ctx->main_stream, 0, SEEK_SET); |
1433 | | |
1434 | 94.5k | bytes = Offset = min(BUF_SIZE - 1, ctx->main_stream_length); |
1435 | | |
1436 | 94.5k | if (ctx->args.pdfdebug) |
1437 | 0 | outprintf(ctx->memory, "%% Reading header\n"); |
1438 | | |
1439 | 94.5k | bytes = pdfi_read_bytes(ctx, Buffer, 1, Offset, ctx->main_stream); |
1440 | 94.5k | if (bytes <= 0) { |
1441 | 6 | errprintf(ctx->memory, "Failed to read any bytes from input stream\n"); |
1442 | 6 | code = gs_error_ioerror; |
1443 | 6 | goto error; |
1444 | 6 | } |
1445 | 94.5k | if (bytes < 8) { |
1446 | 60 | errprintf(ctx->memory, "Failed to read enough bytes for a valid PDF header from input stream\n"); |
1447 | 60 | code = gs_error_ioerror; |
1448 | 60 | goto error; |
1449 | 60 | } |
1450 | 94.5k | Buffer[Offset] = 0x00; |
1451 | | |
1452 | | /* First check for existence of header */ |
1453 | 94.5k | test = (char *)Buffer; |
1454 | 94.5k | bytes_left = bytes; |
1455 | 94.5k | s = strstr((char *)test, "%PDF-"); |
1456 | 94.5k | if (s == NULL) { |
1457 | 0 | if ((code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, W_PDF_GARBAGE_B4HDR, "pdfi_set_input_stream", "")) < 0) { |
1458 | 0 | goto error; |
1459 | 0 | } |
1460 | 0 | } |
1461 | | /* Garbage before the header can be anything, including binary and NULL (0x00) characters |
1462 | | * which can defeat using strstr, so if we fail to find a header, try moving on by the length |
1463 | | * of the C string + 1 and try again. |
1464 | | */ |
1465 | 94.5k | while (s == NULL) { |
1466 | 0 | bytes_left -= (strlen(test) + 1); |
1467 | 0 | if (bytes_left < 5) |
1468 | 0 | break; |
1469 | 0 | test += strlen(test) + 1; |
1470 | 0 | s = strstr((char *)test, "%PDF-"); |
1471 | 0 | }; |
1472 | 94.5k | if (s == NULL) { |
1473 | 0 | char extra_info[gp_file_name_sizeof]; |
1474 | |
|
1475 | 0 | if (ctx->filename) |
1476 | 0 | gs_snprintf(extra_info, sizeof(extra_info), "%% File %s does not appear to be a PDF file (no %%PDF in first 2Kb of file)\n", ctx->filename); |
1477 | 0 | else |
1478 | 0 | gs_snprintf(extra_info, sizeof(extra_info), "%% File does not appear to be a PDF stream (no %%PDF in first 2Kb of stream)\n"); |
1479 | |
|
1480 | 0 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_NOHEADER, "pdfi_set_input_stream", extra_info) ) < 0) |
1481 | 0 | goto error; |
1482 | 94.5k | } else { |
1483 | | /* Now extract header version (may be overridden later) */ |
1484 | 94.5k | if (sscanf(s + 5, "%f", &version) != 1) { |
1485 | 3.80k | ctx->HeaderVersion = 0; |
1486 | 3.80k | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_NOHEADERVERSION, "pdfi_set_input_stream", (char *)"%% Unable to read PDF version from header\n")) < 0) |
1487 | 0 | goto error; |
1488 | 3.80k | } |
1489 | 90.7k | else { |
1490 | 90.7k | ctx->HeaderVersion = version; |
1491 | 90.7k | s += 5; |
1492 | 382k | while (((*s >= '0' && *s <= '9') || *s == '.') && *s != 0x00) |
1493 | 291k | s++; |
1494 | 95.0k | while (*s != 0x00) { |
1495 | 94.6k | if (*s == 0x09 || *s== 0x0c || *s == 0x20) { |
1496 | 4.29k | s++; |
1497 | 4.29k | continue; |
1498 | 4.29k | } |
1499 | 90.3k | if (*s != 0x0A && *s != 0x0D) { |
1500 | 4.49k | if ((code = pdfi_set_warning_stop(ctx, 0, NULL, W_PDF_VERSION_NO_EOL, "pdfi_set_input_stream", (char *)"%% PDF version not immediately followed with EOL\n")) < 0) |
1501 | 0 | goto error; |
1502 | 4.49k | } |
1503 | 90.3k | break; |
1504 | 90.3k | } |
1505 | 90.7k | } |
1506 | 94.5k | if (ctx->args.pdfdebug) |
1507 | 0 | outprintf(ctx->memory, "%% Found header, PDF version is %f\n", ctx->HeaderVersion); |
1508 | 94.5k | } |
1509 | | |
1510 | | /* Jump to EOF and scan backwards looking for startxref */ |
1511 | 94.5k | pdfi_seek(ctx, ctx->main_stream, 0, SEEK_END); |
1512 | | |
1513 | 94.5k | if (ctx->args.pdfdebug) |
1514 | 0 | outprintf(ctx->memory, "%% Searching for 'startxref' keyword\n"); |
1515 | | |
1516 | | /* Initially read min(BUF_SIZE, file_length) bytes of data to the buffer */ |
1517 | 94.5k | bytes = Offset; |
1518 | | |
1519 | 813k | do { |
1520 | 813k | byte *last_lineend = NULL; |
1521 | 813k | uint32_t read; |
1522 | | |
1523 | 813k | if (pdfi_seek(ctx, ctx->main_stream, ctx->main_stream_length - Offset, SEEK_SET) != 0) { |
1524 | 9 | char msg[128]; |
1525 | 9 | gs_snprintf(msg, 128, "%% File is smaller than %"PRIi64" bytes\n", (int64_t)Offset); |
1526 | 9 | code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_ioerror), NULL, E_PDF_GS_LIB_ERROR, "pdfi_set_input_stream", msg); |
1527 | 9 | goto error; |
1528 | 9 | } |
1529 | 813k | read = pdfi_read_bytes(ctx, Buffer, 1, bytes, ctx->main_stream); |
1530 | | |
1531 | 813k | if (read <= 0) { |
1532 | 0 | char msg[128]; |
1533 | 0 | gs_snprintf(msg, 128, "Failed to read %"PRIi64" bytes from file\n", (int64_t)bytes); |
1534 | 0 | code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_ioerror), NULL, E_PDF_GS_LIB_ERROR, "pdfi_set_input_stream", msg); |
1535 | 0 | goto error; |
1536 | 0 | } |
1537 | | |
1538 | | /* When reading backwards, if we ran out of data in the last buffer while looking |
1539 | | * for a 'startxref, but we had found a linefeed, then we preserved everything |
1540 | | * from the beginning of the buffer up to that linefeed, by copying it to the end |
1541 | | * of the buffer and reducing the number of bytes to read so that it should have filled |
1542 | | * in the gap. If we didn't read enough bytes, then we have a gap between the end of |
1543 | | * the data we just read and the leftover data from teh last buffer. Move the preserved |
1544 | | * data down to meet the end of the data we just read. |
1545 | | */ |
1546 | 813k | if (bytes != read && leftover != 0) |
1547 | 0 | memcpy(Buffer + read, Buffer + bytes, leftover); |
1548 | | |
1549 | | /* As above, if we had any leftover data from the last buffer then increase the |
1550 | | * number of bytes available by that amount. We increase 'bytes' (the number of bytes |
1551 | | * to read) to the same value, which should mean we read an entire buffer's worth. Of |
1552 | | * course if we have any data left out of this buffer we'll reduce bytes again... |
1553 | | */ |
1554 | 813k | read = bytes = read + leftover; |
1555 | | |
1556 | | /* Now search backwards in the buffer for the startxref token */ |
1557 | 1.55G | while(read) { |
1558 | 1.55G | if (memcmp(Buffer + read - 9, "startxref", 9) == 0) { |
1559 | 44.3k | found = true; |
1560 | 44.3k | break; |
1561 | 1.55G | } else { |
1562 | 1.55G | if (Buffer[read - 1] == 0x0a || Buffer[read - 1] == 0x0d) |
1563 | 25.0M | last_lineend = Buffer + read; |
1564 | 1.55G | } |
1565 | 1.55G | read--; |
1566 | 1.55G | } |
1567 | 813k | if (found) { |
1568 | 44.3k | byte *b = Buffer + read; |
1569 | | |
1570 | | /* Success! stop now */ |
1571 | 44.3k | if(sscanf((char *)b, " %"PRIdOFFSET"", &ctx->startxref) != 1) { |
1572 | 687 | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_BADSTARTXREF, "pdfi_set_input_stream", "Unable to read offset of xref from PDF file\n")) < 0) |
1573 | 0 | goto error; |
1574 | 687 | } |
1575 | 44.3k | break; |
1576 | 769k | } else { |
1577 | | /* Our file read could conceivably have read back to the point where we read |
1578 | | * part of the 'startxref' token, but not all of it. So we want to preserve |
1579 | | * the data in the buffer, but not all of it obviously! The 'startxref' should be followed |
1580 | | * by a line ending, so above we keep a note of the last line ending. If we found one, then |
1581 | | * we preserve from the start of the buffer to that point. This could slow us up if the file |
1582 | | * Is broken, or has a load of junk after the EOF, because we could potentially be saving a |
1583 | | * lot of data on each pass, but that's only going to happen with bad files. |
1584 | | * Note we reduce the number of bytes to read so that it just fits into the buffer up to the |
1585 | | * beginning of the data we preserved. |
1586 | | */ |
1587 | 769k | if (last_lineend) { |
1588 | 708k | leftover = last_lineend - Buffer; |
1589 | | /* Ensure we don't try to copy more than half a buffer, because that will |
1590 | | * end up overrunning the buffer end. Since we are only doing this to |
1591 | | * ensure we don't drop a partial 'startxref' that's far more than enough. |
1592 | | */ |
1593 | 708k | if (leftover < BUF_SIZE / 2) { |
1594 | 693k | memmove(Buffer + bytes - leftover, last_lineend, leftover); |
1595 | 693k | bytes -= leftover; |
1596 | 693k | } else |
1597 | 14.7k | leftover = 0; |
1598 | 708k | } else |
1599 | 61.0k | leftover = 0; |
1600 | 769k | } |
1601 | | |
1602 | 769k | Offset += bytes; |
1603 | 769k | } while(Offset < ctx->main_stream_length); |
1604 | | |
1605 | 94.5k | if (!found) { |
1606 | 50.2k | if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_undefined), NULL, E_PDF_NOSTARTXREF, "pdfi_set_input_stream", NULL)) < 0) |
1607 | 0 | goto error; |
1608 | 50.2k | } |
1609 | | |
1610 | 94.5k | code = pdfi_init_file(ctx); |
1611 | | |
1612 | 94.5k | error: |
1613 | 94.5k | gs_free_object(ctx->memory, Buffer, "PDF interpreter - allocate working buffer for file validation"); |
1614 | 94.5k | return code; |
1615 | 94.5k | } |
1616 | | |
1617 | | int pdfi_open_pdf_file(pdf_context *ctx, char *filename) |
1618 | 0 | { |
1619 | 0 | stream *s = NULL; |
1620 | 0 | int code; |
1621 | |
|
1622 | 0 | if (ctx->args.pdfdebug) |
1623 | 0 | outprintf(ctx->memory, "%% Attempting to open %s as a PDF file\n", filename); |
1624 | |
|
1625 | 0 | ctx->filename = (char *)gs_alloc_bytes(ctx->memory, strlen(filename) + 1, "copy of filename"); |
1626 | 0 | if (ctx->filename == NULL) |
1627 | 0 | return_error(gs_error_VMerror); |
1628 | 0 | strcpy(ctx->filename, filename); |
1629 | |
|
1630 | 0 | s = sfopen(filename, "r", ctx->memory); |
1631 | 0 | if (s == NULL) { |
1632 | 0 | emprintf1(ctx->memory, "Failed to open file %s\n", filename); |
1633 | 0 | return_error(gs_error_ioerror); |
1634 | 0 | } |
1635 | 0 | code = pdfi_set_input_stream(ctx, s); |
1636 | 0 | return code; |
1637 | 0 | } |
1638 | | |
1639 | | static size_t pdfi_grdir_path_string_match(const byte *str, size_t sl0, byte *pat, size_t pl) |
1640 | 0 | { |
1641 | 0 | bool found = false; |
1642 | 0 | size_t sl = sl0; |
1643 | |
|
1644 | 0 | while (found == false) { |
1645 | 0 | if (pl > sl) |
1646 | 0 | break; |
1647 | 0 | if (*str == *pat && memcmp(str, pat, pl) == 0) |
1648 | 0 | found = true; |
1649 | 0 | else { |
1650 | 0 | str++; |
1651 | 0 | sl--; |
1652 | 0 | } |
1653 | 0 | } |
1654 | 0 | if (found) |
1655 | 0 | return (sl0 - sl) + pl; |
1656 | 0 | else |
1657 | 0 | return 0; |
1658 | 0 | } |
1659 | | |
1660 | | int pdfi_add_paths_to_search_paths(pdf_context *ctx, const char *ppath, int l, bool fontpath) |
1661 | 0 | { |
1662 | 0 | int i, slen, npaths = (l > 0) ? 1 : 0; |
1663 | 0 | const char *p = ppath; |
1664 | 0 | char *ps; |
1665 | 0 | const char *pe = p + l + 1; |
1666 | 0 | int code = 0; |
1667 | 0 | static const char *resstr = "Resource"; |
1668 | 0 | const int restrlen = strlen(resstr); |
1669 | 0 | const char *dirsepstr = gp_file_name_directory_separator(); |
1670 | 0 | const int dirsepstrlen = strlen(dirsepstr); |
1671 | 0 | char genresstr[64]; |
1672 | |
|
1673 | 0 | for (ps = (char *)p; ps < pe; ps++) { |
1674 | 0 | if (*ps == gp_file_name_list_separator) |
1675 | 0 | npaths++; |
1676 | 0 | } |
1677 | |
|
1678 | 0 | if (npaths > 0) { |
1679 | 0 | gs_param_string *pathstrings; |
1680 | 0 | int new_npaths = ctx->search_paths.num_resource_paths + npaths; |
1681 | |
|
1682 | 0 | if (fontpath != true) { |
1683 | 0 | pathstrings = (gs_param_string *)gs_alloc_bytes(ctx->memory, sizeof(gs_param_string) * new_npaths, "array of paths"); |
1684 | 0 | if (pathstrings == NULL) |
1685 | 0 | return_error(gs_error_VMerror); |
1686 | | |
1687 | 0 | memset(pathstrings, 0x00, sizeof(gs_param_string) * new_npaths); |
1688 | |
|
1689 | 0 | for (i = 1; i <= ctx->search_paths.num_init_resource_paths; i++) { |
1690 | 0 | pathstrings[new_npaths - i] = ctx->search_paths.resource_paths[ctx->search_paths.num_resource_paths - i]; |
1691 | 0 | } |
1692 | |
|
1693 | 0 | for (i = 0; i < ctx->search_paths.num_resource_paths - ctx->search_paths.num_init_resource_paths; i++) { |
1694 | 0 | pathstrings[i] = ctx->search_paths.resource_paths[i]; |
1695 | 0 | } |
1696 | | /* DO NOT CHANGE "i" BETWEEN HERE....... */ |
1697 | 0 | gs_free_object(ctx->memory, ctx->search_paths.resource_paths, "old array of paths"); |
1698 | 0 | ctx->search_paths.resource_paths = pathstrings; |
1699 | 0 | ctx->search_paths.num_resource_paths += npaths; |
1700 | | |
1701 | | /* .....AND HERE */ |
1702 | 0 | for (ps = (char *)p; ps < pe; ps++) { |
1703 | 0 | if (*ps == gp_file_name_list_separator || ps == pe - 1) { |
1704 | 0 | if (*p == gp_file_name_list_separator) p++; /* move past the separator */ |
1705 | 0 | slen = ps - p; |
1706 | 0 | pathstrings[i].data = (byte *)gs_alloc_bytes(ctx->memory, slen, "path string body"); |
1707 | |
|
1708 | 0 | if (pathstrings[i].data == NULL) { |
1709 | 0 | code = gs_note_error(gs_error_VMerror); |
1710 | 0 | break; |
1711 | 0 | } |
1712 | | |
1713 | 0 | memcpy((char *)pathstrings[i].data, p, slen); |
1714 | 0 | pathstrings[i].size = slen; |
1715 | 0 | pathstrings[i].persistent = false; |
1716 | 0 | i++; |
1717 | 0 | p = ps++; |
1718 | 0 | } |
1719 | 0 | } |
1720 | 0 | if ((restrlen + 2 * dirsepstrlen) < 64) { |
1721 | 0 | size_t grdlen; |
1722 | |
|
1723 | 0 | memcpy(genresstr, resstr, restrlen +1); /* +1 So we get the null terminator */ |
1724 | 0 | strncat(genresstr, dirsepstr, dirsepstrlen); |
1725 | |
|
1726 | 0 | for (i = 0; i < ctx->search_paths.num_resource_paths; i++) { |
1727 | 0 | if ((grdlen = pdfi_grdir_path_string_match(ctx->search_paths.resource_paths[i].data, ctx->search_paths.resource_paths[i].size, (byte *)genresstr, restrlen + dirsepstrlen)) > 0) { |
1728 | 0 | ctx->search_paths.genericresourcedir.data = ctx->search_paths.resource_paths[i].data; |
1729 | 0 | ctx->search_paths.genericresourcedir.size = grdlen; |
1730 | 0 | ctx->search_paths.genericresourcedir.persistent = true; |
1731 | 0 | break; |
1732 | 0 | } |
1733 | 0 | } |
1734 | 0 | } |
1735 | 0 | } |
1736 | 0 | else { |
1737 | 0 | p = ppath; |
1738 | 0 | pathstrings = (gs_param_string *)gs_alloc_bytes(ctx->memory, sizeof(gs_param_string) * (npaths + ctx->search_paths.num_font_paths), "array of font paths"); |
1739 | 0 | if (pathstrings == NULL) |
1740 | 0 | return_error(gs_error_VMerror); |
1741 | | |
1742 | 0 | memset(pathstrings, 0x00, sizeof(gs_param_string) * (npaths + ctx->search_paths.num_font_paths)); |
1743 | |
|
1744 | 0 | for (i = 0; i < ctx->search_paths.num_font_paths; i++) { |
1745 | 0 | pathstrings[ctx->search_paths.num_font_paths + i] = ctx->search_paths.font_paths[i]; |
1746 | 0 | } |
1747 | 0 | gs_free_object(ctx->memory, ctx->search_paths.font_paths, "old array of paths"); |
1748 | 0 | ctx->search_paths.font_paths = pathstrings; |
1749 | 0 | ctx->search_paths.num_font_paths += npaths; |
1750 | |
|
1751 | 0 | i = 0; |
1752 | 0 | for (ps = (char *)p; ps < pe; ps++) { |
1753 | 0 | if (*ps == gp_file_name_list_separator || ps == pe - 1) { |
1754 | 0 | slen = ps - p; |
1755 | 0 | pathstrings[i].data = (byte *)gs_alloc_bytes(ctx->memory, slen, "path string body"); |
1756 | |
|
1757 | 0 | if (pathstrings[i].data == NULL) { |
1758 | 0 | code = gs_note_error(gs_error_VMerror); |
1759 | 0 | break; |
1760 | 0 | } |
1761 | | |
1762 | 0 | memcpy((char *)pathstrings[i].data, p, slen); |
1763 | 0 | pathstrings[i].size = slen; |
1764 | 0 | pathstrings[i].persistent = false; |
1765 | 0 | i++; |
1766 | 0 | p = ps++; |
1767 | 0 | } |
1768 | 0 | } |
1769 | 0 | } |
1770 | 0 | } |
1771 | | |
1772 | 0 | return code; |
1773 | 0 | } |
1774 | | |
1775 | | int pdfi_add_initial_paths_to_search_paths(pdf_context *ctx, const char *ppath, int l) |
1776 | 0 | { |
1777 | 0 | int code; |
1778 | 0 | if (ctx->search_paths.num_resource_paths != 0) |
1779 | 0 | return_error(gs_error_invalidaccess); |
1780 | | |
1781 | 0 | code = pdfi_add_paths_to_search_paths(ctx, ppath, l, false); |
1782 | 0 | ctx->search_paths.num_init_resource_paths = ctx->search_paths.num_resource_paths; |
1783 | |
|
1784 | 0 | return code; |
1785 | 0 | } |
1786 | | |
1787 | | static void pdfi_free_search_paths(pdf_context *ctx) |
1788 | 94.6k | { |
1789 | 94.6k | int i; |
1790 | 1.32M | for (i = 0; i < ctx->search_paths.num_resource_paths; i++) { |
1791 | 1.22M | if (ctx->search_paths.resource_paths[i].persistent == false) |
1792 | 1.22M | gs_free_object(ctx->memory, (byte *)ctx->search_paths.resource_paths[i].data, "path string body"); |
1793 | 1.22M | } |
1794 | 94.6k | for (i = 0; i < ctx->search_paths.num_font_paths; i++) { |
1795 | 0 | if (ctx->search_paths.font_paths[i].persistent == false) |
1796 | 0 | gs_free_object(ctx->memory, (byte *)ctx->search_paths.font_paths[i].data, "path string body"); |
1797 | 0 | } |
1798 | 94.6k | gs_free_object(ctx->memory, (byte *)ctx->search_paths.resource_paths, "array of paths"); |
1799 | 94.6k | gs_free_object(ctx->memory, (byte *)ctx->search_paths.font_paths, "array of font paths"); |
1800 | | |
1801 | 94.6k | if (ctx->search_paths.genericresourcedir.persistent == false) |
1802 | 94.6k | gs_free_object(ctx->memory, (byte *)ctx->search_paths.genericresourcedir.data, "generic resource directory"); |
1803 | 94.6k | } |
1804 | | |
1805 | | static void pdfi_free_fontmapfiles(pdf_context *ctx) |
1806 | 94.6k | { |
1807 | 94.6k | int i; |
1808 | 94.6k | for (i = 0; i < ctx->num_fontmapfiles; i++) { |
1809 | 0 | gs_free_object(ctx->memory, ctx->fontmapfiles[i].data, "fontmapfiles string body"); |
1810 | 0 | } |
1811 | 94.6k | gs_free_object(ctx->memory, ctx->fontmapfiles, "fontmapfiles array"); |
1812 | 94.6k | } |
1813 | | |
1814 | | /* The fontmap file list doesn't extend, later settings in the command line override earlier ones |
1815 | | (Unlike the "-I" search paths above). |
1816 | | */ |
1817 | | int pdfi_add_fontmapfiles(pdf_context *ctx, const char *ppath, int l) |
1818 | 0 | { |
1819 | 0 | int i, nfilenames = (l > 0) ? 1 : 0; |
1820 | 0 | const char *p = ppath; |
1821 | 0 | char *ps; |
1822 | 0 | const char *pe = p + l + 1; |
1823 | 0 | int code = 0; |
1824 | |
|
1825 | 0 | pdfi_free_fontmapfiles(ctx); |
1826 | |
|
1827 | 0 | for (ps = (char *)p; ps < pe; ps++) { |
1828 | 0 | if (*ps == gp_file_name_list_separator) |
1829 | 0 | nfilenames++; |
1830 | 0 | } |
1831 | 0 | if (nfilenames > 0) { |
1832 | 0 | ctx->fontmapfiles = (gs_string *)gs_alloc_bytes(ctx->memory, sizeof(gs_string) * nfilenames, "array of fontmap files"); |
1833 | 0 | if (ctx->fontmapfiles == NULL) { |
1834 | 0 | return_error(gs_error_VMerror); |
1835 | 0 | } |
1836 | 0 | else { |
1837 | 0 | memset(ctx->fontmapfiles, 0x00, sizeof(gs_string) * nfilenames); |
1838 | 0 | ctx->num_fontmapfiles = nfilenames; |
1839 | |
|
1840 | 0 | for (i = 0; i < nfilenames; i++) { |
1841 | 0 | for (ps = (char *)p; ps < pe; ps++) { |
1842 | 0 | if (*ps == gp_file_name_list_separator) |
1843 | 0 | break; |
1844 | 0 | } |
1845 | 0 | ctx->fontmapfiles[i].data = gs_alloc_bytes(ctx->memory, ps - p, "fontmap file name body"); |
1846 | 0 | if (ctx->fontmapfiles[i].data == NULL) { |
1847 | 0 | code = gs_note_error(gs_error_VMerror); |
1848 | 0 | goto done; |
1849 | 0 | } |
1850 | 0 | memcpy(ctx->fontmapfiles[i].data, p, ps - p); |
1851 | 0 | ctx->fontmapfiles[i].size = ps - p; |
1852 | 0 | p = ps + 1; |
1853 | 0 | } |
1854 | 0 | } |
1855 | 0 | } |
1856 | 0 | done: |
1857 | 0 | return code; |
1858 | 0 | } |
1859 | | |
1860 | | /***********************************************************************************/ |
1861 | | /* Highest level functions. The context we create here is returned to the 'PL' */ |
1862 | | /* implementation, in future we plan to return it to PostScript by wrapping a */ |
1863 | | /* gargabe collected object 'ref' around it and returning that to the PostScript */ |
1864 | | /* world. custom PostScript operators will then be able to render pages, annots, */ |
1865 | | /* AcroForms etc by passing the opaque object back to functions here, allowing */ |
1866 | | /* the interpreter access to its context. */ |
1867 | | |
1868 | | /* We start with routines for creating and destroying the interpreter context */ |
1869 | | pdf_context *pdfi_create_context(gs_memory_t *mem) |
1870 | 94.6k | { |
1871 | 94.6k | pdf_context *ctx = NULL; |
1872 | 94.6k | gs_gstate *pgs = NULL; |
1873 | 94.6k | int code = 0; |
1874 | 94.6k | gs_memory_t *pmem = mem->non_gc_memory; |
1875 | | #if PDFI_LEAK_CHECK |
1876 | | gs_memory_status_t mstat; |
1877 | | code = gs_memory_chunk_wrap(&pmem, mem->non_gc_memory); |
1878 | | if (code < 0) |
1879 | | return NULL; |
1880 | | gs_memory_status(pmem, &mstat); |
1881 | | #endif |
1882 | | |
1883 | 94.6k | ctx = (pdf_context *) gs_alloc_bytes(pmem, sizeof(pdf_context), "pdf_create_context"); |
1884 | | |
1885 | 94.6k | pgs = gs_gstate_alloc(pmem); |
1886 | | |
1887 | 94.6k | if (!ctx || !pgs) |
1888 | 0 | { |
1889 | 0 | if (ctx) |
1890 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1891 | 0 | if (pgs) |
1892 | 0 | gs_gstate_free(pgs); |
1893 | 0 | return NULL; |
1894 | 0 | } |
1895 | | |
1896 | 94.6k | memset(ctx, 0, sizeof(pdf_context)); |
1897 | 94.6k | ctx->memory = pmem; |
1898 | 94.6k | ctx->type = PDF_CTX; |
1899 | 94.6k | ctx->flags = 0; |
1900 | 94.6k | ctx->refcnt = 1; |
1901 | 94.6k | ctx->ctx = ctx; |
1902 | | |
1903 | | #if PDFI_LEAK_CHECK |
1904 | | ctx->memstat = mstat; |
1905 | | #endif |
1906 | | |
1907 | 94.6k | ctx->stack_bot = (pdf_obj **)gs_alloc_bytes(ctx->memory, INITIAL_STACK_SIZE * sizeof (pdf_obj *), "pdf_imp_allocate_interp_stack"); |
1908 | 94.6k | if (ctx->stack_bot == NULL) { |
1909 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1910 | 0 | gs_gstate_free(pgs); |
1911 | 0 | return NULL; |
1912 | 0 | } |
1913 | 94.6k | ctx->stack_size = INITIAL_STACK_SIZE; |
1914 | 94.6k | ctx->stack_top = ctx->stack_bot - 1; |
1915 | 94.6k | code = sizeof(pdf_obj *); |
1916 | 94.6k | code *= ctx->stack_size; |
1917 | 94.6k | ctx->stack_limit = ctx->stack_bot + ctx->stack_size; |
1918 | | |
1919 | 94.6k | code = pdfi_init_font_directory(ctx); |
1920 | 94.6k | if (code < 0) { |
1921 | 0 | gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); |
1922 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1923 | 0 | gs_gstate_free(pgs); |
1924 | 0 | return NULL; |
1925 | 0 | } |
1926 | | |
1927 | 94.6k | code = gsicc_init_iccmanager(pgs); |
1928 | 94.6k | if (code < 0) { |
1929 | 0 | gs_free_object(ctx->memory, ctx->font_dir, "pdf_create_context"); |
1930 | 0 | gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); |
1931 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1932 | 0 | gs_gstate_free(pgs); |
1933 | 0 | return NULL; |
1934 | 0 | } |
1935 | | |
1936 | 94.6k | ctx->pgs = pgs; |
1937 | 94.6k | code = pdfi_gstate_set_client(ctx, pgs); |
1938 | 94.6k | if (code < 0) { |
1939 | 0 | gs_free_object(ctx->memory, ctx->font_dir, "pdf_create_context"); |
1940 | 0 | gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); |
1941 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1942 | 0 | gs_gstate_free(pgs); |
1943 | 0 | return NULL; |
1944 | 0 | } |
1945 | | |
1946 | | /* Some (but not all) path construction operations can either return |
1947 | | * an error or clamp values when out of range. In order to match Ghostscript's |
1948 | | * PDF interpreter written in PostScript, we need to clamp them. |
1949 | | */ |
1950 | 94.6k | gs_setlimitclamp(pgs, true); |
1951 | | |
1952 | | /* Declare PDL client support for high level patterns, for the benefit |
1953 | | * of pdfwrite and other high-level devices |
1954 | | */ |
1955 | 94.6k | ctx->pgs->have_pattern_streams = true; |
1956 | 94.6k | ctx->device_state.preserve_tr_mode = 0; |
1957 | 94.6k | ctx->args.notransparency = false; |
1958 | | |
1959 | 94.6k | ctx->main_stream = NULL; |
1960 | | |
1961 | | /* Setup some flags that don't default to 'false' */ |
1962 | 94.6k | ctx->args.showannots = true; |
1963 | 94.6k | ctx->args.preserveannots = true; |
1964 | 94.6k | ctx->args.preservemarkedcontent = true; |
1965 | 94.6k | ctx->args.preserveembeddedfiles = true; |
1966 | 94.6k | ctx->args.preservedocview = true; |
1967 | | /* NOTE: For testing certain annotations on cluster, might want to set this to false */ |
1968 | 94.6k | ctx->args.printed = false; /* True if OutputFile is set, false otherwise see pdftop.c, pdf_impl_set_param() */ |
1969 | | |
1970 | | /* Initially, prefer the XrefStm in a hybrid file */ |
1971 | 94.6k | ctx->prefer_xrefstm = true; |
1972 | | |
1973 | | /* We decrypt strings from encrypted files until we start a page */ |
1974 | 94.6k | ctx->encryption.decrypt_strings = true; |
1975 | 94.6k | ctx->get_glyph_name = pdfi_glyph_name; |
1976 | 94.6k | ctx->get_glyph_index = pdfi_glyph_index; |
1977 | | |
1978 | 94.6k | ctx->job_gstate_level = ctx->pgs->level; |
1979 | | /* Weirdly the graphics library wants us to always have two gstates, the |
1980 | | * initial state and at least one saved state. if we don't then when we |
1981 | | * grestore back to the initial state, it immediately saves another one. |
1982 | | */ |
1983 | 94.6k | code = gs_gsave(ctx->pgs); |
1984 | 94.6k | if (code < 0) { |
1985 | 0 | gs_free_object(ctx->memory, ctx->font_dir, "pdf_create_context"); |
1986 | 0 | gs_free_object(pmem, ctx->stack_bot, "pdf_create_context"); |
1987 | 0 | gs_gstate_free(ctx->pgs); |
1988 | 0 | gs_free_object(pmem, ctx, "pdf_create_context"); |
1989 | 0 | return NULL; |
1990 | 0 | } |
1991 | | #if REFCNT_DEBUG |
1992 | | ctx->UID = 1; |
1993 | | #endif |
1994 | | #if CACHE_STATISTICS |
1995 | | ctx->hits = 0; |
1996 | | ctx->misses = 0; |
1997 | | ctx->compressed_hits = 0; |
1998 | | ctx->compressed_misses = 0; |
1999 | | #endif |
2000 | | #ifdef DEBUG |
2001 | | ctx->args.verbose_errors = ctx->args.verbose_warnings = 1; |
2002 | | #endif |
2003 | 94.6k | return ctx; |
2004 | 94.6k | } |
2005 | | |
2006 | | /* Purge all */ |
2007 | | static bool |
2008 | | pdfi_fontdir_purge_all(const gs_memory_t * mem, cached_char * cc, void *dummy) |
2009 | 1.34M | { |
2010 | 1.34M | return true; |
2011 | 1.34M | } |
2012 | | |
2013 | | #if DEBUG_CACHE |
2014 | | #if DEBUG_CACHE_FREE |
2015 | | static void |
2016 | | pdfi_print_cache(pdf_context *ctx) |
2017 | | { |
2018 | | pdf_obj_cache_entry *entry = ctx->cache_LRU, *next; |
2019 | | |
2020 | | outprintf(ctx->memory, "CACHE: #entries=%d\n", ctx->cache_entries); |
2021 | | while(entry) { |
2022 | | next = entry->next; |
2023 | | #if REFCNT_DEBUG |
2024 | | outprintf(ctx->memory, "UID:%ld, Object:%d, refcnt:%d, next=%p, prev=%p\n", |
2025 | | entry->o->UID, entry->o->object_num, entry->o->refcnt, |
2026 | | entry->next, entry->previous); |
2027 | | #else |
2028 | | outprintf(ctx->memory, "Object:%d, refcnt:%d, next=%p, prev=%p\n", |
2029 | | entry->o->object_num, entry->o->refcnt, |
2030 | | entry->next, entry->previous); |
2031 | | #endif |
2032 | | entry = next; |
2033 | | } |
2034 | | } |
2035 | | #else |
2036 | | static void |
2037 | | pdfi_print_cache(pdf_context *ctx) |
2038 | | {} |
2039 | | #endif |
2040 | | #endif /* DEBUG */ |
2041 | | |
2042 | | #if PURGE_CACHE_PER_PAGE |
2043 | | void |
2044 | | pdfi_purge_obj_cache(pdf_context *ctx) |
2045 | | { |
2046 | | if (ctx->cache_entries != 0) { |
2047 | | pdf_obj_cache_entry *entry = ctx->cache_LRU, *next; |
2048 | | |
2049 | | while(entry) { |
2050 | | next = entry->next; |
2051 | | if (entry->o->object_num != 0) { |
2052 | | ctx->xref_table->xref[entry->o->object_num].cache = NULL; |
2053 | | } |
2054 | | pdfi_countdown(entry->o); |
2055 | | ctx->cache_entries--; |
2056 | | gs_free_object(ctx->memory, entry, "pdfi_clear_context, free LRU"); |
2057 | | entry = next; |
2058 | | #if REFCNT_DEBUG |
2059 | | ctx->cache_LRU = entry; |
2060 | | #endif |
2061 | | } |
2062 | | ctx->cache_LRU = ctx->cache_MRU = NULL; |
2063 | | ctx->cache_entries = 0; |
2064 | | } |
2065 | | } |
2066 | | #endif |
2067 | | |
2068 | | /* pdfi_clear_context frees all the PDF objects associated with interpreting a given |
2069 | | * PDF file. Once we've called this we can happily run another file. This function is |
2070 | | * called by pdf_free_context (in case of errors during the file leaving state around) |
2071 | | * and by pdfi_close_pdf_file. |
2072 | | */ |
2073 | | int pdfi_clear_context(pdf_context *ctx) |
2074 | 94.6k | { |
2075 | | #if CACHE_STATISTICS |
2076 | | float compressed_hit_rate = 0.0, hit_rate = 0.0; |
2077 | | |
2078 | | if (ctx->compressed_hits > 0 || ctx->compressed_misses > 0) |
2079 | | compressed_hit_rate = (float)ctx->compressed_hits / (float)(ctx->compressed_hits + ctx->compressed_misses); |
2080 | | if (ctx->hits > 0 || ctx->misses > 0) |
2081 | | hit_rate = (float)ctx->hits / (float)(ctx->hits + ctx->misses); |
2082 | | |
2083 | | outprintf(ctx->memory, "Number of normal object cache hits: %"PRIi64"\n", ctx->hits); |
2084 | | outprintf(ctx->memory, "Number of normal object cache misses: %"PRIi64"\n", ctx->misses); |
2085 | | outprintf(ctx->memory, "Number of compressed object cache hits: %"PRIi64"\n", ctx->compressed_hits); |
2086 | | outprintf(ctx->memory, "Number of compressed object cache misses: %"PRIi64"\n", ctx->compressed_misses); |
2087 | | outprintf(ctx->memory, "Normal object cache hit rate: %f\n", hit_rate); |
2088 | | outprintf(ctx->memory, "Compressed object cache hit rate: %f\n", compressed_hit_rate); |
2089 | | #endif |
2090 | 94.6k | if (ctx->PathSegments != NULL) { |
2091 | 5.41k | gs_free_object(ctx->memory, ctx->PathSegments, "pdfi_clear_context"); |
2092 | 5.41k | ctx->PathSegments = NULL; |
2093 | 5.41k | } |
2094 | 94.6k | if (ctx->PathPts != NULL) { |
2095 | 5.41k | gs_free_object(ctx->memory, ctx->PathPts, "pdfi_clear_context"); |
2096 | 5.41k | ctx->PathPts = NULL; |
2097 | 5.41k | } |
2098 | | |
2099 | 94.6k | if (ctx->args.PageList) { |
2100 | 0 | gs_free_object(ctx->memory, ctx->args.PageList, "pdfi_clear_context"); |
2101 | 0 | ctx->args.PageList = NULL; |
2102 | 0 | } |
2103 | 94.6k | if (ctx->Trailer) { |
2104 | 37.2k | pdfi_countdown(ctx->Trailer); |
2105 | 37.2k | ctx->Trailer = NULL; |
2106 | 37.2k | } |
2107 | | |
2108 | 94.6k | if (ctx->AcroForm) { |
2109 | 5.95k | pdfi_countdown(ctx->AcroForm); |
2110 | 5.95k | ctx->AcroForm = NULL; |
2111 | 5.95k | } |
2112 | | |
2113 | 94.6k | if(ctx->Root) { |
2114 | 77.5k | pdfi_countdown(ctx->Root); |
2115 | 77.5k | ctx->Root = NULL; |
2116 | 77.5k | } |
2117 | | |
2118 | 94.6k | if (ctx->Info) { |
2119 | 22.1k | pdfi_countdown(ctx->Info); |
2120 | 22.1k | ctx->Info = NULL; |
2121 | 22.1k | } |
2122 | | |
2123 | 94.6k | if (ctx->PagesTree) { |
2124 | 73.9k | pdfi_countdown(ctx->PagesTree); |
2125 | 73.9k | ctx->PagesTree = NULL; |
2126 | 73.9k | } |
2127 | | |
2128 | 94.6k | if (ctx->args.cidfsubstpath.data != NULL) { |
2129 | 0 | gs_free_object(ctx->memory, ctx->args.cidfsubstpath.data, "cidfsubstpath.data"); |
2130 | 0 | ctx->args.cidfsubstpath.data = NULL; |
2131 | 0 | } |
2132 | | |
2133 | 94.6k | if (ctx->args.cidfsubstfont.data != NULL) { |
2134 | 0 | gs_free_object(ctx->memory, ctx->args.cidfsubstfont.data, "cidfsubstfont.data"); |
2135 | 0 | ctx->args.cidfsubstfont.data = NULL; |
2136 | 0 | } |
2137 | | |
2138 | 94.6k | if (ctx->args.defaultfont.data != NULL) { |
2139 | 0 | gs_free_object(ctx->memory, ctx->args.defaultfont.data, "cidfsubstfont.data"); |
2140 | 0 | ctx->args.defaultfont.data = NULL; |
2141 | 0 | } |
2142 | | |
2143 | 94.6k | pdfi_free_cstring_array(ctx, &ctx->args.showannottypes); |
2144 | 94.6k | pdfi_free_cstring_array(ctx, &ctx->args.preserveannottypes); |
2145 | | |
2146 | 94.6k | pdfi_doc_page_array_free(ctx); |
2147 | | |
2148 | 94.6k | if (ctx->xref_table) { |
2149 | 86.1k | pdfi_countdown(ctx->xref_table); |
2150 | 86.1k | ctx->xref_table = NULL; |
2151 | 86.1k | } |
2152 | | |
2153 | 94.6k | pdfi_free_OptionalRoot(ctx); |
2154 | | |
2155 | 94.6k | if (ctx->stack_bot) |
2156 | 94.6k | pdfi_clearstack(ctx); |
2157 | | |
2158 | 94.6k | if (ctx->filename) { |
2159 | | /* This should already be closed! */ |
2160 | 0 | pdfi_close_pdf_file(ctx); |
2161 | 0 | gs_free_object(ctx->memory, ctx->filename, "pdfi_clear_context, free copy of filename"); |
2162 | 0 | ctx->filename = NULL; |
2163 | 0 | } |
2164 | | |
2165 | 94.6k | if (ctx->main_stream) { |
2166 | 94.5k | gs_free_object(ctx->memory, ctx->main_stream, "pdfi_clear_context, free main PDF stream"); |
2167 | 94.5k | ctx->main_stream = NULL; |
2168 | 94.5k | } |
2169 | 94.6k | ctx->main_stream_length = 0; |
2170 | | |
2171 | 94.6k | if(ctx->pgs != NULL) { |
2172 | 94.6k | gx_pattern_cache_free(ctx->pgs->pattern_cache); |
2173 | 94.6k | ctx->pgs->pattern_cache = NULL; |
2174 | 94.6k | if (ctx->pgs->font) |
2175 | 0 | pdfi_countdown_current_font(ctx); |
2176 | | |
2177 | | /* We use gs_grestore_only() instead of gs_grestore, because gs_grestore |
2178 | | * will not restore below two gstates and we want to clear the entire |
2179 | | * stack of saved states, back to the initial state. |
2180 | | */ |
2181 | 189k | while (ctx->pgs->level != ctx->job_gstate_level && ctx->pgs->saved) |
2182 | 94.6k | gs_grestore_only(ctx->pgs); |
2183 | 94.6k | } |
2184 | | |
2185 | 94.6k | pdfi_free_DefaultQState(ctx); |
2186 | 94.6k | pdfi_oc_free(ctx); |
2187 | | |
2188 | 94.6k | if(ctx->encryption.EKey) { |
2189 | 940 | pdfi_countdown(ctx->encryption.EKey); |
2190 | 940 | ctx->encryption.EKey = NULL; |
2191 | 940 | } |
2192 | 94.6k | if (ctx->encryption.Password) { |
2193 | 0 | gs_free_object(ctx->memory, ctx->encryption.Password, "PDF Password from params"); |
2194 | 0 | ctx->encryption.Password = NULL; |
2195 | 0 | } |
2196 | | |
2197 | 94.6k | if (ctx->cache_entries != 0) { |
2198 | 75.8k | pdf_obj_cache_entry *entry = ctx->cache_LRU, *next; |
2199 | | |
2200 | | #if DEBUG_CACHE |
2201 | | int count; |
2202 | | bool stop = true; |
2203 | | pdf_obj_cache_entry *prev; |
2204 | | |
2205 | | do { |
2206 | | pdfi_print_cache(ctx); |
2207 | | entry = ctx->cache_LRU; |
2208 | | stop = true; |
2209 | | while(entry) { |
2210 | | pdfi_print_cache(ctx); |
2211 | | next = entry->next; |
2212 | | prev = entry->previous; |
2213 | | |
2214 | | /* pass through the cache, count down any objects which are only referenced by the cache (refcnt == 1) |
2215 | | * this may cause other objects (referred to by the newly freed object) to decrement their refcnt |
2216 | | * until they are also only pointed at by the cache. |
2217 | | */ |
2218 | | if (entry->o->refcnt == 1) { |
2219 | | stop = false; |
2220 | | pdfi_countdown(entry->o); |
2221 | | if (prev != NULL) |
2222 | | prev->next = next; |
2223 | | else |
2224 | | ctx->cache_LRU = next; |
2225 | | if (next) |
2226 | | next->previous = prev; |
2227 | | ctx->cache_entries--; |
2228 | | gs_free_object(ctx->memory, entry, "pdfi_clear_context, free LRU"); |
2229 | | } |
2230 | | entry = next; |
2231 | | } |
2232 | | } while (stop == false); |
2233 | | |
2234 | | entry = ctx->cache_LRU; |
2235 | | while(entry) { |
2236 | | next = entry->next; |
2237 | | prev = entry->previous; |
2238 | | count = entry->o->refcnt; |
2239 | | dbgmprintf1(ctx->memory, "CLEANUP cache entry obj %d", entry->o->object_num); |
2240 | | dbgmprintf1(ctx->memory, " has refcnt %d\n", count); |
2241 | | entry = next; |
2242 | | } |
2243 | | #else |
2244 | 1.81M | while(entry) { |
2245 | 1.74M | next = entry->next; |
2246 | 1.74M | pdfi_countdown(entry->o); |
2247 | 1.74M | ctx->cache_entries--; |
2248 | 1.74M | gs_free_object(ctx->memory, entry, "pdfi_clear_context, free LRU"); |
2249 | 1.74M | entry = next; |
2250 | | #if REFCNT_DEBUG |
2251 | | ctx->cache_LRU = entry; |
2252 | | #endif |
2253 | 1.74M | } |
2254 | 75.8k | #endif |
2255 | 75.8k | ctx->cache_LRU = ctx->cache_MRU = NULL; |
2256 | 75.8k | ctx->cache_entries = 0; |
2257 | 75.8k | } |
2258 | | |
2259 | | /* We can't free the font directory before the graphics library fonts fonts are freed, as they reference the font_dir. |
2260 | | * graphics library fonts are refrenced from pdf_font objects, and those may be in the cache, which means they |
2261 | | * won't be freed until we empty the cache. So we can't free 'font_dir' until after the cache has been cleared. |
2262 | | */ |
2263 | 94.6k | if (ctx->font_dir) |
2264 | 94.6k | gx_purge_selected_cached_chars(ctx->font_dir, pdfi_fontdir_purge_all, (void *)NULL); |
2265 | | |
2266 | 94.6k | pdfi_countdown(ctx->pdffontmap); |
2267 | 94.6k | ctx->pdffontmap = NULL; |
2268 | 94.6k | pdfi_countdown(ctx->pdfnativefontmap); |
2269 | 94.6k | ctx->pdfnativefontmap = NULL; |
2270 | 94.6k | pdfi_countdown(ctx->pdf_substitute_fonts); |
2271 | 94.6k | ctx->pdf_substitute_fonts = NULL; |
2272 | | |
2273 | 94.6k | return 0; |
2274 | 94.6k | } |
2275 | | |
2276 | | int pdfi_free_context(pdf_context *ctx) |
2277 | 94.6k | { |
2278 | | #if PDFI_LEAK_CHECK |
2279 | | gs_memory_status_t mstat, ctxmstat = ctx->memstat; |
2280 | | gs_memory_t *mem = ctx->memory; |
2281 | | #endif |
2282 | 94.6k | pdfi_clear_context(ctx); |
2283 | | |
2284 | 94.6k | gs_free_object(ctx->memory, ctx->stack_bot, "pdfi_free_context"); |
2285 | | |
2286 | 94.6k | pdfi_free_name_table(ctx); |
2287 | | |
2288 | | /* And here we free the initial graphics state */ |
2289 | 94.6k | while (ctx->pgs->saved) |
2290 | 0 | gs_grestore_only(ctx->pgs); |
2291 | | |
2292 | 94.6k | gs_gstate_free(ctx->pgs); |
2293 | | |
2294 | 94.6k | ctx->pgs = NULL; |
2295 | | |
2296 | 94.6k | if (ctx->font_dir) |
2297 | 94.6k | gs_free_object(ctx->memory, ctx->font_dir, "pdfi_free_context"); |
2298 | | |
2299 | | /* Currently this should never happen, but in future it might if we choose |
2300 | | * not to keep freeing and reallocating the array. |
2301 | | */ |
2302 | 94.6k | if (ctx->loop_detection != NULL) { |
2303 | 9 | dbgmprintf(ctx->memory, "Loop detection array exists at EOJ\n"); |
2304 | 9 | gs_free_object(ctx->memory, ctx->loop_detection, "pdfi_free_context"); |
2305 | 9 | } |
2306 | | |
2307 | 94.6k | pdfi_free_search_paths(ctx); |
2308 | 94.6k | pdfi_free_fontmapfiles(ctx); |
2309 | | |
2310 | 94.6k | if (ctx->pdfcidfmap != NULL) { |
2311 | 5.99k | pdfi_countdown(ctx->pdfcidfmap); |
2312 | 5.99k | ctx->pdfcidfmap = NULL; |
2313 | 5.99k | } |
2314 | 94.6k | if (ctx->pdffontmap != NULL) { |
2315 | 0 | pdfi_countdown(ctx->pdffontmap); |
2316 | 0 | ctx->pdffontmap = NULL; |
2317 | 0 | } |
2318 | 94.6k | rc_decrement(ctx->devbbox, "pdfi_free_context"); |
2319 | | |
2320 | 94.6k | gs_free_object(ctx->memory, ctx, "pdfi_free_context"); |
2321 | | #if PDFI_LEAK_CHECK |
2322 | | gs_memory_status(mem, &mstat); |
2323 | | if (mstat.allocated > ctxmstat.allocated) |
2324 | | errprintf(mem, "\nMemory Leak Detected: Pre %d, Post %d\n", (int)ctxmstat.allocated, (int)mstat.allocated); |
2325 | | (void)gs_memory_chunk_unwrap(mem); |
2326 | | #endif |
2327 | 94.6k | return 0; |
2328 | 94.6k | } |
2329 | | |
2330 | | /* These routines are used from the PostScript interpreter inteerface. It is important that |
2331 | | * the 'interpreter part of the graphics state' should be a pdfi interpreter context while pdfi is running |
2332 | | * but the PostScript itnerpreter context when the PostScript interpreter is running. If we are going |
2333 | | * to inherit the PostScript graphics state for pdfi, then we need to turn it into a 'pdfi' |
2334 | | * graphics state for the duration of the interpretation, and back to a PostScript one when |
2335 | | * we return to the PostScript interpreter. |
2336 | | * |
2337 | | * Bizarrely it appears that the interpreter part of the gstate does not obey grestore, instead we copy |
2338 | | * the 'current' context to the saved context when we do a grestore. This seems wrong to me, but |
2339 | | * it seems to be what happens, so we can't rely on grestore to put back the interpreter context, but must |
2340 | | * do so ourselves. |
2341 | | * |
2342 | | * Hence the 'from_PS' routine fills in pointers with the current context and procs, with the expectation that |
2343 | | * these will be saved and used to restore the data in the 'to_PS' routine. |
2344 | | */ |
2345 | | /* NOTE see the comments in zpdfops.c just under the declaration of the pdfi_switch_t strcuture regarding |
2346 | | * complications with the ICC profile cache. |
2347 | | */ |
2348 | | |
2349 | | int pdfi_gstate_from_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch, gsicc_profile_cache_t *profile_cache) |
2350 | 419k | { |
2351 | 419k | int code; |
2352 | 419k | i_switch->pgs = ctx->pgs; |
2353 | 419k | i_switch->procs = pgs->client_procs; |
2354 | 419k | i_switch->client_data = (void *)pgs->client_data; |
2355 | 419k | i_switch->profile_cache = pgs->icc_profile_cache; |
2356 | 419k | code = pdfi_gstate_set_client(ctx, pgs); |
2357 | 419k | if (code < 0) |
2358 | 0 | return code; |
2359 | 419k | i_switch->psfont = pgs->font; |
2360 | 419k | pgs->icc_profile_cache = profile_cache; |
2361 | 419k | rc_increment(pgs->icc_profile_cache); |
2362 | 419k | pgs->font = NULL; |
2363 | 419k | ctx->pgs = pgs; |
2364 | 419k | return code; |
2365 | 419k | } |
2366 | | |
2367 | | void pdfi_gstate_to_PS(pdf_context *ctx, gs_gstate *pgs, pdfi_switch_t *i_switch) |
2368 | 419k | { |
2369 | 419k | pgs->client_procs.free(pgs->client_data, pgs->memory, pgs); |
2370 | 419k | pgs->client_data = NULL; |
2371 | 419k | rc_decrement(pgs->icc_profile_cache, "pdfi_gstate_to_PS"); |
2372 | 419k | pgs->icc_profile_cache = i_switch->profile_cache; |
2373 | 419k | gs_gstate_set_client(pgs, i_switch->client_data, &i_switch->procs, true); |
2374 | 419k | ctx->pgs->font = NULL; |
2375 | 419k | ctx->pgs = i_switch->pgs; |
2376 | 419k | pgs->font = i_switch->psfont; |
2377 | 419k | } |