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