1 /* Copyright (C) 1996, 2000, 2001 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gdevpdfp.c,v 1.53 2005/09/12 11:34:50 leonardo Exp $ */
18 /* Get/put parameters for PDF-writing driver */
19 #include "memory_.h"
20 #include "string_.h"
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gdevpdfx.h"
24 #include "gdevpdfo.h"
25 #include "gdevpdfg.h"
26 #include "gsparamx.h"
27
28 /*
29 * The pdfwrite device supports the following "real" parameters:
30 * OutputFile <string>
31 * all the Distiller parameters (also see gdevpsdp.c)
32 * Only some of the Distiller parameters actually have any effect.
33 *
34 * The device also supports the following write-only pseudo-parameters that
35 * serve only to communicate other information from the PostScript file.
36 * Their "value" is an array of strings, some of which may be the result
37 * of converting arbitrary PostScript objects to string form.
38 * pdfmark - see gdevpdfm.c
39 * DSC - processed in this file
40 */
41 private int pdf_dsc_process(gx_device_pdf * pdev,
42 const gs_param_string_array * pma);
43
44 private const int CoreDistVersion = 5000; /* Distiller 5.0 */
45 private const gs_param_item_t pdf_param_items[] = {
46 #define pi(key, type, memb) { key, type, offset_of(gx_device_pdf, memb) }
47
48 /* Acrobat Distiller 4 parameters */
49
50 /*
51 * EndPage and StartPage are renamed because EndPage collides with
52 * a page device parameter.
53 */
54 pi("PDFEndPage", gs_param_type_int, EndPage),
55 pi("PDFStartPage", gs_param_type_int, StartPage),
56 pi("Optimize", gs_param_type_bool, Optimize),
57 pi("ParseDSCCommentsForDocInfo", gs_param_type_bool,
58 ParseDSCCommentsForDocInfo),
59 pi("ParseDSCComments", gs_param_type_bool, ParseDSCComments),
60 pi("EmitDSCWarnings", gs_param_type_bool, EmitDSCWarnings),
61 pi("CreateJobTicket", gs_param_type_bool, CreateJobTicket),
62 pi("PreserveEPSInfo", gs_param_type_bool, PreserveEPSInfo),
63 pi("AutoPositionEPSFiles", gs_param_type_bool, AutoPositionEPSFiles),
64 pi("PreserveCopyPage", gs_param_type_bool, PreserveCopyPage),
65 pi("UsePrologue", gs_param_type_bool, UsePrologue),
66
67 /* Acrobat Distiller 5 parameters */
68
69 pi("OffOptimizations", gs_param_type_int, OffOptimizations),
70
71 /* Ghostscript-specific parameters */
72
73 pi("ReAssignCharacters", gs_param_type_bool, ReAssignCharacters),
74 pi("ReEncodeCharacters", gs_param_type_bool, ReEncodeCharacters),
75 pi("FirstObjectNumber", gs_param_type_long, FirstObjectNumber),
76 pi("CompressFonts", gs_param_type_bool, CompressFonts),
77 pi("PrintStatistics", gs_param_type_bool, PrintStatistics),
78 pi("MaxInlineImageSize", gs_param_type_long, MaxInlineImageSize),
79
80 /* PDF Encryption */
81 pi("OwnerPassword", gs_param_type_string, OwnerPassword),
82 pi("UserPassword", gs_param_type_string, UserPassword),
83 pi("KeyLength", gs_param_type_int, KeyLength),
84 pi("Permissions", gs_param_type_int, Permissions),
85 pi("EncryptionR", gs_param_type_int, EncryptionR),
86 pi("NoEncrypt", gs_param_type_string, NoEncrypt),
87
88 /* Target viewer capabilities (Ghostscript-specific) */
89 pi("ForOPDFRead", gs_param_type_bool, ForOPDFRead),
90 pi("PatternImagemask", gs_param_type_bool, PatternImagemask),
91 pi("MaxClipPathSize", gs_param_type_int, MaxClipPathSize),
92 pi("MaxShadingBitmapSize", gs_param_type_int, MaxShadingBitmapSize),
93 pi("MaxViewerMemorySize", gs_param_type_int, MaxViewerMemorySize),
94 pi("HaveTrueTypes", gs_param_type_bool, HaveTrueTypes),
95 pi("HaveCIDSystem", gs_param_type_bool, HaveCIDSystem),
96 pi("HaveTransparency", gs_param_type_bool, HaveTransparency),
97 pi("OPDFReadProcsetPath", gs_param_type_string, OPDFReadProcsetPath),
98 pi("CompressEntireFile", gs_param_type_bool, CompressEntireFile),
99 pi("PDFX", gs_param_type_bool, PDFX),
100 #undef pi
101 gs_param_item_end
102 };
103
104 /*
105 Notes on implementing the remaining Distiller functionality
106 ===========================================================
107
108 Architectural issues
109 --------------------
110
111 Must optionally disable application of TR, BG, UCR similarly. Affects:
112 PreserveHalftoneInfo
113 PreserveOverprintSettings
114 TransferFunctionInfo
115 UCRandBGInfo
116
117 Current limitations
118 -------------------
119
120 Non-primary elements in HalftoneType 5 are not written correctly
121
122 Acrobat Distiller 3
123 -------------------
124
125 ---- Image parameters ----
126
127 AntiAlias{Color,Gray,Mono}Images
128
129 ---- Other parameters ----
130
131 CompressPages
132 Compress things other than page contents
133 * PreserveHalftoneInfo
134 PreserveOPIComments
135 ? see OPI spec?
136 * PreserveOverprintSettings
137 * TransferFunctionInfo
138 * UCRandBGInfo
139 ColorConversionStrategy
140 Select color space for drawing commands
141 ConvertImagesToIndexed
142 Postprocess image data *after* downsampling (requires an extra pass)
143
144 Acrobat Distiller 4
145 -------------------
146
147 ---- Other functionality ----
148
149 Document structure pdfmarks
150
151 ---- Parameters ----
152
153 xxxDownsampleType = /Bicubic
154 Add new filter (or use siscale?) & to setup (gdevpsdi.c)
155 DetectBlends
156 Idiom recognition? PatternType 2 patterns / shfill? (see AD4)
157 DoThumbnails
158 Also output to memory device -- resolution issue
159
160 ---- Job-level control ----
161
162 EmitDSCWarnings
163 Require DSC parser / interceptor
164 CreateJobTicket
165 ?
166 AutoPositionEPSFiles
167 Require DSC parsing
168 PreserveCopyPage
169 Concatenate Contents streams
170 UsePrologue
171 Needs hack in top-level control?
172
173 */
174
175 /* ---------------- Get parameters ---------------- */
176
177 /* Get parameters. */
178 int
gdev_pdf_get_params(gx_device * dev,gs_param_list * plist)179 gdev_pdf_get_params(gx_device * dev, gs_param_list * plist)
180 {
181 gx_device_pdf *pdev = (gx_device_pdf *) dev;
182 float cl = (float)pdev->CompatibilityLevel;
183 int code = gdev_psdf_get_params(dev, plist);
184 int cdv = CoreDistVersion;
185 int EmbedFontObjects = 1;
186
187 if (code < 0 ||
188 (code = param_write_int(plist, ".EmbedFontObjects", &EmbedFontObjects)) < 0 ||
189 (code = param_write_int(plist, "CoreDistVersion", &cdv)) < 0 ||
190 (code = param_write_float(plist, "CompatibilityLevel", &cl)) < 0 ||
191 /* Indicate that we can process pdfmark and DSC. */
192 (param_requested(plist, "pdfmark") > 0 &&
193 (code = param_write_null(plist, "pdfmark")) < 0) ||
194 (param_requested(plist, "DSC") > 0 &&
195 (code = param_write_null(plist, "DSC")) < 0) ||
196 (code = gs_param_write_items(plist, pdev, NULL, pdf_param_items)) < 0
197 );
198 return code;
199 }
200
201 /* ---------------- Put parameters ---------------- */
202
203 /* Put parameters. */
204 int
gdev_pdf_put_params(gx_device * dev,gs_param_list * plist)205 gdev_pdf_put_params(gx_device * dev, gs_param_list * plist)
206 {
207 gx_device_pdf *pdev = (gx_device_pdf *) dev;
208 int ecode, code;
209 gx_device_pdf save_dev;
210 float cl = (float)pdev->CompatibilityLevel;
211 bool locked = pdev->params.LockDistillerParams;
212 gs_param_name param_name;
213
214 /*
215 * If this is a pseudo-parameter (pdfmark or DSC),
216 * don't bother checking for any real ones.
217 */
218
219 {
220 gs_param_string_array ppa;
221
222 code = param_read_string_array(plist, (param_name = "pdfmark"), &ppa);
223 switch (code) {
224 case 0:
225 code = pdf_open_document(pdev);
226 if (code < 0)
227 return code;
228 code = pdfmark_process(pdev, &ppa);
229 if (code >= 0)
230 return code;
231 /* falls through for errors */
232 default:
233 param_signal_error(plist, param_name, code);
234 return code;
235 case 1:
236 break;
237 }
238
239 code = param_read_string_array(plist, (param_name = "DSC"), &ppa);
240 switch (code) {
241 case 0:
242 code = pdf_open_document(pdev);
243 if (code < 0)
244 return code;
245 code = pdf_dsc_process(pdev, &ppa);
246 if (code >= 0)
247 return code;
248 /* falls through for errors */
249 default:
250 param_signal_error(plist, param_name, code);
251 return code;
252 case 1:
253 break;
254 }
255 }
256
257 /*
258 * Check for LockDistillerParams before doing anything else.
259 * If LockDistillerParams is true and is not being set to false,
260 * ignore all resettings of PDF-specific parameters. Note that
261 * LockDistillerParams is read again, and reset if necessary, in
262 * psdf_put_params.
263 */
264 ecode = code = param_read_bool(plist, "LockDistillerParams", &locked);
265
266 if (!(locked && pdev->params.LockDistillerParams)) {
267 /* General parameters. */
268
269 {
270 int efo = 1;
271
272 ecode = param_put_int(plist, (param_name = ".EmbedFontObjects"), &efo, ecode);
273 if (efo != 1)
274 param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
275 }
276 {
277 int cdv = CoreDistVersion;
278
279 ecode = param_put_int(plist, (param_name = "CoreDistVersion"), &cdv, ecode);
280 if (cdv != CoreDistVersion)
281 param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
282 }
283
284 save_dev = *pdev;
285
286 switch (code = param_read_float(plist, (param_name = "CompatibilityLevel"), &cl)) {
287 default:
288 ecode = code;
289 param_signal_error(plist, param_name, ecode);
290 case 0:
291 /*
292 * Must be 1.2, 1.3, or 1.4. Per Adobe documentation, substitute
293 * the nearest achievable value.
294 */
295 if (cl < (float)1.15)
296 cl = (float)1.1;
297 else if (cl < (float)1.25)
298 cl = (float)1.2;
299 else if (cl >= (float)1.35)
300 cl = (float)1.4;
301 else
302 cl = (float)1.3;
303 case 1:
304 break;
305 }
306
307 code = gs_param_read_items(plist, pdev, pdf_param_items);
308 if (code < 0)
309 ecode = code;
310 {
311 /*
312 * Setting FirstObjectNumber is only legal if the file
313 * has just been opened and nothing has been written,
314 * or if we are setting it to the same value.
315 */
316 long fon = pdev->FirstObjectNumber;
317
318 if (fon != save_dev.FirstObjectNumber) {
319 if (fon <= 0 || fon > 0x7fff0000 ||
320 (pdev->next_id != 0 &&
321 pdev->next_id !=
322 save_dev.FirstObjectNumber + pdf_num_initial_ids)
323 ) {
324 ecode = gs_error_rangecheck;
325 param_signal_error(plist, "FirstObjectNumber", ecode);
326 }
327 }
328 }
329 {
330 /*
331 * Set ProcessColorModel now, because gx_default_put_params checks
332 * it.
333 */
334 static const char *const pcm_names[] = {
335 "DeviceGray", "DeviceRGB", "DeviceCMYK", "DeviceN", 0
336 };
337 int pcm = -1;
338
339 ecode = param_put_enum(plist, "ProcessColorModel", &pcm,
340 pcm_names, ecode);
341 if (pcm >= 0) {
342 pdf_set_process_color_model(pdev, pcm);
343 pdf_set_initial_color(pdev, &pdev->saved_fill_color, &pdev->saved_stroke_color,
344 &pdev->fill_used_process_color, &pdev->stroke_used_process_color);
345 }
346 }
347 }
348 if (ecode < 0)
349 goto fail;
350 /*
351 * We have to set version to the new value, because the set of
352 * legal parameter values for psdf_put_params varies according to
353 * the version.
354 */
355 if (pdev->PDFX)
356 cl = (float)1.3; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
357 pdev->version = (cl < 1.2 ? psdf_version_level2 : psdf_version_ll3);
358 if (pdev->ForOPDFRead) {
359 pdev->ResourcesBeforeUsage = true;
360 pdev->HaveCFF = false;
361 pdev->HavePDFWidths = false;
362 pdev->HaveStrokeColor = false;
363 cl = (float)1.2; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
364 pdev->MaxInlineImageSize = max_long; /* Save printer's RAM from saving temporary image data.
365 Immediate images doen't need buffering. */
366 pdev->version = psdf_version_level2;
367 } else {
368 pdev->ResourcesBeforeUsage = false;
369 pdev->HaveCFF = true;
370 pdev->HavePDFWidths = true;
371 pdev->HaveStrokeColor = true;
372 }
373 ecode = gdev_psdf_put_params(dev, plist);
374 if (ecode < 0)
375 goto fail;
376 if (pdev->HaveTrueTypes && pdev->version == psdf_version_level2) {
377 pdev->version = psdf_version_level2_with_TT ;
378 }
379 /*
380 * Acrobat Reader doesn't handle user-space coordinates larger than
381 * MAX_USER_COORD. To compensate for this, reduce the resolution so
382 * that the page size in device space (which we equate to user space) is
383 * significantly less than MAX_USER_COORD. Note that this still does
384 * not protect us against input files that use coordinates far outside
385 * the page boundaries.
386 */
387 #define MAX_EXTENT ((int)(MAX_USER_COORD * 0.9))
388 /* Changing resolution or page size requires closing the device, */
389 if (dev->height > MAX_EXTENT || dev->width > MAX_EXTENT) {
390 double factor =
391 max(dev->height / (double)MAX_EXTENT,
392 dev->width / (double)MAX_EXTENT);
393
394 gx_device_set_resolution(dev, dev->HWResolution[0] / factor,
395 dev->HWResolution[1] / factor);
396 }
397 #undef MAX_EXTENT
398 if (pdev->FirstObjectNumber != save_dev.FirstObjectNumber) {
399 if (pdev->xref.file != 0) {
400 fseek(pdev->xref.file, 0L, SEEK_SET);
401 pdf_initialize_ids(pdev);
402 }
403 }
404 /* Handle the float/double mismatch. */
405 pdev->CompatibilityLevel = (int)(cl * 10 + 0.5) / 10.0;
406 return 0;
407 fail:
408 /* Restore all the parameters to their original state. */
409 pdev->version = save_dev.version;
410 pdf_set_process_color_model(pdev, save_dev.pcm_color_info_index);
411 pdev->saved_fill_color = save_dev.saved_fill_color;
412 pdev->saved_stroke_color = save_dev.saved_fill_color;
413 {
414 const gs_param_item_t *ppi = pdf_param_items;
415
416 for (; ppi->key; ++ppi)
417 memcpy((char *)pdev + ppi->offset,
418 (char *)&save_dev + ppi->offset,
419 gs_param_type_sizes[ppi->type]);
420 }
421 return ecode;
422 }
423
424 /* ---------------- Process DSC comments ---------------- */
425
426 private int
pdf_dsc_process(gx_device_pdf * pdev,const gs_param_string_array * pma)427 pdf_dsc_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
428 {
429 /*
430 * The Adobe "Distiller Parameters" documentation says that Distiller
431 * looks at DSC comments, but it doesn't say which ones. We look at
432 * the ones that we see how to map directly to obvious PDF constructs.
433 */
434 int code = 0;
435 int i;
436
437 /*
438 * If ParseDSCComments is false, all DSC comments are ignored, even if
439 * ParseDSCComentsForDocInfo or PreserveEPSInfo is true.
440 */
441 if (!pdev->ParseDSCComments)
442 return 0;
443
444 for (i = 0; i + 1 < pma->size && code >= 0; i += 2) {
445 const gs_param_string *pkey = &pma->data[i];
446 const gs_param_string *pvalue = &pma->data[i + 1];
447 const char *key;
448 int code;
449
450 /*
451 * %%For, %%Creator, and %%Title are recognized only if either
452 * ParseDSCCommentsForDocInfo or PreserveEPSInfo is true.
453 * The other DSC comments are always recognized.
454 *
455 * Acrobat Distiller sets CreationDate and ModDate to the current
456 * time, not the value of %%CreationDate. We think this is wrong,
457 * but we do the same -- we ignore %%CreationDate here.
458 */
459
460 if (pdf_key_eq(pkey, "Creator"))
461 key = "/Creator";
462 else if (pdf_key_eq(pkey, "Title"))
463 key = "/Title";
464 else if (pdf_key_eq(pkey, "For"))
465 key = "/Author";
466 else {
467 pdf_page_dsc_info_t *ppdi;
468
469 if ((ppdi = &pdev->doc_dsc_info,
470 pdf_key_eq(pkey, "Orientation")) ||
471 (ppdi = &pdev->page_dsc_info,
472 pdf_key_eq(pkey, "PageOrientation"))
473 ) {
474 if (pvalue->size == 1 && pvalue->data[0] >= '0' &&
475 pvalue->data[0] <= '3'
476 )
477 ppdi->orientation = pvalue->data[0] - '0';
478 else
479 ppdi->orientation = -1;
480 } else if ((ppdi = &pdev->doc_dsc_info,
481 pdf_key_eq(pkey, "ViewingOrientation")) ||
482 (ppdi = &pdev->page_dsc_info,
483 pdf_key_eq(pkey, "PageViewingOrientation"))
484 ) {
485 gs_matrix mat;
486 int orient;
487
488 if (sscanf((const char *)pvalue->data, "[%g %g %g %g]",
489 &mat.xx, &mat.xy, &mat.yx, &mat.yy) != 4
490 )
491 continue; /* error */
492 for (orient = 0; orient < 4; ++orient) {
493 if (mat.xx == 1 && mat.xy == 0 && mat.yx == 0 && mat.yy == 1)
494 break;
495 gs_matrix_rotate(&mat, -90.0, &mat);
496 }
497 if (orient == 4) /* error */
498 orient = -1;
499 ppdi->viewing_orientation = orient;
500 } else {
501 gs_rect box;
502
503 if (pdf_key_eq(pkey, "EPSF")) {
504 pdev->is_EPS = (pvalue->size >= 1 && pvalue->data[0] != '0');
505 continue;
506 }
507 /*
508 * We only parse the BoundingBox for the sake of
509 * AutoPositionEPSFiles.
510 */
511 if (pdf_key_eq(pkey, "BoundingBox"))
512 ppdi = &pdev->doc_dsc_info;
513 else if (pdf_key_eq(pkey, "PageBoundingBox"))
514 ppdi = &pdev->page_dsc_info;
515 else
516 continue;
517 if (sscanf((const char *)pvalue->data, "[%lg %lg %lg %lg]",
518 &box.p.x, &box.p.y, &box.q.x, &box.q.y) != 4
519 )
520 continue; /* error */
521 ppdi->bounding_box = box;
522 }
523 continue;
524 }
525
526 if (pdev->ParseDSCCommentsForDocInfo || pdev->PreserveEPSInfo)
527 code = cos_dict_put_c_key_string(pdev->Info, key,
528 pvalue->data, pvalue->size);
529 }
530 return code;
531 }
532