1 /* Copyright (C) 2001-2006 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, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: gdevpdfp.c 10141 2009-10-06 07:33:33Z ken $ */
15 /* Get/put parameters for PDF-writing driver */
16 #include "memory_.h"
17 #include "gx.h"
18 #include "gserrors.h"
19 #include "gdevpdfx.h"
20 #include "gdevpdfo.h"
21 #include "gdevpdfg.h"
22 #include "gsparamx.h"
23 
24 /*
25  * The pdfwrite device supports the following "real" parameters:
26  *      OutputFile <string>
27  *      all the Distiller parameters (also see gdevpsdp.c)
28  * Only some of the Distiller parameters actually have any effect.
29  *
30  * The device also supports the following write-only pseudo-parameters that
31  * serve only to communicate other information from the PostScript file.
32  * Their "value" is an array of strings, some of which may be the result
33  * of converting arbitrary PostScript objects to string form.
34  *      pdfmark - see gdevpdfm.c
35  *	DSC - processed in this file
36  */
37 static int pdf_dsc_process(gx_device_pdf * pdev,
38 			    const gs_param_string_array * pma);
39 
40 static const int CoreDistVersion = 5000;	/* Distiller 5.0 */
41 static const gs_param_item_t pdf_param_items[] = {
42 #define pi(key, type, memb) { key, type, offset_of(gx_device_pdf, memb) }
43 
44 	/* Acrobat Distiller 4 parameters */
45 
46     /*
47      * EndPage and StartPage are renamed because EndPage collides with
48      * a page device parameter.
49      */
50     pi("PDFEndPage", gs_param_type_int, EndPage),
51     pi("PDFStartPage", gs_param_type_int, StartPage),
52     pi("Optimize", gs_param_type_bool, Optimize),
53     pi("ParseDSCCommentsForDocInfo", gs_param_type_bool,
54        ParseDSCCommentsForDocInfo),
55     pi("ParseDSCComments", gs_param_type_bool, ParseDSCComments),
56     pi("EmitDSCWarnings", gs_param_type_bool, EmitDSCWarnings),
57     pi("CreateJobTicket", gs_param_type_bool, CreateJobTicket),
58     pi("PreserveEPSInfo", gs_param_type_bool, PreserveEPSInfo),
59     pi("AutoPositionEPSFiles", gs_param_type_bool, AutoPositionEPSFiles),
60     pi("PreserveCopyPage", gs_param_type_bool, PreserveCopyPage),
61     pi("UsePrologue", gs_param_type_bool, UsePrologue),
62 
63 	/* Acrobat Distiller 5 parameters */
64 
65     pi("OffOptimizations", gs_param_type_int, OffOptimizations),
66 
67 	/* Ghostscript-specific parameters */
68 
69     pi("ReAssignCharacters", gs_param_type_bool, ReAssignCharacters),
70     pi("ReEncodeCharacters", gs_param_type_bool, ReEncodeCharacters),
71     pi("FirstObjectNumber", gs_param_type_long, FirstObjectNumber),
72     pi("CompressFonts", gs_param_type_bool, CompressFonts),
73     pi("PrintStatistics", gs_param_type_bool, PrintStatistics),
74     pi("MaxInlineImageSize", gs_param_type_long, MaxInlineImageSize),
75     pi("DSCEncodingToUnicode", gs_param_type_int_array, DSCEncodingToUnicode),
76 
77 	/* PDF Encryption */
78     pi("OwnerPassword", gs_param_type_string, OwnerPassword),
79     pi("UserPassword", gs_param_type_string, UserPassword),
80     pi("KeyLength", gs_param_type_int, KeyLength),
81     pi("Permissions", gs_param_type_int, Permissions),
82     pi("EncryptionR", gs_param_type_int, EncryptionR),
83     pi("NoEncrypt", gs_param_type_string, NoEncrypt),
84 
85 	/* Target viewer capabilities (Ghostscript-specific)  */
86  /* pi("ForOPDFRead", gs_param_type_bool, ForOPDFRead),			    pdfwrite-only */
87     pi("PatternImagemask", gs_param_type_bool, PatternImagemask),
88     pi("MaxClipPathSize", gs_param_type_int, MaxClipPathSize),
89     pi("MaxShadingBitmapSize", gs_param_type_int, MaxShadingBitmapSize),
90     pi("MaxViewerMemorySize", gs_param_type_int, MaxViewerMemorySize),
91     pi("HaveTrueTypes", gs_param_type_bool, HaveTrueTypes),
92     pi("HaveCIDSystem", gs_param_type_bool, HaveCIDSystem),
93     pi("HaveTransparency", gs_param_type_bool, HaveTransparency),
94  /* pi("OPDFReadProcsetPath", gs_param_type_string, OPDFReadProcsetPath),   ps2write-only */
95     pi("CompressEntireFile", gs_param_type_bool, CompressEntireFile),
96     pi("PDFX", gs_param_type_bool, PDFX),
97     pi("PDFA", gs_param_type_bool, PDFA),
98     pi("DocumentUUID", gs_param_type_string, DocumentUUID),
99     pi("InstanceUUID", gs_param_type_string, InstanceUUID),
100     pi("DocumentTimeSeq", gs_param_type_int, DocumentTimeSeq),
101 
102     /* PDF/X parameters */
103     pi("PDFXTrimBoxToMediaBoxOffset", gs_param_type_float_array, PDFXTrimBoxToMediaBoxOffset),
104     pi("PDFXSetBleedBoxToMediaBox", gs_param_type_bool, PDFXSetBleedBoxToMediaBox),
105     pi("PDFXBleedBoxToTrimBoxOffset", gs_param_type_float_array, PDFXBleedBoxToTrimBoxOffset),
106 
107     /* media selection parameters */
108     pi("SetPageSize", gs_param_type_bool, SetPageSize),
109     pi("RotatePages", gs_param_type_bool, RotatePages),
110     pi("FitPages", gs_param_type_bool, FitPages),
111     pi("CenterPages", gs_param_type_bool, CenterPages),
112     pi("DoNumCopies", gs_param_type_bool, DoNumCopies),
113     pi("PreserveSeparation", gs_param_type_bool, PreserveSeparation),
114     pi("PreserveDeviceN", gs_param_type_bool, PreserveDeviceN),
115     pi("PDFACompatibilityPolicy", gs_param_type_int, PDFACompatibilityPolicy),
116 #undef pi
117     gs_param_item_end
118 };
119 
120 /*
121   Notes on implementing the remaining Distiller functionality
122   ===========================================================
123 
124   Architectural issues
125   --------------------
126 
127   Must optionally disable application of TR, BG, UCR similarly.  Affects:
128     PreserveHalftoneInfo
129     PreserveOverprintSettings
130     TransferFunctionInfo
131     UCRandBGInfo
132 
133   Current limitations
134   -------------------
135 
136   Non-primary elements in HalftoneType 5 are not written correctly
137 
138   Acrobat Distiller 3
139   -------------------
140 
141   ---- Image parameters ----
142 
143   AntiAlias{Color,Gray,Mono}Images
144 
145   ---- Other parameters ----
146 
147   CompressPages
148     Compress things other than page contents
149   * PreserveHalftoneInfo
150   PreserveOPIComments
151     ? see OPI spec?
152   * PreserveOverprintSettings
153   * TransferFunctionInfo
154   * UCRandBGInfo
155   ColorConversionStrategy
156     Select color space for drawing commands
157   ConvertImagesToIndexed
158     Postprocess image data *after* downsampling (requires an extra pass)
159 
160   Acrobat Distiller 4
161   -------------------
162 
163   ---- Other functionality ----
164 
165   Document structure pdfmarks
166 
167   ---- Parameters ----
168 
169   xxxDownsampleType = /Bicubic
170     Add new filter (or use siscale?) & to setup (gdevpsdi.c)
171   DetectBlends
172     Idiom recognition?  PatternType 2 patterns / shfill?  (see AD4)
173   DoThumbnails
174     Also output to memory device -- resolution issue
175 
176   ---- Job-level control ----
177 
178   EmitDSCWarnings
179     Require DSC parser / interceptor
180   CreateJobTicket
181     ?
182   AutoPositionEPSFiles
183     Require DSC parsing
184   PreserveCopyPage
185     Concatenate Contents streams
186   UsePrologue
187     Needs hack in top-level control?
188 
189 */
190 
191 /* ---------------- Get parameters ---------------- */
192 
193 /* Get parameters. */
194 int
gdev_pdf_get_params(gx_device * dev,gs_param_list * plist)195 gdev_pdf_get_params(gx_device * dev, gs_param_list * plist)
196 {
197     gx_device_pdf *pdev = (gx_device_pdf *) dev;
198     float cl = (float)pdev->CompatibilityLevel;
199     int code;
200     int cdv = CoreDistVersion;
201 
202     pdev->ParamCompatibilityLevel = cl;
203     code = gdev_psdf_get_params(dev, plist);
204     if (code < 0 ||
205 	(code = param_write_int(plist, "CoreDistVersion", &cdv)) < 0 ||
206 	(code = param_write_float(plist, "CompatibilityLevel", &cl)) < 0 ||
207 	(pdev->is_ps2write && (code = param_write_string(plist, "OPDFReadProcsetPath", &pdev->OPDFReadProcsetPath)) < 0) ||
208 	(!pdev->is_ps2write && (code = param_write_bool(plist, "ForOPDFRead", &pdev->ForOPDFRead)) < 0) ||
209 	/* Indicate that we can process pdfmark and DSC. */
210 	(param_requested(plist, "pdfmark") > 0 &&
211 	 (code = param_write_null(plist, "pdfmark")) < 0) ||
212 	(param_requested(plist, "DSC") > 0 &&
213 	 (code = param_write_null(plist, "DSC")) < 0) ||
214 	(code = gs_param_write_items(plist, pdev, NULL, pdf_param_items)) < 0
215 	);
216     return code;
217 }
218 
219 /* ---------------- Put parameters ---------------- */
220 
221 /* Put parameters, implementation */
222 static int
gdev_pdf_put_params_impl(gx_device * dev,const gx_device_pdf * save_dev,gs_param_list * plist)223 gdev_pdf_put_params_impl(gx_device * dev, const gx_device_pdf * save_dev, gs_param_list * plist)
224 {
225     int ecode, code;
226     gx_device_pdf *pdev = (gx_device_pdf *) dev;
227     float cl = (float)pdev->CompatibilityLevel;
228     bool locked = pdev->params.LockDistillerParams;
229     gs_param_name param_name;
230     enum psdf_color_conversion_strategy save_ccs = pdev->params.ColorConversionStrategy;
231 
232     pdev->pdf_memory = gs_memory_stable(pdev->memory);
233     /*
234      * If this is a pseudo-parameter (pdfmark or DSC),
235      * don't bother checking for any real ones.
236      */
237 
238     {
239 	gs_param_string_array ppa;
240 
241 	code = param_read_string_array(plist, (param_name = "pdfmark"), &ppa);
242 	switch (code) {
243 	    case 0:
244 		code = pdf_open_document(pdev);
245 		if (code < 0)
246 		    return code;
247 		code = pdfmark_process(pdev, &ppa);
248 		if (code >= 0)
249 		    return code;
250 		/* falls through for errors */
251 	    default:
252 		param_signal_error(plist, param_name, code);
253 		return code;
254 	    case 1:
255 		break;
256 	}
257 
258 	code = param_read_string_array(plist, (param_name = "DSC"), &ppa);
259 	switch (code) {
260 	    case 0:
261 		code = pdf_open_document(pdev);
262 		if (code < 0)
263 		    return code;
264 		code = pdf_dsc_process(pdev, &ppa);
265 		if (code >= 0)
266 		    return code;
267 		/* falls through for errors */
268 	    default:
269 		param_signal_error(plist, param_name, code);
270 		return code;
271 	    case 1:
272 		break;
273 	}
274     }
275 
276     /*
277      * Check for LockDistillerParams before doing anything else.
278      * If LockDistillerParams is true and is not being set to false,
279      * ignore all resettings of PDF-specific parameters.  Note that
280      * LockDistillerParams is read again, and reset if necessary, in
281      * psdf_put_params.
282      */
283     ecode = code = param_read_bool(plist, "LockDistillerParams", &locked);
284 
285     if (!(locked && pdev->params.LockDistillerParams)) {
286 	/* General parameters. */
287 
288 	{
289 	    int efo = 1;
290 
291 	    ecode = param_put_int(plist, (param_name = ".EmbedFontObjects"), &efo, ecode);
292 	    if (efo != 1)
293 		param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
294 	}
295 	{
296 	    int cdv = CoreDistVersion;
297 
298 	    ecode = param_put_int(plist, (param_name = "CoreDistVersion"), &cdv, ecode);
299 	    if (cdv != CoreDistVersion)
300 		param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
301 	}
302 
303 	switch (code = param_read_float(plist, (param_name = "CompatibilityLevel"), &cl)) {
304 	    default:
305 		ecode = code;
306 		param_signal_error(plist, param_name, ecode);
307 	    case 0:
308 		/*
309 		 * Must be 1.2, 1.3, 1.4, or 1.5.  Per Adobe documentation, substitute
310 		 * the nearest achievable value.
311 		 */
312 		if (cl < (float)1.15)
313 		    cl = (float)1.1;
314 		else if (cl < (float)1.25)
315 		    cl = (float)1.2;
316 		else if (cl < (float)1.35)
317 		    cl = (float)1.3;
318 		else if (cl < (float)1.45)
319 		    cl = (float)1.4;
320 		else
321 		    cl = (float)1.5;
322 	    case 1:
323 		break;
324 	}
325 	{   /* HACK : gs_param_list_s::memory is documented in gsparam.h as
326 	       "for allocating coerced arrays". Not sure why zputdeviceparams
327 	       sets it to the current memory space, while the device
328 	       assumes to store them in the device's memory space.
329 	       As a hackish workaround we temporary replace it here.
330 	       Doing so because we don't want to change the global code now
331 	       because we're unable to test it with all devices.
332 	       Bug 688531 "Segmentation fault running pdfwrite from 219-01.ps".
333 
334 	       This solution to be reconsidered after fixing
335 	       the bug 688533 "zputdeviceparams specifies a wrong memory space.".
336  	    */
337 	    gs_memory_t *mem = plist->memory;
338 
339 	    plist->memory = pdev->pdf_memory;
340 	    code = gs_param_read_items(plist, pdev, pdf_param_items);
341 	    if (code < 0 ||
342 		(pdev->is_ps2write && (code = param_read_string(plist, "OPDFReadProcsetPath", &pdev->OPDFReadProcsetPath)) < 0) ||
343 		(!pdev->is_ps2write && (code = param_read_bool(plist, "ForOPDFRead", &pdev->ForOPDFRead)) < 0)
344 		);
345 	    plist->memory = mem;
346 	}
347 	if (code < 0)
348 	    ecode = code;
349 	{
350 	    /*
351 	     * Setting FirstObjectNumber is only legal if the file
352 	     * has just been opened and nothing has been written,
353 	     * or if we are setting it to the same value.
354 	     */
355 	    long fon = pdev->FirstObjectNumber;
356 
357 	    if (fon != save_dev->FirstObjectNumber) {
358 		if (fon <= 0 || fon > 0x7fff0000 ||
359 		    (pdev->next_id != 0 &&
360 		     pdev->next_id !=
361 		     save_dev->FirstObjectNumber + pdf_num_initial_ids)
362 		    ) {
363 		    ecode = gs_error_rangecheck;
364 		    param_signal_error(plist, "FirstObjectNumber", ecode);
365 		}
366 	    }
367 	}
368 	{
369 	    /*
370 	     * Set ProcessColorModel now, because gx_default_put_params checks
371 	     * it.
372 	     */
373 	    static const char *const pcm_names[] = {
374 		"DeviceGray", "DeviceRGB", "DeviceCMYK", "DeviceN", 0
375 	    };
376 	    int pcm = -1;
377 
378 	    ecode = param_put_enum(plist, "ProcessColorModel", &pcm,
379 				   pcm_names, ecode);
380 	    if (pcm >= 0) {
381 		pdf_set_process_color_model(pdev, pcm);
382 		pdf_set_initial_color(pdev, &pdev->saved_fill_color, &pdev->saved_stroke_color,
383 				&pdev->fill_used_process_color, &pdev->stroke_used_process_color);
384 	    }
385 	}
386     }
387     if (ecode < 0)
388 	goto fail;
389     /* PDFA and PDFX are stored in the page device dictionary and therefore
390      * set on every setpagedevice. However, if we have encountered a file which
391      * can't be made this way, and the PDFACompatibilityPolicy is 1, we want to
392      * continue producing the file, but not as a PDF/A or PDF/X file. Its more
393      * or less impossible to alter the setting in the (potentially saved) page
394      * device dictionary, so we use this rather clunky method.
395      */
396     if(pdev->PDFA && pdev->AbortPDFAX)
397 	pdev->PDFA = false;
398     if(pdev->PDFX && pdev->AbortPDFAX)
399 	pdev->PDFX = false;
400     if (pdev->PDFX && pdev->PDFA) {
401 	ecode = gs_note_error(gs_error_rangecheck);
402 	param_signal_error(plist, "PDFA", ecode);
403 	goto fail;
404     }
405     if (pdev->PDFX && pdev->ForOPDFRead) {
406 	ecode = gs_note_error(gs_error_rangecheck);
407 	param_signal_error(plist, "PDFX", ecode);
408 	goto fail;
409     }
410     if (pdev->PDFA && pdev->ForOPDFRead) {
411 	ecode = gs_note_error(gs_error_rangecheck);
412 	param_signal_error(plist, "PDFA", ecode);
413 	goto fail;
414     }
415     if (pdev->PDFA)
416 	 pdev->HaveTransparency = false;
417     /*
418      * We have to set version to the new value, because the set of
419      * legal parameter values for psdf_put_params varies according to
420      * the version.
421      */
422     if (pdev->PDFX)
423 	cl = (float)1.3; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
424     if (pdev->PDFA && cl < 1.4)
425 	cl = (float)1.4;
426     pdev->version = (cl < 1.2 ? psdf_version_level2 : psdf_version_ll3);
427     if (pdev->ForOPDFRead) {
428 	pdev->ResourcesBeforeUsage = true;
429 	pdev->HaveCFF = false;
430 	pdev->HavePDFWidths = false;
431 	pdev->HaveStrokeColor = false;
432 	cl = (float)1.2; /* Instead pdev->CompatibilityLevel = 1.2; - see below. */
433 	pdev->MaxInlineImageSize = max_long; /* Save printer's RAM from saving temporary image data.
434 					        Immediate images doen't need buffering. */
435 	pdev->version = psdf_version_level2;
436     } else {
437 	pdev->ResourcesBeforeUsage = false;
438 	pdev->HaveCFF = true;
439 	pdev->HavePDFWidths = true;
440 	pdev->HaveStrokeColor = true;
441     }
442     pdev->ParamCompatibilityLevel = cl;
443     ecode = gdev_psdf_put_params(dev, plist);
444     if (ecode < 0)
445 	goto fail;
446     if ((pdev->params.ColorConversionStrategy == ccs_CMYK &&
447 	 strcmp(pdev->color_info.cm_name, "DeviceCMYK")) ||
448 	(pdev->params.ColorConversionStrategy == ccs_sRGB &&
449 	  strcmp(pdev->color_info.cm_name, "DeviceRGB")) ||
450 	(pdev->params.ColorConversionStrategy == ccs_Gray &&
451 	  strcmp(pdev->color_info.cm_name, "DeviceGray"))) {
452 	eprintf("ColorConversionStrategy is incompatible to ProcessColorModel.\n");
453 	ecode = gs_note_error(gs_error_rangecheck);
454 	pdev->params.ColorConversionStrategy = save_ccs;
455     }
456     if (pdev->params.ColorConversionStrategy == ccs_UseDeviceIndependentColor) {
457 	if (!pdev->UseCIEColor) {
458 	    eprintf("Set UseCIEColor for UseDeviceIndependentColor to work properly.\n");
459 	    ecode = gs_note_error(gs_error_rangecheck);
460 	    pdev->UseCIEColor = true;
461 	}
462     }
463     if (pdev->params.ColorConversionStrategy == ccs_UseDeviceIndependentColorForImages) {
464 	if (!pdev->UseCIEColor) {
465 	    eprintf("UseDeviceDependentColorForImages is not supported. Use UseDeviceIndependentColor.\n");
466 	    pdev->params.ColorConversionStrategy = ccs_UseDeviceIndependentColor;
467 	    if (!pdev->UseCIEColor) {
468 		eprintf("Set UseCIEColor for UseDeviceIndependentColor to work properly.\n");
469 		ecode = gs_note_error(gs_error_rangecheck);
470 		pdev->UseCIEColor = true;
471 	    }
472 	}
473     }
474     if (pdev->params.ColorConversionStrategy == ccs_UseDeviceDependentColor) {
475 	if (!strcmp(pdev->color_info.cm_name, "DeviceCMYK")) {
476 	    eprintf("Replacing the deprecated device parameter value UseDeviceDependentColor with CMYK.\n");
477 	    pdev->params.ColorConversionStrategy = ccs_CMYK;
478 	} else if (!strcmp(pdev->color_info.cm_name, "DeviceRGB")) {
479 	    eprintf("Replacing the deprecated device parameter value UseDeviceDependentColor with sRGB.\n");
480 	    pdev->params.ColorConversionStrategy = ccs_sRGB;
481 	} else {
482 	    eprintf("Replacing the deprecated device parameter value UseDeviceDependentColor with Gray.\n");
483 	    pdev->params.ColorConversionStrategy = ccs_Gray;
484 	}
485     }
486     if (cl < 1.5 && pdev->params.ColorImage.Filter != NULL &&
487 	    !strcmp(pdev->params.ColorImage.Filter, "JPXEncode")) {
488 	eprintf("JPXEncode requires CompatibilityLevel >= 1.5 .\n");
489 	ecode = gs_note_error(gs_error_rangecheck);
490     }
491     if (cl < 1.5 && pdev->params.GrayImage.Filter != NULL &&
492 	    !strcmp(pdev->params.GrayImage.Filter, "JPXEncode")) {
493 	eprintf("JPXEncode requires CompatibilityLevel >= 1.5 .\n");
494 	ecode = gs_note_error(gs_error_rangecheck);
495     }
496     if (cl < 1.4  && pdev->params.MonoImage.Filter != NULL &&
497 	    !strcmp(pdev->params.MonoImage.Filter, "JBIG2Encode")) {
498 	eprintf("JBIG2Encode requires CompatibilityLevel >= 1.4 .\n");
499 	ecode = gs_note_error(gs_error_rangecheck);
500     }
501     if (pdev->HaveTrueTypes && pdev->version == psdf_version_level2) {
502 	pdev->version = psdf_version_level2_with_TT ;
503     }
504     /*
505      * Acrobat Reader doesn't handle user-space coordinates larger than
506      * MAX_USER_COORD.  To compensate for this, reduce the resolution so
507      * that the page size in device space (which we equate to user space) is
508      * significantly less than MAX_USER_COORD.  Note that this still does
509      * not protect us against input files that use coordinates far outside
510      * the page boundaries.
511      */
512 #define MAX_EXTENT ((int)(MAX_USER_COORD * 0.9))
513     /* Changing resolution or page size requires closing the device, */
514     if (dev->height > MAX_EXTENT || dev->width > MAX_EXTENT) {
515 	double factor =
516 	    max(dev->height / (double)MAX_EXTENT,
517 		dev->width / (double)MAX_EXTENT);
518 
519 	gx_device_set_resolution(dev, dev->HWResolution[0] / factor,
520 				 dev->HWResolution[1] / factor);
521     }
522 #undef MAX_EXTENT
523     if (pdev->FirstObjectNumber != save_dev->FirstObjectNumber) {
524 	if (pdev->xref.file != 0) {
525 	    fseek(pdev->xref.file, 0L, SEEK_SET);
526 	    pdf_initialize_ids(pdev);
527 	}
528     }
529     /* Handle the float/double mismatch. */
530     pdev->CompatibilityLevel = (int)(cl * 10 + 0.5) / 10.0;
531     return 0;
532  fail:
533     /* Restore all the parameters to their original state. */
534     pdev->version = save_dev->version;
535     pdf_set_process_color_model(pdev, save_dev->pcm_color_info_index);
536     pdev->saved_fill_color = save_dev->saved_fill_color;
537     pdev->saved_stroke_color = save_dev->saved_fill_color;
538     {
539 	const gs_param_item_t *ppi = pdf_param_items;
540 
541 	for (; ppi->key; ++ppi)
542 	    memcpy((char *)pdev + ppi->offset,
543 		   (char *)save_dev + ppi->offset,
544 		   gs_param_type_sizes[ppi->type]);
545 	pdev->ForOPDFRead = save_dev->ForOPDFRead;
546 	pdev->OPDFReadProcsetPath = save_dev->OPDFReadProcsetPath;
547     }
548     return ecode;
549 }
550 
551 /* Put parameters */
552 int
gdev_pdf_put_params(gx_device * dev,gs_param_list * plist)553 gdev_pdf_put_params(gx_device * dev, gs_param_list * plist)
554 {
555     int code;
556     gx_device_pdf *pdev = (gx_device_pdf *) dev;
557     gs_memory_t *mem = gs_memory_stable(pdev->memory);
558     gx_device_pdf *save_dev = gs_malloc(mem, sizeof(gx_device_pdf), 1,
559         "saved gx_device_pdf");
560 
561     if (!save_dev)
562         return_error(gs_error_VMerror);
563     memcpy(save_dev, pdev, sizeof(gx_device_pdf));
564     code = gdev_pdf_put_params_impl(dev, save_dev, plist);
565     gs_free(mem, save_dev, sizeof(gx_device_pdf), 1, "saved gx_device_pdf");
566     return code;
567 }
568 
569 /* ---------------- Process DSC comments ---------------- */
570 
571 static int
pdf_dsc_process(gx_device_pdf * pdev,const gs_param_string_array * pma)572 pdf_dsc_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
573 {
574     /*
575      * The Adobe "Distiller Parameters" documentation says that Distiller
576      * looks at DSC comments, but it doesn't say which ones.  We look at
577      * the ones that we see how to map directly to obvious PDF constructs.
578      */
579     int code = 0;
580     uint i;
581 
582     /*
583      * If ParseDSCComments is false, all DSC comments are ignored, even if
584      * ParseDSCComentsForDocInfo or PreserveEPSInfo is true.
585      */
586     if (!pdev->ParseDSCComments)
587 	return 0;
588 
589     for (i = 0; i + 1 < pma->size && code >= 0; i += 2) {
590 	const gs_param_string *pkey = &pma->data[i];
591 	const gs_param_string *pvalue = &pma->data[i + 1];
592 	const char *key;
593 	int code;
594 
595 	/*
596 	 * %%For, %%Creator, and %%Title are recognized only if either
597 	 * ParseDSCCommentsForDocInfo or PreserveEPSInfo is true.
598 	 * The other DSC comments are always recognized.
599 	 *
600 	 * Acrobat Distiller sets CreationDate and ModDate to the current
601 	 * time, not the value of %%CreationDate.  We think this is wrong,
602 	 * but we do the same -- we ignore %%CreationDate here.
603 	 */
604 
605 	if (pdf_key_eq(pkey, "Creator"))
606 	    key = "/Creator";
607 	else if (pdf_key_eq(pkey, "Title"))
608 	    key = "/Title";
609 	else if (pdf_key_eq(pkey, "For"))
610 	    key = "/Author";
611 	else {
612 	    pdf_page_dsc_info_t *ppdi;
613             char scan_buf[200]; /* arbitrary */
614 
615 	    if ((ppdi = &pdev->doc_dsc_info,
616 		 pdf_key_eq(pkey, "Orientation")) ||
617 		(ppdi = &pdev->page_dsc_info,
618 		 pdf_key_eq(pkey, "PageOrientation"))
619 		) {
620 		if (pvalue->size == 1 && pvalue->data[0] >= '0' &&
621 		    pvalue->data[0] <= '3'
622 		    )
623 		    ppdi->orientation = pvalue->data[0] - '0';
624 		else
625 		    ppdi->orientation = -1;
626 	    } else if ((ppdi = &pdev->doc_dsc_info,
627 			pdf_key_eq(pkey, "ViewingOrientation")) ||
628 		       (ppdi = &pdev->page_dsc_info,
629 			pdf_key_eq(pkey, "PageViewingOrientation"))
630 		       ) {
631 		gs_matrix mat;
632 		int orient;
633 
634 		if(pvalue->size >= sizeof(scan_buf) - 1)
635 		    continue;	/* error */
636                 memcpy(scan_buf, pvalue->data, pvalue->size);
637                 scan_buf[pvalue->size] = 0;
638                 if (sscanf(scan_buf, "[%g %g %g %g]",
639 			   &mat.xx, &mat.xy, &mat.yx, &mat.yy) != 4
640 		    )
641 		    continue;	/* error */
642 		for (orient = 0; orient < 4; ++orient) {
643 		    if (mat.xx == 1 && mat.xy == 0 && mat.yx == 0 && mat.yy == 1)
644 			break;
645 		    gs_matrix_rotate(&mat, -90.0, &mat);
646 		}
647 		if (orient == 4) /* error */
648 		    orient = -1;
649 		ppdi->viewing_orientation = orient;
650 	    } else {
651 		gs_rect box;
652 
653 		if (pdf_key_eq(pkey, "EPSF")) {
654 		    pdev->is_EPS = (pvalue->size >= 1 && pvalue->data[0] != '0');
655 		    continue;
656 		}
657 		/*
658 		 * We only parse the BoundingBox for the sake of
659 		 * AutoPositionEPSFiles.
660 		 */
661 		if (pdf_key_eq(pkey, "BoundingBox"))
662 		    ppdi = &pdev->doc_dsc_info;
663 		else if (pdf_key_eq(pkey, "PageBoundingBox"))
664 		    ppdi = &pdev->page_dsc_info;
665 		else
666 		    continue;
667 		if(pvalue->size >= sizeof(scan_buf) - 1)
668 		    continue;	/* error */
669                 memcpy(scan_buf, pvalue->data, pvalue->size);
670                 scan_buf[pvalue->size] = 0;
671 		if (sscanf(scan_buf, "[%lg %lg %lg %lg]",
672 			   &box.p.x, &box.p.y, &box.q.x, &box.q.y) != 4
673 		    )
674 		    continue;	/* error */
675 		ppdi->bounding_box = box;
676 	    }
677 	    continue;
678 	}
679 
680 	if (pdev->ParseDSCCommentsForDocInfo || pdev->PreserveEPSInfo)
681 	    code = cos_dict_put_c_key_string(pdev->Info, key,
682 					     pvalue->data, pvalue->size);
683     }
684     return code;
685 }
686