/src/ghostpdl/pdf/pdftop.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 API implementation of PDF */ |
17 | | |
18 | | /* Language wrapper implementation (see pltop.h) */ |
19 | | |
20 | | #include "ghostpdf.h" |
21 | | |
22 | | #include "pltop.h" |
23 | | #include "plmain.h" |
24 | | |
25 | | #include "plparse.h" /* for e_ExitLanguage */ |
26 | | #include "gxdevice.h" /* so we can include gxht.h below */ |
27 | | #include "gxht.h" /* gsht1.h is incomplete, we need storage size of gs_halftone */ |
28 | | #include "gsht1.h" |
29 | | #include "pdf_device.h" |
30 | | #include "pdf_misc.h" |
31 | | |
32 | | #include "gsstate.h" /* For gs_sethalftonephase() */ |
33 | | #include "gspaint.h" /* For gs_erasepage() */ |
34 | | #include "gscolor3.h" /* For gs_setsmoothness() */ |
35 | | |
36 | | extern const char gp_file_name_list_separator; |
37 | | |
38 | | static int pdfi_install_halftone(pdf_context *ctx, gx_device *pdevice); |
39 | | static int pdf_impl_add_path(pl_interp_implementation_t *impl, const char *path); |
40 | | |
41 | | /* |
42 | | * The PDF interpreter instance is derived from pl_interp_implementation_t. |
43 | | */ |
44 | | |
45 | | typedef struct pdf_interp_instance_s |
46 | | { |
47 | | gs_memory_t *memory; /* memory allocator to use */ |
48 | | |
49 | | pdf_context *ctx; |
50 | | gp_file *scratch_file; |
51 | | char scratch_name[gp_file_name_sizeof]; |
52 | | }pdf_interp_instance_t; |
53 | | |
54 | | extern const char gp_file_name_list_separator; |
55 | | |
56 | 24.2k | #define GS_LIB_DEFAULT_STRING GS_STRINGIZE(GS_LIB_DEFAULT) |
57 | | |
58 | | static int |
59 | | pdf_detect_language(const char *s, int len) |
60 | 17.7k | { |
61 | 17.7k | if (len >= 5 && memcmp(s, "%PDF-", 5) == 0) |
62 | 8 | return 100; |
63 | 17.7k | return 0; |
64 | 17.7k | } |
65 | | |
66 | | static const pl_interp_characteristics_t * |
67 | | pdf_impl_characteristics(const pl_interp_implementation_t *pimpl) |
68 | 37.5k | { |
69 | 37.5k | static pl_interp_characteristics_t pdf_characteristics = |
70 | 37.5k | { |
71 | 37.5k | "PDF", |
72 | 37.5k | pdf_detect_language, |
73 | 37.5k | }; |
74 | 37.5k | return &pdf_characteristics; |
75 | 37.5k | } |
76 | | |
77 | | #if 0 /* the following are not currently used */ |
78 | | static void |
79 | | pdf_set_nocache(pl_interp_implementation_t *impl, gs_font_dir *font_dir) |
80 | | { |
81 | | bool nocache; |
82 | | pdf_interp_instance_t *pdfi = impl->interp_client_data; |
83 | | nocache = pl_main_get_nocache(pdfi->memory); |
84 | | if (nocache) |
85 | | gs_setcachelimit(font_dir, 0); |
86 | | return; |
87 | | } |
88 | | |
89 | | |
90 | | static int |
91 | | pdf_set_icc_user_params(pl_interp_implementation_t *impl, gs_gstate *pgs) |
92 | | { |
93 | | pdf_interp_instance_t *pdfi = impl->interp_client_data; |
94 | | return pl_set_icc_params(pdfi->memory, pgs); |
95 | | } |
96 | | |
97 | | #endif |
98 | | |
99 | | static int |
100 | | pdf_impl_finish_page(pdf_context *ctx) |
101 | 0 | { |
102 | 0 | return pl_finish_page(ctx->memory->gs_lib_ctx->top_of_system, ctx->pgs, 1, true); |
103 | 0 | } |
104 | | |
105 | | /* Do per-instance interpreter allocation/init. No device is set yet */ |
106 | | static int |
107 | | pdf_impl_allocate_interp_instance(pl_interp_implementation_t *impl, |
108 | | gs_memory_t *pmem) |
109 | 8.09k | { |
110 | 8.09k | pdf_interp_instance_t *instance; |
111 | 8.09k | pdf_context *ctx; |
112 | 8.09k | int code; |
113 | 8.09k | const char *rompathstr = "%rom%Resource/Init"; |
114 | 8.09k | const char *deflibstr = GS_LIB_DEFAULT_STRING; |
115 | 8.09k | char *newpathsstr = (char *)deflibstr; |
116 | | |
117 | 8.09k | instance = (pdf_interp_instance_t *) gs_alloc_bytes(pmem, |
118 | 8.09k | sizeof(pdf_interp_instance_t), "pdf_impl_allocate_interp_instance"); |
119 | | |
120 | 8.09k | if (!instance) |
121 | 0 | return gs_error_VMerror; |
122 | | |
123 | 8.09k | ctx = pdfi_create_context(pmem); |
124 | | |
125 | 8.09k | if (ctx == NULL) { |
126 | 0 | gs_free_object(pmem, instance, "pdf_impl_allocate_interp_instance"); |
127 | 0 | return gs_error_VMerror; |
128 | 0 | } |
129 | | |
130 | 8.09k | ctx->finish_page = pdf_impl_finish_page; |
131 | | |
132 | 8.09k | ctx->instance = instance; |
133 | 8.09k | instance->ctx = ctx; |
134 | 8.09k | instance->scratch_file = NULL; |
135 | 8.09k | instance->scratch_name[0] = 0; |
136 | 8.09k | instance->memory = pmem; |
137 | | |
138 | 8.09k | impl->interp_client_data = instance; |
139 | 8.09k | if (COMPILE_INITS == 1) { |
140 | 8.09k | newpathsstr = (char *)gs_alloc_bytes(ctx->memory, (size_t)strlen(rompathstr) + strlen(GS_LIB_DEFAULT_STRING) + 2, "temp paths string"); |
141 | 8.09k | if (newpathsstr == NULL) { |
142 | 0 | newpathsstr = (char *)rompathstr; |
143 | 0 | } |
144 | 8.09k | else { |
145 | 8.09k | char sepstr[2]; |
146 | | |
147 | 8.09k | sepstr[0] = gp_file_name_list_separator; |
148 | 8.09k | sepstr[1] = '\0'; |
149 | | |
150 | 8.09k | memcpy(newpathsstr, rompathstr, strlen(rompathstr) + 1); |
151 | 8.09k | strncat(newpathsstr, sepstr, strlen(sepstr)); |
152 | 8.09k | strncat(newpathsstr, GS_LIB_DEFAULT_STRING, strlen(GS_LIB_DEFAULT_STRING)); |
153 | 8.09k | } |
154 | 8.09k | } |
155 | | |
156 | 8.09k | code = pdfi_add_initial_paths_to_search_paths(ctx, newpathsstr, strlen(newpathsstr)); |
157 | 8.09k | if (newpathsstr != deflibstr && newpathsstr != rompathstr) { |
158 | 8.09k | gs_free_object(ctx->memory, newpathsstr, "temp paths string"); |
159 | 8.09k | } |
160 | 8.09k | return code; |
161 | 8.09k | } |
162 | | |
163 | | static int |
164 | | pdf_impl_set_device(pl_interp_implementation_t *impl, gx_device *pdevice) |
165 | 8 | { |
166 | 8 | pdf_interp_instance_t *instance = impl->interp_client_data; |
167 | 8 | pdf_context *ctx = instance->ctx; |
168 | 8 | int code; |
169 | | |
170 | | #if 0 |
171 | | gx_device_fill_in_procs(pmi->device); |
172 | | code = gs_opendevice(pdevice); |
173 | | if (code < 0) |
174 | | goto cleanup_setdevice; |
175 | | #endif |
176 | | |
177 | | /* gsave and grestore (among other places) assume that */ |
178 | | /* there are at least 2 gstates on the graphics stack. */ |
179 | | /* Ensure that now. */ |
180 | 8 | code = gs_gsave(ctx->pgs); |
181 | 8 | if (code < 0) |
182 | 0 | goto cleanup_gsave; |
183 | | |
184 | 8 | code = gs_setdevice_no_erase(ctx->pgs, pdevice); |
185 | 8 | if (code < 0) |
186 | 0 | goto cleanup_setdevice; |
187 | | |
188 | | /* TODO: See stuff with init_graphics in pdfi_page_render -- I think this |
189 | | * should be collected in one place? |
190 | | * This is essentially doing it one time, and the other is doing it per page. |
191 | | * The main thing seems to be doing it here is before the pdfwrite device |
192 | | * gets setup, and doing it in pdf_page.c is after the pdfwrite device gets |
193 | | * setup. It can cause spurious 'gs' state entries if there are discrepencies. |
194 | | */ |
195 | | /* Doing this here to avoid having a ".02 /SM" entry showing up in ExtGState |
196 | | * of pdfwrite output. |
197 | | */ |
198 | 8 | code = gs_setsmoothness(ctx->pgs, 0.02); /* Match gs code */ |
199 | | |
200 | | /* NOTE: Not sure what this is or when it matters, but it should match gs, which sets it to true */ |
201 | | /* TODO: Compiled out because it caused a seemingly random incorrect pdfwrite output. |
202 | | * Will try putting it back in when pdfwrite is more stable. |
203 | | */ |
204 | | #if 0 |
205 | | gs_setlimitclamp(ctx->pgs, true); /* Match gs code */ |
206 | | #endif |
207 | | /* We need to setup strokeadjust the same way gs does, which is currently based on |
208 | | * the device resolution and the DITHERPPI arg. |
209 | | * |
210 | | * TODO: Ken thinks 150 dpi is too low for the cutoff, but if so it should |
211 | | * change in both gs and here, so they match. See gs_init.ps/.useloresscreen |
212 | | * |
213 | | * TODO: Currently pdfi-in-gs is not inheriting the graphics state from gs, so it |
214 | | * will have the wrong setting at 300dpi. That needs to be fixed. |
215 | | * (but pdfi-in-gs never actually calls this function, it is supposed to inherit |
216 | | * the device and graphics state from gs. Remove this comment when that is fixed :) |
217 | | */ |
218 | 8 | if (pdevice->HWResolution[0] >= 150 || pdevice->HWResolution[1] >= 150 || ctx->args.ditherppi) { |
219 | | /* false for "high resolution" devices */ |
220 | 0 | gs_setstrokeadjust(ctx->pgs, false); |
221 | 8 | } else { |
222 | | /* true for "low resolution" devices */ |
223 | 8 | gs_setstrokeadjust(ctx->pgs, true); |
224 | 8 | } |
225 | | |
226 | 8 | gs_setscanconverter(ctx->pgs, pl_main_get_scanconverter(ctx->memory)); |
227 | | |
228 | 8 | code = gs_erasepage(ctx->pgs); |
229 | 8 | if (code < 0) |
230 | 0 | goto cleanup_erase; |
231 | | |
232 | 8 | code = pdfi_install_halftone(ctx, pdevice); |
233 | 8 | if (code < 0) |
234 | 0 | goto cleanup_halftone; |
235 | | |
236 | | /* TODO: Hack to do what is in the bottom of gs_pdfwr.ps |
237 | | * This basically causes the pdfwrite device to be initialized. |
238 | | * Not sure if this is the correct place to do this. |
239 | | * Note that if running gs/pdfi, it will happen in the gs interpreter. |
240 | | * Putting it here means it only runs in gpdf, which seems correct. |
241 | | */ |
242 | 8 | if (pdfi_device_check_param_exists(pdevice, "ForOPDFRead")) { |
243 | 0 | gs_newpath(ctx->pgs); |
244 | 0 | gs_fill(ctx->pgs); |
245 | 0 | } |
246 | | |
247 | 8 | ctx->job_gstate_level = ctx->pgs->level; |
248 | 8 | return 0; |
249 | | |
250 | 0 | cleanup_halftone: |
251 | 0 | cleanup_erase: |
252 | | /* undo gsave */ |
253 | 0 | gs_grestore_only(ctx->pgs); /* destroys gs_save stack */ |
254 | |
|
255 | 0 | cleanup_gsave: |
256 | | /* undo setdevice */ |
257 | 0 | gs_nulldevice(ctx->pgs); |
258 | |
|
259 | 0 | cleanup_setdevice: |
260 | | /* nothing to undo */ |
261 | 0 | return code; |
262 | 0 | } |
263 | | |
264 | | /* Parse an entire random access file */ |
265 | | static int |
266 | | pdf_impl_process_file(pl_interp_implementation_t *impl, const char *filename) |
267 | 8 | { |
268 | 8 | pdf_interp_instance_t *instance = impl->interp_client_data; |
269 | 8 | pdf_context *ctx = instance->ctx; |
270 | 8 | int code; |
271 | | |
272 | 8 | code = pdfi_process_pdf_file(ctx, (char *)filename); |
273 | 8 | if (code) |
274 | 8 | return code; |
275 | | |
276 | 0 | return 0; |
277 | 8 | } |
278 | | |
279 | | static int |
280 | | pdf_impl_process_begin(pl_interp_implementation_t * impl) |
281 | 0 | { |
282 | 0 | return 0; |
283 | 0 | } |
284 | | |
285 | | /* Parse a cursor-full of data */ |
286 | | static int |
287 | | pdf_impl_process(pl_interp_implementation_t *impl, stream_cursor_read *cursor) |
288 | 0 | { |
289 | 0 | pdf_interp_instance_t *instance = impl->interp_client_data; |
290 | 0 | pdf_context *ctx = instance->ctx; |
291 | 0 | int avail, n; |
292 | |
|
293 | 0 | if (!instance->scratch_file) |
294 | 0 | { |
295 | 0 | instance->scratch_file = gp_open_scratch_file(ctx->memory, |
296 | 0 | "ghostpdf-scratch-", instance->scratch_name, "wb"); |
297 | 0 | if (!instance->scratch_file) |
298 | 0 | { |
299 | 0 | gs_catch(gs_error_invalidfileaccess, "cannot open scratch file"); |
300 | 0 | return e_ExitLanguage; |
301 | 0 | } |
302 | 0 | if_debug1m('|', ctx->memory, "pdf: open scratch file '%s'\n", instance->scratch_name); |
303 | 0 | } |
304 | | |
305 | 0 | avail = cursor->limit - cursor->ptr; |
306 | 0 | n = gp_fwrite(cursor->ptr + 1, 1, avail, instance->scratch_file); |
307 | 0 | if (n != avail) |
308 | 0 | { |
309 | 0 | gs_catch(gs_error_invalidfileaccess, "cannot write to scratch file"); |
310 | 0 | return e_ExitLanguage; |
311 | 0 | } |
312 | 0 | cursor->ptr = cursor->limit; |
313 | |
|
314 | 0 | return 0; |
315 | 0 | } |
316 | | |
317 | | static int /* ret 0 or +ve if ok, else -ve error code */ |
318 | | pdf_impl_process_end(pl_interp_implementation_t * impl) |
319 | 0 | { |
320 | 0 | return 0; |
321 | 0 | } |
322 | | |
323 | | /* Skip to end of job. |
324 | | * Return 1 if done, 0 ok but EOJ not found, else negative error code. |
325 | | */ |
326 | | static int |
327 | | pdf_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *pcursor) |
328 | 0 | { |
329 | | /* assume PDF cannot be pjl embedded */ |
330 | 0 | pcursor->ptr = pcursor->limit; |
331 | 0 | return 0; |
332 | 0 | } |
333 | | |
334 | | /* Parser action for end-of-file */ |
335 | | static int |
336 | | pdf_impl_process_eof(pl_interp_implementation_t *impl) |
337 | 0 | { |
338 | 0 | pdf_interp_instance_t *instance = impl->interp_client_data; |
339 | 0 | pdf_context *ctx = instance->ctx; |
340 | 0 | int code; |
341 | |
|
342 | 0 | if (instance->scratch_file) |
343 | 0 | { |
344 | 0 | if_debug0m('|', ctx->memory, "pdf: executing scratch file\n"); |
345 | 0 | gp_fclose(instance->scratch_file); |
346 | 0 | instance->scratch_file = NULL; |
347 | 0 | code = pdfi_process_pdf_file(ctx, instance->scratch_name); |
348 | 0 | gp_unlink(ctx->memory, instance->scratch_name); |
349 | 0 | if (code < 0) |
350 | 0 | { |
351 | 0 | gs_catch(code, "cannot process PDF file"); |
352 | 0 | return e_ExitLanguage; |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | return 0; |
357 | 0 | } |
358 | | |
359 | | /* Report any errors after running a job */ |
360 | | static int |
361 | | pdf_impl_report_errors(pl_interp_implementation_t *impl, |
362 | | int code, /* prev termination status */ |
363 | | long file_position, /* file position of error, -1 if unknown */ |
364 | | bool force_to_cout /* force errors to cout */ |
365 | | ) |
366 | 0 | { |
367 | 0 | return 0; |
368 | 0 | } |
369 | | |
370 | | #if 0 |
371 | | /* |
372 | | * Get the allocator with which to allocate a device |
373 | | */ |
374 | | static gs_memory_t * |
375 | | pdf_impl_get_device_memory(pl_interp_implementation_t *impl) |
376 | | { |
377 | | pdf_interp_instance_t *instance = impl->interp_client_data; |
378 | | pdf_context *ctx = instance->ctx; |
379 | | |
380 | | return ctx->memory; |
381 | | } |
382 | | #endif |
383 | | |
384 | | static int plist_value_get_int64(gs_param_typed_value *pvalue, int64_t *pint) |
385 | 0 | { |
386 | 0 | if (pvalue->type == gs_param_type_i64) { |
387 | 0 | *pint = pvalue->value.i64; |
388 | 0 | return 0; |
389 | 0 | } |
390 | 0 | return_error(gs_error_typecheck); |
391 | 0 | } |
392 | | |
393 | | /* Get the value for a string or a name (null terminated) */ |
394 | | static int plist_value_get_string_or_name(pdf_context *ctx, gs_param_typed_value *pvalue, |
395 | | char **pstr, int *plen, bool *is_name) |
396 | 0 | { |
397 | 0 | const byte *data; |
398 | 0 | uint size; |
399 | |
|
400 | 0 | if (pvalue->type == gs_param_type_string) { |
401 | 0 | data = pvalue->value.s.data; |
402 | 0 | size = pvalue->value.s.size; |
403 | 0 | *is_name = false; |
404 | 0 | } else if (pvalue->type == gs_param_type_name) { |
405 | 0 | data = pvalue->value.n.data; |
406 | 0 | size = pvalue->value.n.size; |
407 | 0 | *is_name = true; |
408 | 0 | } else { |
409 | 0 | return_error(gs_error_typecheck); |
410 | 0 | } |
411 | | |
412 | | /* Free any previous value, for example if param appears more than once */ |
413 | 0 | if (*pstr) |
414 | 0 | gs_free_object(ctx->memory, *pstr, "plist_value_get_string_or_name()"); |
415 | |
|
416 | 0 | *pstr = (char *)gs_alloc_bytes(ctx->memory, (size_t)size + 1, "plist_value_get_string_or_name()"); |
417 | 0 | if (*pstr == NULL) |
418 | 0 | return_error(gs_error_VMerror); |
419 | | |
420 | 0 | memcpy(*pstr, data, size); |
421 | 0 | *((*pstr)+size) = 0; /* Null terminate */ |
422 | 0 | *plen = size; |
423 | 0 | return 0; |
424 | 0 | } |
425 | | |
426 | | /* Get the value for a list of names as c-strings, (null terminated) */ |
427 | | /* Format: -s/Item1,/Item2,/Item3 (no white space) */ |
428 | | static int plist_value_get_namelist(pdf_context *ctx, gs_param_typed_value *pvalue, |
429 | | char ***pstrlist) |
430 | 0 | { |
431 | 0 | char *data; |
432 | 0 | uint size; |
433 | |
|
434 | 0 | if (pvalue->type != gs_param_type_string) |
435 | 0 | return_error(gs_error_typecheck); |
436 | | |
437 | 0 | data = (char *)pvalue->value.s.data; |
438 | 0 | size = pvalue->value.s.size; |
439 | |
|
440 | 0 | return pdfi_parse_name_cstring_array(ctx, data, size, pstrlist); |
441 | 0 | } |
442 | | |
443 | | static int plist_value_get_int(gs_param_typed_value *pvalue, int *pint) |
444 | 0 | { |
445 | 0 | if (pvalue->type == gs_param_type_int) { |
446 | 0 | *pint = (int64_t)pvalue->value.i; |
447 | 0 | return 0; |
448 | 0 | } |
449 | 0 | if (pvalue->type == gs_param_type_i64) { |
450 | 0 | int64_t i64; |
451 | 0 | int code; |
452 | |
|
453 | 0 | code = plist_value_get_int64(pvalue, &i64); |
454 | 0 | if (code < 0) |
455 | 0 | return code; |
456 | | |
457 | 0 | if (i64 > ((int64_t)1 << 32)) |
458 | 0 | return_error(gs_error_rangecheck); |
459 | | |
460 | 0 | *pint = (int)i64; |
461 | 0 | return 0; |
462 | 0 | } |
463 | 0 | return_error(gs_error_typecheck); |
464 | 0 | } |
465 | | |
466 | | static int plist_value_get_bool(gs_param_typed_value *pvalue, bool *pbool) |
467 | 8.09k | { |
468 | 8.09k | if (pvalue->type == gs_param_type_bool) { |
469 | 8.09k | *pbool = pvalue->value.b; |
470 | 8.09k | return 0; |
471 | 8.09k | } |
472 | 8.09k | return_error(gs_error_typecheck); |
473 | 8.09k | } |
474 | | |
475 | 1.39M | #define argis(P, S) \ |
476 | 1.39M | (!strncmp((P), (S), sizeof(S)-1) && ((P)[sizeof(S)-1] == 0 || (P)[sizeof(S)-1] == '=' || (P)[sizeof(S)-1] == '#')) |
477 | | |
478 | | static int |
479 | | pdf_impl_set_param(pl_interp_implementation_t *impl, |
480 | | gs_param_list *plist) |
481 | 32.3k | { |
482 | 32.3k | pdf_interp_instance_t *instance = impl->interp_client_data; |
483 | 32.3k | pdf_context *ctx = instance->ctx; |
484 | 32.3k | gs_param_enumerator_t enumerator; |
485 | 32.3k | gs_param_key_t key; |
486 | 32.3k | int code; |
487 | 32.3k | int len; |
488 | 32.3k | bool discard_isname; |
489 | 32.3k | bool Printed_set = false; |
490 | | |
491 | 32.3k | param_init_enumerator(&enumerator); |
492 | 32.3k | if ((code = param_get_next_key(plist, &enumerator, &key)) == 0) { |
493 | 32.3k | char param[256]; /* big enough for any reasonable key */ |
494 | 32.3k | gs_param_typed_value pvalue; |
495 | | |
496 | 32.3k | if (key.size > sizeof(param) - 1) { |
497 | 0 | code = gs_note_error(gs_error_rangecheck); |
498 | 0 | goto exit; |
499 | 0 | } |
500 | 32.3k | memcpy(param, key.data, key.size); |
501 | 32.3k | param[key.size] = 0; |
502 | 32.3k | if ((code = param_read_typed(plist, param, &pvalue)) != 0) { |
503 | 0 | code = (code > 0 ? gs_note_error(gs_error_unknownerror) : code); |
504 | 0 | goto exit; |
505 | 0 | } |
506 | | |
507 | 32.3k | if (argis(param, "QUIET")) { |
508 | 8.09k | code = plist_value_get_bool(&pvalue, &ctx->args.QUIET); |
509 | 8.09k | if (code < 0) |
510 | 0 | return code; |
511 | 8.09k | } |
512 | 32.3k | if (argis(param, "FirstPage")) { |
513 | 0 | code = plist_value_get_int(&pvalue, &ctx->args.first_page); |
514 | 0 | if (code < 0) |
515 | 0 | return code; |
516 | 0 | } |
517 | 32.3k | if (argis(param, "LastPage")) { |
518 | 0 | code = plist_value_get_int(&pvalue, &ctx->args.last_page); |
519 | 0 | if (code < 0) |
520 | 0 | return code; |
521 | 0 | } |
522 | | /* PDF interpreter flags */ |
523 | 32.3k | if (argis(param, "VerboseErrors")) { |
524 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.verbose_errors); |
525 | 0 | if (code < 0) |
526 | 0 | return code; |
527 | 0 | } |
528 | 32.3k | if (argis(param, "VerboseWarnings")) { |
529 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.verbose_warnings); |
530 | 0 | if (code < 0) |
531 | 0 | return code; |
532 | 0 | } |
533 | 32.3k | if (argis(param, "PDFCACHE")) { |
534 | 0 | code = plist_value_get_int(&pvalue, &ctx->args.PDFCacheSize); |
535 | 0 | if (code < 0) |
536 | 0 | return code; |
537 | 0 | } |
538 | 32.3k | if (argis(param, "PDFDEBUG")) { |
539 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.pdfdebug); |
540 | 0 | if (code < 0) |
541 | 0 | return code; |
542 | 0 | } |
543 | 32.3k | if (argis(param, "PDFSTOPONERROR")) { |
544 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.pdfstoponerror); |
545 | 0 | if (code < 0) |
546 | 0 | return code; |
547 | 0 | } |
548 | 32.3k | if (argis(param, "PDFSTOPONWARNING")) { |
549 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.pdfstoponwarning); |
550 | 0 | if (code < 0) |
551 | 0 | return code; |
552 | 0 | if (ctx->args.pdfstoponwarning != 0) |
553 | 0 | ctx->args.pdfstoponerror = 1; |
554 | 0 | } |
555 | 32.3k | if (argis(param, "NOTRANSPARENCY")) { |
556 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.notransparency); |
557 | 0 | if (code < 0) |
558 | 0 | return code; |
559 | 0 | } |
560 | 32.3k | if (argis(param, "PDFNOCIDFALLBACK")) { |
561 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.nocidfallback); |
562 | 0 | if (code < 0) |
563 | 0 | return code; |
564 | 0 | } |
565 | 32.3k | if (argis(param, "NO_PDFMARK_OUTLINES")) { |
566 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.no_pdfmark_outlines); |
567 | 0 | if (code < 0) |
568 | 0 | return code; |
569 | 0 | } |
570 | 32.3k | if (argis(param, "NO_PDFMARK_DESTS")) { |
571 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.no_pdfmark_dests); |
572 | 0 | if (code < 0) |
573 | 0 | return code; |
574 | 0 | } |
575 | 32.3k | if (argis(param, "PDFFitPage")) { |
576 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.pdffitpage); |
577 | 0 | if (code < 0) |
578 | 0 | return code; |
579 | 0 | } |
580 | 32.3k | if (argis(param, "UseCropBox")) { |
581 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.usecropbox); |
582 | 0 | if (code < 0) |
583 | 0 | return code; |
584 | 0 | } |
585 | 32.3k | if (argis(param, "UseArtBox")) { |
586 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.useartbox); |
587 | 0 | if (code < 0) |
588 | 0 | return code; |
589 | 0 | } |
590 | 32.3k | if (argis(param, "UseBleedBox")) { |
591 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.usebleedbox); |
592 | 0 | if (code < 0) |
593 | 0 | return code; |
594 | 0 | } |
595 | 32.3k | if (argis(param, "UseTrimBox")) { |
596 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.usetrimbox); |
597 | 0 | if (code < 0) |
598 | 0 | return code; |
599 | 0 | } |
600 | 32.3k | if (argis(param, "Printed")) { |
601 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.printed); |
602 | 0 | if (code < 0) |
603 | 0 | return code; |
604 | 0 | Printed_set = true; |
605 | 0 | } |
606 | 32.3k | if (argis(param, "DITHERPPI")) { |
607 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.ditherppi); |
608 | 0 | if (code < 0) |
609 | 0 | return code; |
610 | 0 | } |
611 | 32.3k | if (argis(param, "ShowAcroForm")) { |
612 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.showacroform); |
613 | 0 | if (code < 0) |
614 | 0 | return code; |
615 | 0 | } |
616 | 32.3k | if (argis(param, "ShowAnnots")) { |
617 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.showannots); |
618 | 0 | if (code < 0) |
619 | 0 | return code; |
620 | 0 | } |
621 | 32.3k | if (argis(param, "SHOWANNOTTYPES")) { |
622 | 0 | code = plist_value_get_namelist(ctx, &pvalue, &ctx->args.showannottypes); |
623 | 0 | if (code < 0) |
624 | 0 | return code; |
625 | 0 | } |
626 | 32.3k | if (argis(param, "PreserveAnnots")) { |
627 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.preserveannots); |
628 | 0 | if (code < 0) |
629 | 0 | return code; |
630 | 0 | } |
631 | 32.3k | if (argis(param, "PRESERVEANNOTTYPES")) { |
632 | 0 | code = plist_value_get_namelist(ctx, &pvalue, &ctx->args.preserveannottypes); |
633 | 0 | if (code < 0) |
634 | 0 | return code; |
635 | 0 | } |
636 | 32.3k | if (argis(param, "PreserveMarkedContent")) { |
637 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.preservemarkedcontent); |
638 | 0 | if (code < 0) |
639 | 0 | return code; |
640 | 0 | } |
641 | 32.3k | if (argis(param, "PreserveEmbeddedFiles")) { |
642 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.preserveembeddedfiles); |
643 | 0 | if (code < 0) |
644 | 0 | return code; |
645 | 0 | } |
646 | 32.3k | if (argis(param, "PreserveDocView")) { |
647 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.preservedocview); |
648 | 0 | if (code < 0) |
649 | 0 | return code; |
650 | 0 | } |
651 | 32.3k | if (argis(param, "NoUserUnit")) { |
652 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.nouserunit); |
653 | 0 | if (code < 0) |
654 | 0 | return code; |
655 | 0 | } |
656 | 32.3k | if (argis(param, "RENDERTTNOTDEF")) { |
657 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.renderttnotdef); |
658 | 0 | if (code < 0) |
659 | 0 | return code; |
660 | 0 | } |
661 | 32.3k | if (argis(param, "PDFINFO")) { |
662 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.pdfinfo); |
663 | 0 | if (code < 0) |
664 | 0 | return code; |
665 | 0 | } |
666 | 32.3k | if (argis(param, "DOPDFMARKS")) { |
667 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.dopdfmarks); |
668 | 0 | if (code < 0) |
669 | 0 | return code; |
670 | 0 | } |
671 | 32.3k | if (argis(param, "PDFPassword")) { |
672 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->encryption.Password , &ctx->encryption.PasswordLen, &discard_isname); |
673 | 0 | if (code < 0) |
674 | 0 | return code; |
675 | 0 | } |
676 | 32.3k | if (argis(param, "UsePDFX3Profile")) { |
677 | | /* This is a weird one because it can be either a bool or an int. |
678 | | * If it's a bool=true, then it defaults to PDFX3Profile_num = 0 |
679 | | * If it's an int, then we set the flag to true and use the |
680 | | * specified number. |
681 | | * PS: Yuck! |
682 | | */ |
683 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.UsePDFX3Profile); |
684 | 0 | if (code == gs_error_typecheck) { |
685 | 0 | code = plist_value_get_int(&pvalue, &ctx->args.PDFX3Profile_num); |
686 | 0 | if (code == 0) |
687 | 0 | ctx->args.UsePDFX3Profile = true; |
688 | 0 | } |
689 | 0 | if (code < 0) |
690 | 0 | return code; |
691 | 0 | } |
692 | 32.3k | if (argis(param, "NOSUBSTDEVICECOLORS")) { |
693 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.NOSUBSTDEVICECOLORS); |
694 | 0 | if (code < 0) |
695 | 0 | return code; |
696 | 0 | } |
697 | 32.3k | if (argis(param, "UseOutputIntent")) { |
698 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, &ctx->args.UseOutputIntent, &len, &discard_isname); |
699 | 0 | if (code < 0) |
700 | 0 | return code; |
701 | 0 | } |
702 | 32.3k | if (argis(param, "FONTPATH")) { |
703 | 0 | char *s = NULL; |
704 | 0 | int slen; |
705 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, &s , &slen, &discard_isname); |
706 | 0 | if (code < 0) |
707 | 0 | return code; |
708 | 0 | code = pdfi_add_paths_to_search_paths(ctx, (const char *)s, slen, true); |
709 | 0 | gs_free_object(ctx->memory, s, "FONTPATH param string"); |
710 | 0 | } |
711 | 32.3k | if (argis(param, "FONTMAP")) { |
712 | 0 | char *s = NULL; |
713 | 0 | int slen; |
714 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, &s, &slen, &discard_isname); |
715 | 0 | if (code < 0) |
716 | 0 | return code; |
717 | 0 | code = pdfi_add_fontmapfiles(ctx, (const char *)s, slen); |
718 | 0 | gs_free_object(ctx->memory, s, "FONTMAP param string"); |
719 | 0 | } |
720 | 32.3k | if (argis(param, "CIDSubstPath")) { |
721 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidfsubstpath.data, (int *)&ctx->args.cidfsubstpath.size, &discard_isname); |
722 | 0 | if (code < 0) |
723 | 0 | return code; |
724 | 0 | } |
725 | 32.3k | if (argis(param, "CIDFSubstFont")) { |
726 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.cidfsubstfont.data, (int *)&ctx->args.cidfsubstfont.size, &discard_isname); |
727 | 0 | if (code < 0) |
728 | 0 | return code; |
729 | 0 | } |
730 | 32.3k | if (argis(param, "SUBSTFONT")) { |
731 | 0 | code = plist_value_get_string_or_name(ctx, &pvalue, (char **)&ctx->args.defaultfont.data, (int *)&ctx->args.defaultfont.size, &ctx->args.defaultfont_is_name); |
732 | 0 | if (code < 0) |
733 | 0 | return code; |
734 | 0 | } |
735 | 32.3k | if (argis(param, "IgnoreToUnicode")) { |
736 | 0 | code = plist_value_get_bool(&pvalue, &ctx->args.ignoretounicode); |
737 | 0 | if (code < 0) |
738 | 0 | return code; |
739 | 0 | } |
740 | 32.3k | if (argis(param, "OutputFile")) { |
741 | 8.09k | if (!Printed_set) |
742 | 8.09k | ctx->args.printed = true; |
743 | 8.09k | } |
744 | 32.3k | } |
745 | | |
746 | 32.3k | exit: |
747 | 32.3k | return code; |
748 | 32.3k | } |
749 | | |
750 | | static int |
751 | | pdf_impl_add_path(pl_interp_implementation_t *impl, const char *path) |
752 | 0 | { |
753 | 0 | pdf_interp_instance_t *instance = impl->interp_client_data; |
754 | 0 | pdf_context *ctx = instance->ctx; |
755 | |
|
756 | 0 | return pdfi_add_paths_to_search_paths(ctx, path, strlen(path), false); |
757 | 0 | } |
758 | | |
759 | | static int |
760 | | pdf_impl_post_args_init(pl_interp_implementation_t *impl) |
761 | 8.09k | { |
762 | 8.09k | return 0; |
763 | 8.09k | } |
764 | | |
765 | | /* Prepare interp instance for the next "job" */ |
766 | | static int |
767 | | pdf_impl_init_job(pl_interp_implementation_t *impl, |
768 | | gx_device *device) |
769 | 8 | { |
770 | 8 | pdf_interp_instance_t *instance = impl->interp_client_data; |
771 | 8 | pdf_context *ctx = instance->ctx; |
772 | | |
773 | 8 | if (getenv("PDF_DISABLE_TRANSPARENCY")) |
774 | 0 | ctx->args.notransparency = true; |
775 | | |
776 | 8 | return pdf_impl_set_device(impl, device); |
777 | 8 | } |
778 | | |
779 | | /* Wrap up interp instance after a "job" */ |
780 | | static int |
781 | | pdf_impl_dnit_job(pl_interp_implementation_t *impl) |
782 | 8 | { |
783 | 8 | pdf_interp_instance_t *instance = impl->interp_client_data; |
784 | 8 | pdf_context *ctx = instance->ctx; |
785 | | |
786 | 8 | return gs_grestoreall(ctx->pgs); /* pdf_impl_set_device does a gsave which we need to restore now. |
787 | | * Using grestoreall will restore any dangling gstates back to the last restore. |
788 | | * This should ensure we don't have dangling references to the device created by the |
789 | | * top level code. |
790 | | */ |
791 | 8 | } |
792 | | |
793 | | /* Deallocate a interpreter instance */ |
794 | | static int |
795 | | pdf_impl_deallocate_interp_instance(pl_interp_implementation_t *impl) |
796 | 8.09k | { |
797 | 8.09k | pdf_interp_instance_t *instance = impl->interp_client_data; |
798 | 8.09k | pdf_context *ctx = instance->ctx; |
799 | 8.09k | gs_memory_t *mem = instance->memory; |
800 | 8.09k | int code = 0; |
801 | | |
802 | 8.09k | code = pdfi_free_context(ctx); |
803 | | |
804 | 8.09k | gs_free_object(mem, instance, "pdf_impl_deallocate_interp_instance"); |
805 | | |
806 | 8.09k | return code; |
807 | 8.09k | } |
808 | | |
809 | | /* Parser implementation descriptor */ |
810 | | pl_interp_implementation_t pdf_implementation = |
811 | | { |
812 | | pdf_impl_characteristics, |
813 | | pdf_impl_allocate_interp_instance, |
814 | | NULL, /* pdf_impl_get_device_memory, */ |
815 | | pdf_impl_set_param, |
816 | | pdf_impl_add_path, |
817 | | pdf_impl_post_args_init, |
818 | | pdf_impl_init_job, |
819 | | NULL, /* run_prefix_commands */ |
820 | | pdf_impl_process_file, |
821 | | pdf_impl_process_begin, |
822 | | pdf_impl_process, |
823 | | pdf_impl_process_end, |
824 | | pdf_impl_flush_to_eoj, |
825 | | pdf_impl_process_eof, |
826 | | pdf_impl_report_errors, |
827 | | pdf_impl_dnit_job, |
828 | | pdf_impl_deallocate_interp_instance, |
829 | | NULL, /* interp_client_data */ |
830 | | }; |
831 | | |
832 | | /* |
833 | | * We need to install a halftone ourselves, this is not |
834 | | * done automatically. |
835 | | */ |
836 | | |
837 | | static float |
838 | | identity_transfer(double tint, const gx_transfer_map *ignore_map) |
839 | 2.04k | { |
840 | 2.04k | return tint; |
841 | 2.04k | } |
842 | | |
843 | | /* The following is a 45 degree spot screen with the spots enumerated |
844 | | * in a defined order. */ |
845 | | static byte order16x16[256] = { |
846 | | 38, 11, 14, 32, 165, 105, 90, 171, 38, 12, 14, 33, 161, 101, 88, 167, |
847 | | 30, 6, 0, 16, 61, 225, 231, 125, 30, 6, 1, 17, 63, 222, 227, 122, |
848 | | 27, 3, 8, 19, 71, 242, 205, 110, 28, 4, 9, 20, 74, 246, 208, 106, |
849 | | 35, 24, 22, 40, 182, 46, 56, 144, 36, 25, 22, 41, 186, 48, 58, 148, |
850 | | 152, 91, 81, 174, 39, 12, 15, 34, 156, 95, 84, 178, 40, 13, 16, 34, |
851 | | 69, 212, 235, 129, 31, 7, 2, 18, 66, 216, 239, 133, 32, 8, 2, 18, |
852 | | 79, 254, 203, 114, 28, 4, 10, 20, 76, 250, 199, 118, 29, 5, 10, 21, |
853 | | 193, 44, 54, 142, 36, 26, 23, 42, 189, 43, 52, 139, 37, 26, 24, 42, |
854 | | 39, 12, 15, 33, 159, 99, 87, 169, 38, 11, 14, 33, 163, 103, 89, 172, |
855 | | 31, 7, 1, 17, 65, 220, 229, 123, 30, 6, 1, 17, 62, 223, 233, 127, |
856 | | 28, 4, 9, 20, 75, 248, 210, 108, 27, 3, 9, 19, 72, 244, 206, 112, |
857 | | 36, 25, 23, 41, 188, 49, 60, 150, 35, 25, 22, 41, 184, 47, 57, 146, |
858 | | 157, 97, 85, 180, 40, 13, 16, 35, 154, 93, 83, 176, 39, 13, 15, 34, |
859 | | 67, 218, 240, 135, 32, 8, 3, 19, 70, 214, 237, 131, 31, 7, 2, 18, |
860 | | 78, 252, 197, 120, 29, 5, 11, 21, 80, 255, 201, 116, 29, 5, 10, 21, |
861 | | 191, 43, 51, 137, 37, 27, 24, 43, 195, 44, 53, 140, 37, 26, 23, 42 |
862 | | }; |
863 | | |
864 | | #define source_phase_x 4 |
865 | | #define source_phase_y 0 |
866 | | |
867 | | static int |
868 | | pdfi_install_halftone(pdf_context *ctx, gx_device *pdevice) |
869 | 8 | { |
870 | 8 | gs_halftone ht; |
871 | 8 | gs_string thresh; |
872 | 8 | int code; |
873 | | |
874 | 8 | int width = 16; |
875 | 8 | int height = 16; |
876 | 8 | thresh.data = order16x16; |
877 | 8 | thresh.size = width * height; |
878 | | |
879 | 8 | if (gx_device_must_halftone(pdevice)) |
880 | 8 | { |
881 | 8 | memset(&ht.rc, 0x00, sizeof(ht.rc)); |
882 | 8 | ht.type = ht_type_threshold; |
883 | 8 | ht.objtype = HT_OBJTYPE_DEFAULT; |
884 | 8 | ht.params.threshold.width = width; |
885 | 8 | ht.params.threshold.height = height; |
886 | 8 | ht.params.threshold.thresholds.data = thresh.data; |
887 | 8 | ht.params.threshold.thresholds.size = thresh.size; |
888 | 8 | ht.params.threshold.transfer = 0; |
889 | 8 | ht.params.threshold.transfer_closure.proc = 0; |
890 | | |
891 | 8 | gs_settransfer(ctx->pgs, identity_transfer); |
892 | | |
893 | 8 | code = gs_sethalftone(ctx->pgs, &ht); |
894 | 8 | if (code < 0) |
895 | 0 | return gs_throw(code, "could not install halftone"); |
896 | | |
897 | 8 | code = gs_sethalftonephase(ctx->pgs, 0, 0); |
898 | 8 | if (code < 0) |
899 | 0 | return gs_throw(code, "could not set halftone phase"); |
900 | 8 | } |
901 | | |
902 | 8 | return 0; |
903 | 8 | } |