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