2
Copyright (c) 2008, Till Kamppeter
3
Copyright (c) 2008, BBR Inc. All rights reserved.
5
Permission is hereby granted, free of charge, to any person obtaining
6
a copy of this software and associated documentation files (the
7
"Software"), to deal in the Software without restriction, including
8
without limitation the rights to use, copy, modify, merge, publish,
9
distribute, sublicense, and/or sell copies of the Software, and to
10
permit persons to whom the Software is furnished to do so, subject to
11
the following conditions:
13
The above copyright notice and this permission notice shall be included
14
in all copies or substantial portions of the Software.
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
MIT Open Source License - http://www.opensource.org/
28
/* $Id: pdftoraster.c 9171 2008-10-17 22:58:21Z giles $ */
30
/* PDF to CUPS Raster filter based on Ghostscript */
34
#include <cups/cups.h>
37
#include <cups/raster.h>
39
#define MAX_CHECK_COMMENT_LINES 20
44
#define BINDIR "/usr/bin"
47
#define CUPS_FONTPATH "/usr/share/cups/fonts"
52
cups_bool_t deviceCollate = CUPS_FALSE;
53
#ifdef CUPS_RASTER_SYNCv1
54
cups_page_header2_t h;
57
#endif /* CUPS_RASTER_SYNCv1 */
59
cups_option_t *options = 0;
61
void parseOpts(int argc, char **argv)
65
if (argc < 6 || argc > 7) {
66
fprintf(stderr, "ERROR: %s job-id user title copies options [file]",
71
ppd = ppdOpenFile(getenv("PPD"));
74
num_options = cupsParseOptions(argv[5],0,&options);
75
cupsMarkOptions(ppd,num_options,options);
76
cupsRasterInterpretPPD(&h,ppd,num_options,options,0);
79
void parsePDFTOPDFComment(FILE *fp)
84
/* skip until PDF start header */
85
while (fgets(buf,sizeof(buf),fp) != 0) {
86
if (strncmp(buf,"%PDF",4) == 0) {
90
for (i = 0;i < MAX_CHECK_COMMENT_LINES;i++) {
91
if (fgets(buf,sizeof(buf),fp) == 0) break;
92
if (strncmp(buf,"%%PDFTOPDFNumCopies",19) == 0) {
95
p = strchr(buf+19,':');
96
deviceCopies = atoi(p+1);
97
} else if (strncmp(buf,"%%PDFTOPDFCollate",17) == 0) {
100
p = strchr(buf+17,':');
101
while (*p == ' ' || *p == '\t') p++;
102
if (strncasecmp(p,"true",4) == 0) {
103
deviceCollate = CUPS_TRUE;
105
deviceCollate = CUPS_FALSE;
111
int main(int argc, char *argv[], char *envp[]) {
114
cups_array_t *gs_args;
116
const char *t = NULL;
127
parseOpts(argc, argv);
132
fd = cupsTempFd(buf,BUFSIZ);
134
fprintf(stderr, "ERROR: Can't create temporary file");
140
/* copy stdin to the tmp file */
141
while ((n = read(0,buf,BUFSIZ)) > 0) {
142
if (write(fd,buf,n) != n) {
143
fprintf(stderr, "ERROR: Can't copy stdin to temporary file");
148
if (lseek(fd,0,SEEK_SET) < 0) {
149
fprintf(stderr, "ERROR: Can't rewind temporary file");
154
if ((fp = fdopen(fd,"rb")) == 0) {
155
fprintf(stderr, "ERROR: Can't fdopen temporary file");
160
/* argc == 7 filenmae is specified */
162
if ((fp = fopen(argv[6],"rb")) == 0) {
163
fprintf(stderr, "ERROR: Can't open input file %s",argv[6]);
167
parsePDFTOPDFComment(fp);
170
/* Fix NumCopies and Collate according to PDFTOPDFComments */
171
h.NumCopies = deviceCopies;
172
h.Collate = deviceCollate;
173
/* fixed other values that pdftopdf handles */
174
h.MirrorPrint = CUPS_FALSE;
175
h.Orientation = CUPS_ORIENT_0;
177
/* Ghostscript parameters */
178
gs_args = cupsArrayNew(NULL, NULL);
181
fprintf(stderr, "ERROR: Unable to allocate memory for Ghostscript arguments array\n");
185
/* Part of Ghostscript command line which is not dependent on the job and/or
187
snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS);
188
cupsArrayAdd(gs_args, strdup(tmpstr));
189
cupsArrayAdd(gs_args, strdup("-dQUIET"));
190
/*cupsArrayAdd(gs_args, strdup("-dDEBUG"));*/
191
cupsArrayAdd(gs_args, strdup("-dPARANOIDSAFER"));
192
cupsArrayAdd(gs_args, strdup("-dNOPAUSE"));
193
cupsArrayAdd(gs_args, strdup("-dBATCH"));
194
/* cupsArrayAdd(gs_args, strdup("-dNOMEDIAATTRS")); */
195
cupsArrayAdd(gs_args, strdup("-sDEVICE=cups"));
196
cupsArrayAdd(gs_args, strdup("-sstdout=%stderr"));
197
cupsArrayAdd(gs_args, strdup("-sOutputFile=%stdout"));
200
if ((t = getenv("CUPS_FONTPATH")) == NULL)
202
snprintf(tmpstr, sizeof(tmpstr), "-I%s", t);
203
cupsArrayAdd(gs_args, strdup(tmpstr));
205
/* Simple boolean, enumerated choice, numerical, and string parameters */
206
if (h.MediaClass[0] |= '\0') {
207
snprintf(tmpstr, sizeof(tmpstr), "-sMediaClass=%s", h.MediaClass);
208
cupsArrayAdd(gs_args, strdup(tmpstr));
210
if (h.MediaColor[0] |= '\0') {
211
snprintf(tmpstr, sizeof(tmpstr), "-sMediaColor=%s", h.MediaColor);
212
cupsArrayAdd(gs_args, strdup(tmpstr));
214
if (h.MediaType[0] |= '\0') {
215
snprintf(tmpstr, sizeof(tmpstr), "-sMediaType=%s", h.MediaType);
216
cupsArrayAdd(gs_args, strdup(tmpstr));
218
if (h.OutputType[0] |= '\0') {
219
snprintf(tmpstr, sizeof(tmpstr), "-sOutputType=%s", h.OutputType);
220
cupsArrayAdd(gs_args, strdup(tmpstr));
222
if (h.AdvanceDistance) {
223
snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceDistance=%d",
224
(unsigned)(h.AdvanceDistance));
225
cupsArrayAdd(gs_args, strdup(tmpstr));
227
if (h.AdvanceMedia) {
228
snprintf(tmpstr, sizeof(tmpstr), "-dAdvanceMedia=%d",
229
(unsigned)(h.AdvanceMedia));
230
cupsArrayAdd(gs_args, strdup(tmpstr));
233
cupsArrayAdd(gs_args, strdup("-dCollate"));
236
snprintf(tmpstr, sizeof(tmpstr), "-dCutMedia=%d",
237
(unsigned)(h.CutMedia));
238
cupsArrayAdd(gs_args, strdup(tmpstr));
241
cupsArrayAdd(gs_args, strdup("-dDuplex"));
243
if ((h.HWResolution[0] != 100) || (h.HWResolution[1] != 100))
244
snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d",
245
(unsigned)(h.HWResolution[0]), (unsigned)(h.HWResolution[1]));
247
snprintf(tmpstr, sizeof(tmpstr), "-r100x100");
248
cupsArrayAdd(gs_args, strdup(tmpstr));
250
cupsArrayAdd(gs_args, strdup("-dInsertSheet"));
253
snprintf(tmpstr, sizeof(tmpstr), "-dJog=%d",
255
cupsArrayAdd(gs_args, strdup(tmpstr));
258
snprintf(tmpstr, sizeof(tmpstr), "-dLeadingEdge=%d",
259
(unsigned)(h.LeadingEdge));
260
cupsArrayAdd(gs_args, strdup(tmpstr));
263
cupsArrayAdd(gs_args, strdup("-dManualFeed"));
265
if (h.MediaPosition) {
266
snprintf(tmpstr, sizeof(tmpstr), "-dMediaPosition=%d",
267
(unsigned)(h.MediaPosition));
268
cupsArrayAdd(gs_args, strdup(tmpstr));
271
snprintf(tmpstr, sizeof(tmpstr), "-dMediaWeight=%d",
272
(unsigned)(h.MediaWeight));
273
cupsArrayAdd(gs_args, strdup(tmpstr));
276
cupsArrayAdd(gs_args, strdup("-dMirrorPrint"));
278
if (h.NegativePrint) {
279
cupsArrayAdd(gs_args, strdup("-dNegativePrint"));
281
if (h.NumCopies != 1) {
282
snprintf(tmpstr, sizeof(tmpstr), "-dNumCopies=%d",
283
(unsigned)(h.NumCopies));
284
cupsArrayAdd(gs_args, strdup(tmpstr));
287
snprintf(tmpstr, sizeof(tmpstr), "-dOrientation=%d",
288
(unsigned)(h.Orientation));
289
cupsArrayAdd(gs_args, strdup(tmpstr));
291
if (h.OutputFaceUp) {
292
cupsArrayAdd(gs_args, strdup("-dOutputFaceUp"));
294
if (h.PageSize[0] != 612)
295
snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEWIDTHPOINTS=%d",
296
(unsigned)(h.PageSize[0]));
298
snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEWIDTHPOINTS=612");
299
cupsArrayAdd(gs_args, strdup(tmpstr));
300
if (h.PageSize[1] != 792)
301
snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEHEIGHTPOINTS=%d",
302
(unsigned)(h.PageSize[1]));
304
snprintf(tmpstr, sizeof(tmpstr), "-dDEVICEHEIGHTPOINTS=792");
305
cupsArrayAdd(gs_args, strdup(tmpstr));
307
cupsArrayAdd(gs_args, strdup("-dSeparations"));
310
cupsArrayAdd(gs_args, strdup("-dTraySwitch"));
313
cupsArrayAdd(gs_args, strdup("-dTumble"));
315
if (h.cupsMediaType) {
316
snprintf(tmpstr, sizeof(tmpstr), "-dcupsMediaType=%d",
317
(unsigned)(h.cupsMediaType));
318
cupsArrayAdd(gs_args, strdup(tmpstr));
320
if (h.cupsBitsPerColor != 1)
321
snprintf(tmpstr, sizeof(tmpstr), "-dcupsBitsPerColor=%d",
322
(unsigned)(h.cupsBitsPerColor));
324
snprintf(tmpstr, sizeof(tmpstr), "-dcupsBitsPerColor=1");
325
cupsArrayAdd(gs_args, strdup(tmpstr));
326
if (h.cupsColorOrder != CUPS_ORDER_CHUNKED)
327
snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorOrder=%d",
328
(unsigned)(h.cupsColorOrder));
330
snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorOrder=%d",
332
cupsArrayAdd(gs_args, strdup(tmpstr));
333
if (h.cupsColorSpace != CUPS_CSPACE_K)
334
snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorSpace=%d",
335
(unsigned)(h.cupsColorSpace));
337
snprintf(tmpstr, sizeof(tmpstr), "-dcupsColorSpace=%d",
339
cupsArrayAdd(gs_args, strdup(tmpstr));
340
if (h.cupsCompression) {
341
snprintf(tmpstr, sizeof(tmpstr), "-dcupsCompression=%d",
342
(unsigned)(h.cupsCompression));
343
cupsArrayAdd(gs_args, strdup(tmpstr));
345
if (h.cupsRowCount) {
346
snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowCount=%d",
347
(unsigned)(h.cupsRowCount));
348
cupsArrayAdd(gs_args, strdup(tmpstr));
351
snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowFeed=%d",
352
(unsigned)(h.cupsRowFeed));
353
cupsArrayAdd(gs_args, strdup(tmpstr));
356
snprintf(tmpstr, sizeof(tmpstr), "-dcupsRowStep=%d",
357
(unsigned)(h.cupsRowStep));
358
cupsArrayAdd(gs_args, strdup(tmpstr));
360
#ifdef CUPS_RASTER_SYNCv1
361
if (h.cupsBorderlessScalingFactor != 1.0f) {
362
snprintf(tmpstr, sizeof(tmpstr), "-dcupsBorderlessScalingFactor=%.4f",
363
h.cupsBorderlessScalingFactor);
364
cupsArrayAdd(gs_args, strdup(tmpstr));
366
for (i=0; i <= 15; i ++)
367
if (h.cupsInteger[i]) {
368
snprintf(tmpstr, sizeof(tmpstr), "-dcupsInteger%d=%d",
369
i, (unsigned)(h.cupsInteger[i]));
370
cupsArrayAdd(gs_args, strdup(tmpstr));
372
for (i=0; i <= 15; i ++)
374
snprintf(tmpstr, sizeof(tmpstr), "-dcupsReal%d=%.4f",
376
cupsArrayAdd(gs_args, strdup(tmpstr));
378
for (i=0; i <= 15; i ++)
379
if (h.cupsString[i][0] != '\0') {
380
snprintf(tmpstr, sizeof(tmpstr), "-scupsString%d=%s",
382
cupsArrayAdd(gs_args, strdup(tmpstr));
384
if (h.cupsMarkerType[0] != '\0') {
385
snprintf(tmpstr, sizeof(tmpstr), "-scupsMarkerType=%s",
387
cupsArrayAdd(gs_args, strdup(tmpstr));
389
if (h.cupsRenderingIntent[0] != '\0') {
390
snprintf(tmpstr, sizeof(tmpstr), "-scupsRenderingIntent=%s",
391
h.cupsRenderingIntent);
392
cupsArrayAdd(gs_args, strdup(tmpstr));
394
if (h.cupsPageSizeName[0] != '\0') {
395
snprintf(tmpstr, sizeof(tmpstr), "-scupsPageSizeName=%s",
397
cupsArrayAdd(gs_args, strdup(tmpstr));
399
#endif /* CUPS_RASTER_SYNCv1 */
401
/* Parameters of array type. They cannot be supplied as "-d" or "-s"
402
command line options. We use "-c" here to supply them as
403
PostScript commands */
405
/* Switch to taking PostScript commands on the Ghostscript command line */
406
cupsArrayAdd(gs_args, "-c");
408
if ((t = cupsGetOption("profile", num_options, options)) != NULL) {
409
snprintf(tmpstr, sizeof(tmpstr), "<</cupsProfile(%s)>>setpagedevice", t);
410
cupsArrayAdd(gs_args, strdup(tmpstr));
413
/* Mark the end of PostScript commands supplied on the Ghostscript command
414
line (with the "-c" option), so that we can supply the input file name */
415
cupsArrayAdd(gs_args, strdup("-f"));
417
/* Let Ghostscript read from STDIN */
418
cupsArrayAdd(gs_args, strdup("-_"));
420
/* Put Ghostscript command line argument into an array for the "exec()"
422
numargs = cupsArrayCount(gs_args);
423
gsargv = calloc(numargs + 1, sizeof(char *));
424
for (argument = (char *)cupsArrayFirst(gs_args), i = 0; argument;
425
argument = (char *)cupsArrayNext(gs_args), i++) {
426
gsargv[i] = argument;
430
/* Debug output: Full Ghostscript command line and environment variables */
431
fprintf(stderr, "DEBUG: Ghostscript command line:");
432
for (i = 0; gsargv[i]; i ++) {
433
if ((strchr(gsargv[i],' ')) || (strchr(gsargv[i],'\t')))
437
fprintf(stderr, " %s%s%s", apos, gsargv[i], apos);
439
fprintf(stderr, "\n");
441
for (i = 0; envp[i]; i ++)
442
fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]);
444
/* Create a pipe for feeding the job into Ghostscript */
449
fprintf(stderr, "ERROR: Unable to establish pipe for Ghostscript call");
453
/* Set the "close on exec" flag on each end of the pipe... */
454
if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
460
fprintf(stderr, "ERROR: Unable to set \"close on exec\" flag on read end of the pipe for Ghostscript call");
463
if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
467
fprintf(stderr, "ERROR: Unable to set \"close on exec\" flag on write end of the pipe for Ghostscript call");
471
if ((pid = fork()) == 0)
473
/* Ghostscript child process */
475
/* Couple pipe with STDIN of Ghostscript process */
481
fprintf(stderr, "ERROR: Unable to couple pipe with STDIN of Ghostscript process");
486
/* Execute Ghostscript command line ... */
487
snprintf(tmpstr, sizeof(tmpstr), "%s/%s", BINDIR, GS);
488
execve(tmpstr, gsargv, envp);
496
/* Feed job data into Ghostscript */
497
while ((n = fread(buf, 1, BUFSIZ, fp)) > 0) {
498
if (write(fds[1], buf, n) != n) {
499
fprintf(stderr, "ERROR: Can't feed job data into Ghostscript");