1 /* Copyright (C) 1996, 2000, 2001 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gdevpdfp.c,v 1.19.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */
20 /* Get/put parameters for PDF-writing driver */
21 #include "memory_.h"
22 #include "string_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gdevpdfx.h"
26 #include "gdevpdfo.h"
27 #include "gsparamx.h"
28 
29 /*
30  * The pdfwrite device supports the following "real" parameters:
31  *      OutputFile <string>
32  *      all the Distiller parameters (also see gdevpsdp.c)
33  * Only some of the Distiller parameters actually have any effect.
34  *
35  * The device also supports the following write-only pseudo-parameters that
36  * serve only to communicate other information from the PostScript file.
37  * Their "value" is an array of strings, some of which may be the result
38  * of converting arbitrary PostScript objects to string form.
39  *      pdfmark - see gdevpdfm.c
40  *	DSC - processed in this file
41  */
42 private int pdf_dsc_process(P2(gx_device_pdf * pdev,
43 			       const gs_param_string_array * pma));
44 
45 private const int CoreDistVersion = 4000;	/* Distiller 4.0 */
46 private const gs_param_item_t pdf_param_items[] = {
47 #define pi(key, type, memb) { key, type, offset_of(gx_device_pdf, memb) }
48 
49 	/* Acrobat Distiller 4 parameters */
50 
51     /*
52      * EndPage and StartPage are renamed because EndPage collides with
53      * a page device parameter.
54      */
55     pi("PDFEndPage", gs_param_type_int, EndPage),
56     pi("PDFStartPage", gs_param_type_int, StartPage),
57     pi("Optimize", gs_param_type_bool, Optimize),
58     pi("ParseDSCCommentsForDocInfo", gs_param_type_bool,
59        ParseDSCCommentsForDocInfo),
60     pi("ParseDSCComments", gs_param_type_bool, ParseDSCComments),
61     pi("EmitDSCWarnings", gs_param_type_bool, EmitDSCWarnings),
62     pi("CreateJobTicket", gs_param_type_bool, CreateJobTicket),
63     pi("PreserveEPSInfo", gs_param_type_bool, PreserveEPSInfo),
64     pi("AutoPositionEPSFiles", gs_param_type_bool, AutoPositionEPSFiles),
65     pi("PreserveCopyPage", gs_param_type_bool, PreserveCopyPage),
66     pi("UsePrologue", gs_param_type_bool, UsePrologue),
67 
68 	/* Ghostscript-specific parameters */
69 
70     pi("ReAssignCharacters", gs_param_type_bool, ReAssignCharacters),
71     pi("ReEncodeCharacters", gs_param_type_bool, ReEncodeCharacters),
72     pi("FirstObjectNumber", gs_param_type_long, FirstObjectNumber),
73 #undef pi
74     gs_param_item_end
75 };
76 
77 /*
78   Notes on implementing the remaining Distiller functionality
79   ===========================================================
80 
81   Architectural issues
82   --------------------
83 
84   Must disable all color conversions, so that driver gets original color
85     and color space -- needs "protean" device color space
86   Must optionally disable application of TR, BG, UCR similarly.  Affects:
87     PreserveHalftoneInfo
88     PreserveOverprintSettings
89     TransferFunctionInfo
90     UCRandBGInfo
91 
92   * = requires architectural change to complete
93 
94   Current limitations
95   -------------------
96 
97   Non-primary elements in HalftoneType 5 are not written correctly
98   Doesn't recognize Default TR/HT/BG/UCR
99   Optimization is a separate program
100 
101   Optimizations
102   -------------
103 
104   Create shared resources for Indexed (and other) color spaces
105   Remember image XObject IDs for sharing
106   Remember image and pattern MD5 fingerprints for sharing -- see
107     CD-ROM from dhoff@margnat.com
108   Merge font subsets?  (k/ricktest.ps, from rick@dgii.com re file output
109     size ps2pdf vs. pstoedit)
110   Minimize tables for embedded TT fonts (requires renumbering glyphs)
111   Clip off image data outside bbox of clip path?
112 
113   Acrobat Distiller 3
114   -------------------
115 
116   ---- Other functionality ----
117 
118   Compress forms, Type 3 fonts, and Cos streams
119 
120   ---- Image parameters ----
121 
122   AntiAlias{Color,Gray,Mono}Images
123   AutoFilter{Color,Gray}Images
124     Needs to scan image
125   Convert CIE images to Device if can't represent color space
126 
127   ---- Other parameters ----
128 
129   CompressPages
130     Compress things other than page contents
131   * PreserveHalftoneInfo
132   PreserveOPIComments
133     ? see OPI spec?
134   * PreserveOverprintSettings
135   * TransferFunctionInfo
136   * UCRandBGInfo
137   ColorConversionStrategy
138     Select color space for drawing commands
139   ConvertImagesToIndexed
140     Postprocess image data *after* downsampling (requires an extra pass)
141 
142   Acrobat Distiller 4
143   -------------------
144 
145   ---- Other functionality ----
146 
147   Document structure pdfmarks
148 
149   ---- Parameters ----
150 
151   xxxDownsampleType = /Bicubic
152     Add new filter (or use siscale?) & to setup (gdevpsdi.c)
153   Binding
154     ? not sure where this goes (check with AD4)
155   DetectBlends
156     Idiom recognition?  PatternType 2 patterns / shfill?  (see AD4)
157   DoThumbnails
158     Also output to memory device -- resolution issue
159   EndPage / StartPage
160     Only affects AR? -- see what AD4 produces
161   ###Profile
162     Output in ICCBased color spaces
163   ColorConversionStrategy
164   * Requires suppressing CIE => Device color conversion
165     Convert other CIE spaces to ICCBased
166   CannotEmbedFontPolicy
167     Check when trying to embed font -- how to produce warning?
168 
169   ---- Job-level control ----
170 
171   EmitDSCWarnings
172     Require DSC parser / interceptor
173   CreateJobTicket
174     ?
175   PreserveEPSInfo
176   AutoPositionEPSFiles
177     Require DSC parsing
178   PreserveCopyPage
179     Concatenate Contents streams
180   UsePrologue
181     Needs hack in top-level control?
182 
183 */
184 
185 /* ---------------- Get parameters ---------------- */
186 
187 /* Get parameters. */
188 int
gdev_pdf_get_params(gx_device * dev,gs_param_list * plist)189 gdev_pdf_get_params(gx_device * dev, gs_param_list * plist)
190 {
191     gx_device_pdf *pdev = (gx_device_pdf *) dev;
192     float cl = (float)pdev->CompatibilityLevel;
193     int code = gdev_psdf_get_params(dev, plist);
194     int cdv = CoreDistVersion;
195 
196     if (code < 0 ||
197 	(code = param_write_int(plist, "CoreDistVersion", &cdv)) < 0 ||
198 	(code = param_write_float(plist, "CompatibilityLevel", &cl)) < 0 ||
199 	/* Indicate that we can process pdfmark and DSC. */
200 	(param_requested(plist, "pdfmark") > 0 &&
201 	 (code = param_write_null(plist, "pdfmark")) < 0) ||
202 	(param_requested(plist, "DSC") > 0 &&
203 	 (code = param_write_null(plist, "DSC")) < 0) ||
204 	(code = gs_param_write_items(plist, pdev, NULL, pdf_param_items)) < 0
205 	);
206     return code;
207 }
208 
209 /* ---------------- Put parameters ---------------- */
210 
211 /* Put parameters. */
212 int
gdev_pdf_put_params(gx_device * dev,gs_param_list * plist)213 gdev_pdf_put_params(gx_device * dev, gs_param_list * plist)
214 {
215     gx_device_pdf *pdev = (gx_device_pdf *) dev;
216     int ecode, code;
217     gx_device_pdf save_dev;
218     float cl = (float)pdev->CompatibilityLevel;
219     bool locked = pdev->params.LockDistillerParams;
220     gs_param_name param_name;
221 
222     /*
223      * If this is a pseudo-parameter (pdfmark or DSC),
224      * don't bother checking for any real ones.
225      */
226 
227     {
228 	gs_param_string_array ppa;
229 
230 	code = param_read_string_array(plist, (param_name = "pdfmark"), &ppa);
231 	switch (code) {
232 	    case 0:
233 		pdf_open_document(pdev);
234 		code = pdfmark_process(pdev, &ppa);
235 		if (code >= 0)
236 		    return code;
237 		/* falls through for errors */
238 	    default:
239 		param_signal_error(plist, param_name, code);
240 		return code;
241 	    case 1:
242 		break;
243 	}
244 
245 	code = param_read_string_array(plist, (param_name = "DSC"), &ppa);
246 	switch (code) {
247 	    case 0:
248 		pdf_open_document(pdev);
249 		code = pdf_dsc_process(pdev, &ppa);
250 		if (code >= 0)
251 		    return code;
252 		/* falls through for errors */
253 	    default:
254 		param_signal_error(plist, param_name, code);
255 		return code;
256 	    case 1:
257 		break;
258 	}
259     }
260 
261     /* Check for LockDistillerParams before doing anything else. */
262 
263     ecode = code = param_read_bool(plist, "LockDistillerParams", &locked);
264     if (locked && pdev->params.LockDistillerParams)
265 	return ecode;
266 
267     /* General parameters. */
268 
269     {
270 	int cdv = CoreDistVersion;
271 
272 	ecode = param_put_int(plist, (param_name = "CoreDistVersion"), &cdv, ecode);
273 	if (cdv != CoreDistVersion)
274 	    param_signal_error(plist, param_name, ecode = gs_error_rangecheck);
275     }
276 
277     save_dev = *pdev;
278 
279     switch (code = param_read_float(plist, (param_name = "CompatibilityLevel"), &cl)) {
280 	default:
281 	    ecode = code;
282 	    param_signal_error(plist, param_name, ecode);
283 	case 0:
284 	case 1:
285 	    break;
286     }
287 
288     code = gs_param_read_items(plist, pdev, pdf_param_items);
289     if (code < 0)
290 	ecode = code;
291     {
292 	/*
293 	 * Setting FirstObjectNumber is only legal if the file
294 	 * has just been opened and nothing has been written,
295 	 * or if we are setting it to the same value.
296 	 */
297 	long fon = pdev->FirstObjectNumber;
298 
299 	if (fon != save_dev.FirstObjectNumber) {
300 	    if (fon <= 0 || fon > 0x7fff0000 ||
301 		(pdev->next_id != 0 &&
302 		 pdev->next_id !=
303 		 save_dev.FirstObjectNumber + pdf_num_initial_ids)
304 		) {
305 		ecode = gs_error_rangecheck;
306 		param_signal_error(plist, "FirstObjectNumber", ecode);
307 	    }
308 	}
309     }
310     {
311 	/*
312 	 * Set ProcessColorModel now, because gx_default_put_params checks
313 	 * it.
314 	 */
315 	static const char *const pcm_names[] = {
316 	    "DeviceGray", "DeviceRGB", "DeviceCMYK", 0
317 	};
318 	static const gx_device_color_info pcm_color_info[] = {
319 	    dci_values(1, 8, 255, 0, 256, 0),
320 	    dci_values(3, 24, 255, 255, 256, 256),
321 	    dci_values(4, 32, 255, 255, 256, 256)
322 	};
323 	int pcm = -1;
324 
325 	ecode = param_put_enum(plist, "ProcessColorModel", &pcm,
326 			       pcm_names, ecode);
327 	if (pcm >= 0) {
328 	    pdev->color_info = pcm_color_info[pcm];
329 	    pdf_set_process_color_model(pdev);
330 	}
331     }
332     if (ecode < 0)
333 	goto fail;
334     /*
335      * We have to set version to the new value, because the set of
336      * legal parameter values for psdf_put_params varies according to
337      * the version.
338      */
339     pdev->version = (cl < 1.2 ? psdf_version_level2 : psdf_version_ll3);
340     ecode = gdev_psdf_put_params(dev, plist);
341     if (ecode < 0)
342 	goto fail;
343     /*
344      * Acrobat Reader doesn't handle user-space coordinates larger than
345      * MAX_USER_COORD.  To compensate for this, reduce the resolution so
346      * that the page size in device space (which we equate to user space) is
347      * significantly less than MAX_USER_COORD.  Note that this still does
348      * not protect us against input files that use coordinates far outside
349      * the page boundaries.
350      */
351 #define MAX_EXTENT ((int)(MAX_USER_COORD * 0.9))
352     /* Changing resolution or page size requires closing the device, */
353     if (dev->height > MAX_EXTENT || dev->width > MAX_EXTENT) {
354 	double factor =
355 	    max(dev->height / (double)MAX_EXTENT,
356 		dev->width / (double)MAX_EXTENT);
357 
358 	if (dev->is_open)
359 	    gs_closedevice(dev);
360 	gx_device_set_resolution(dev, dev->HWResolution[0] / factor,
361 				 dev->HWResolution[1] / factor);
362     }
363 #undef MAX_EXTENT
364     if (pdev->FirstObjectNumber != save_dev.FirstObjectNumber) {
365 	if (pdev->xref.file != 0) {
366 	    fseek(pdev->xref.file, 0L, SEEK_SET);
367 	    pdf_initialize_ids(pdev);
368 	}
369     }
370     /* Handle the float/double mismatch. */
371     pdev->CompatibilityLevel = (int)(cl * 10 + 0.5) / 10.0;
372     return 0;
373  fail:
374     /* Restore all the parameters to their original state. */
375     pdev->version = save_dev.version;
376     pdev->color_info = save_dev.color_info;
377     pdf_set_process_color_model(pdev);
378     {
379 	const gs_param_item_t *ppi = pdf_param_items;
380 
381 	for (; ppi->key; ++ppi)
382 	    memcpy((char *)pdev + ppi->offset,
383 		   (char *)&save_dev + ppi->offset,
384 		   gs_param_type_sizes[ppi->type]);
385     }
386     return ecode;
387 }
388 
389 /* ---------------- Process DSC comments ---------------- */
390 
391 private int
pdf_dsc_process(gx_device_pdf * pdev,const gs_param_string_array * pma)392 pdf_dsc_process(gx_device_pdf * pdev, const gs_param_string_array * pma)
393 {
394     /*
395      * The Adobe "Distiller Parameters" documentation says that Distiller
396      * looks at DSC comments, but it doesn't say which ones.  We look at
397      * the ones that we see how to map directly to obvious PDF constructs.
398      */
399     int code = 0;
400     int i;
401 
402     for (i = 0; i + 1 < pma->size && code >= 0; i += 2) {
403 	const gs_param_string *pkey = &pma->data[i];
404 	const gs_param_string *pvalue = &pma->data[i + 1];
405 	const char *key;
406 	int code;
407 
408 	if (pdf_key_eq(pkey, "Creator"))
409 	    key = "/Creator";
410 	else if (pdf_key_eq(pkey, "CreationDate"))
411 	    key = "/CreationDate";
412 	else if (pdf_key_eq(pkey, "Title"))
413 	    key = "/Title";
414 	else if (pdf_key_eq(pkey, "For"))
415 	    key = "/Author";
416 	else {
417 	    pdf_page_dsc_info_t *ppdi;
418 
419 	    if (!pdev->ParseDSCComments)
420 		continue;
421 	    if ((ppdi = &pdev->doc_dsc_info,
422 		 pdf_key_eq(pkey, "Orientation")) ||
423 		(ppdi = &pdev->page_dsc_info,
424 		 pdf_key_eq(pkey, "PageOrientation"))
425 		) {
426 		if (pvalue->size == 1 && pvalue->data[0] >= '0' &&
427 		    pvalue->data[0] <= '3'
428 		    )
429 		    ppdi->orientation = pvalue->data[0] - '0';
430 		else
431 		    ppdi->orientation = -1;
432 	    } else if ((ppdi = &pdev->doc_dsc_info,
433 			pdf_key_eq(pkey, "ViewingOrientation")) ||
434 		       (ppdi = &pdev->page_dsc_info,
435 			pdf_key_eq(pkey, "PageViewingOrientation"))
436 		       ) {
437 		gs_matrix mat;
438 		int orient;
439 
440 		if (sscanf((const char *)pvalue->data, "[%g %g %g %g]",
441 			   &mat.xx, &mat.xy, &mat.yx, &mat.yy) != 4
442 		    )
443 		    continue;	/* error */
444 		for (orient = 0; orient < 4; ++orient) {
445 		    if (mat.xx == 1 && mat.xy == 0 && mat.yx == 0 && mat.yy == 1)
446 			break;
447 		    gs_matrix_rotate(&mat, -90.0, &mat);
448 		}
449 		if (orient == 4) /* error */
450 		    orient = -1;
451 		ppdi->viewing_orientation = orient;
452 	    } else {
453 		gs_rect box;
454 
455 		if (pdf_key_eq(pkey, "EPSF")) {
456 		    pdev->is_EPS = (pkey->size >= 1 && pkey->data[0] != '0');
457 		    continue;
458 		}
459 		/*
460 		 *
461 		 * We only parse the BoundingBox for the sake of
462 		 * AutoPositionEPSFiles.
463 		 */
464 		if (pdf_key_eq(pkey, "BoundingBox"))
465 		    ppdi = &pdev->doc_dsc_info;
466 		else if (pdf_key_eq(pkey, "PageBoundingBox"))
467 		    ppdi = &pdev->page_dsc_info;
468 		else
469 		    continue;
470 		if (sscanf((const char *)pvalue->data, "[%lg %lg %lg %lg]",
471 			   &box.p.x, &box.p.y, &box.q.x, &box.q.y) != 4
472 		    )
473 		    continue;	/* error */
474 		ppdi->bounding_box = box;
475 		continue;
476 	    }
477 	    continue;
478 	}
479 	if (pdev->ParseDSCCommentsForDocInfo)
480 	    code = cos_dict_put_c_key_string(pdev->Info, key,
481 					     pvalue->data, pvalue->size);
482     }
483     return code;
484 }
485