• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 
3 Copyright (c) 2016, Pranjal Bhor
4 Copyright (c) 2008-2016, Till Kamppeter
5 Copyright (c) 2011, Tim Waugh
6 Copyright (c) 2011-2013, Richard Hughes
7 
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15 
16 The above copyright notice and this permission notice shall be included
17 in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 
27 MIT Open Source License  -  http://www.opensource.org/
28 
29 */
30 
31 
32 /* PS/PDF to CUPS Raster filter based on mutool */
33 
34 #include <config.h>
35 #include <cups/cups.h>
36 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
37 #define HAVE_CUPS_1_7 1
38 #endif
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdarg.h>
43 #include <fcntl.h>
44 #include <cups/raster.h>
45 #include <cupsfilters/colormanager.h>
46 #include <cupsfilters/raster.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #include <signal.h>
50 #include <unistd.h>
51 #include <errno.h>
52 
53 #define PDF_MAX_CHECK_COMMENT_LINES	20
54 
55 #define CUPS_IPTEMPFILE "/tmp/ip-XXXXXX"
56 #define CUPS_OPTEMPFILE "/tmp/op-XXXXXX"
57 
58 #ifdef CUPS_RASTER_SYNCv1
59 typedef cups_page_header2_t mupdf_page_header;
60 #else
61 typedef cups_page_header_t mupdf_page_header;
62 #endif /* CUPS_RASTER_SYNCv1 */
63 
64 
65 int
parse_doc_type(FILE * fp)66 parse_doc_type(FILE *fp)
67 {
68   char buf[5];
69   char *rc;
70 
71   /* get the first few bytes of the file */
72   rewind(fp);
73   rc = fgets(buf,sizeof(buf),fp);
74   /* empty input */
75   if (rc == NULL)
76     return 1;
77 
78   /* is PDF */
79   if (strncmp(buf,"%PDF",4) == 0)
80     return 0;
81 
82   fprintf(stderr,"DEBUG: input file cannot be identified\n");
83   exit(EXIT_FAILURE);
84 }
85 
86 static void
parse_pdf_header_options(FILE * fp,mupdf_page_header * h)87 parse_pdf_header_options(FILE *fp, mupdf_page_header *h)
88 {
89   char buf[4096];
90   int i;
91 
92   rewind(fp);
93   /* skip until PDF start header */
94   while (fgets(buf,sizeof(buf),fp) != 0) {
95     if (strncmp(buf,"%PDF",4) == 0) {
96       break;
97     }
98   }
99   for (i = 0;i < PDF_MAX_CHECK_COMMENT_LINES;i++) {
100     if (fgets(buf,sizeof(buf),fp) == 0) break;
101     if (strncmp(buf,"%%PDFTOPDFNumCopies",19) == 0) {
102       char *p;
103 
104       p = strchr(buf+19,':');
105       h->NumCopies = atoi(p+1);
106     } else if (strncmp(buf,"%%PDFTOPDFCollate",17) == 0) {
107       char *p;
108 
109       p = strchr(buf+17,':');
110       while (*p == ' ' || *p == '\t') p++;
111       if (strncasecmp(p,"true",4) == 0) {
112         h->Collate = CUPS_TRUE;
113       } else {
114         h->Collate = CUPS_FALSE;
115       }
116     }
117   }
118 }
119 
120 static void
add_pdf_header_options(mupdf_page_header * h,cups_array_t * mupdf_args)121 add_pdf_header_options(mupdf_page_header *h,
122 		       cups_array_t 	 *mupdf_args)
123 {
124   char tmpstr[1024];
125 
126   if ((h->HWResolution[0] != 100) || (h->HWResolution[1] != 100)) {
127     snprintf(tmpstr, sizeof(tmpstr), "-r%dx%d", h->HWResolution[0],
128 	     h->HWResolution[1]);
129     cupsArrayAdd(mupdf_args, strdup(tmpstr));
130   } else {
131     snprintf(tmpstr, sizeof(tmpstr), "-r100x100");
132     cupsArrayAdd(mupdf_args, strdup(tmpstr));
133   }
134 
135   snprintf(tmpstr, sizeof(tmpstr), "-w%d", h->cupsWidth);
136   cupsArrayAdd(mupdf_args, strdup(tmpstr));
137 
138   snprintf(tmpstr, sizeof(tmpstr), "-h%d", h->cupsHeight);
139   cupsArrayAdd(mupdf_args, strdup(tmpstr));
140 
141   switch (h->cupsColorSpace) {
142   case CUPS_CSPACE_RGB:
143   case CUPS_CSPACE_CMY:
144   case CUPS_CSPACE_SRGB:
145   case CUPS_CSPACE_ADOBERGB:
146     snprintf(tmpstr, sizeof(tmpstr), "-crgb");
147     break;
148 
149   case CUPS_CSPACE_CMYK:
150     snprintf(tmpstr, sizeof(tmpstr), "-ccmyk");
151     break;
152 
153   case CUPS_CSPACE_SW:
154     snprintf(tmpstr, sizeof(tmpstr), "-cgray");
155     break;
156 
157   default:
158   case CUPS_CSPACE_K:
159   case CUPS_CSPACE_W:
160     snprintf(tmpstr, sizeof(tmpstr), "-cmono");
161     break;
162   }
163   cupsArrayAdd(mupdf_args, strdup(tmpstr));
164 }
165 
166 static int
mutool_spawn(const char * filename,cups_array_t * mupdf_args,char ** envp)167 mutool_spawn (const char *filename,
168 	      cups_array_t *mupdf_args,
169 	      char **envp)
170 {
171   char *argument;
172   char **mutoolargv;
173   const char* apos;
174   int i;
175   int numargs;
176   int pid;
177   int status = 65536;
178   int wstatus;
179 
180   /* Put mutool command line argument into an array for the "exec()"
181      call */
182   numargs = cupsArrayCount(mupdf_args);
183   mutoolargv = calloc(numargs + 1, sizeof(char *));
184   for (argument = (char *)cupsArrayFirst(mupdf_args), i = 0; argument;
185        argument = (char *)cupsArrayNext(mupdf_args), i++) {
186     mutoolargv[i] = argument;
187   }
188   mutoolargv[i] = NULL;
189 
190   /* Debug output: Full mutool command line and environment variables */
191   fprintf(stderr, "DEBUG: mutool command line:");
192   for (i = 0; mutoolargv[i]; i ++) {
193     if ((strchr(mutoolargv[i],' ')) || (strchr(mutoolargv[i],'\t')))
194       apos = "'";
195     else
196       apos = "";
197     fprintf(stderr, " %s%s%s", apos, mutoolargv[i], apos);
198   }
199   fprintf(stderr, "\n");
200 
201   for (i = 0; envp[i]; i ++)
202     fprintf(stderr, "DEBUG: envp[%d]=\"%s\"\n", i, envp[i]);
203 
204   if ((pid = fork()) == 0) {
205     /* Execute mutool command line ... */
206     execvpe(filename, mutoolargv, envp);
207     perror(filename);
208     goto out;
209   }
210 
211  retry_wait:
212   if (waitpid (pid, &wstatus, 0) == -1) {
213     if (errno == EINTR)
214       goto retry_wait;
215     perror ("mutool");
216     goto out;
217   }
218 
219   /* How did mutool process terminate */
220   if (WIFEXITED(wstatus))
221     /* Via exit() anywhere or return() in the main() function */
222     status = WEXITSTATUS(wstatus);
223   else if (WIFSIGNALED(wstatus))
224     /* Via signal */
225     status = 256 * WTERMSIG(wstatus);
226   fprintf(stderr, "DEBUG: mutool completed, status: %d\n", status);
227 
228  out:
229   free(mutoolargv);
230   return status;
231 }
232 
233 int
main(int argc,char ** argv,char * envp[])234 main (int argc, char **argv, char *envp[])
235 {
236   char buf[BUFSIZ];
237   char *icc_profile = NULL;
238   char tmpstr[1024];
239   const char *t = NULL;
240   cups_array_t *mupdf_args = NULL;
241   cups_option_t *options = NULL;
242   FILE *fp = NULL;
243   char infilename[1024];
244   mupdf_page_header h;
245   int fd = -1;
246   int cm_disabled;
247   int n;
248   int num_options;
249   int empty = 0;
250   int status = 1;
251   ppd_file_t *ppd = NULL;
252   struct sigaction sa;
253   cm_calibration_t cm_calibrate;
254 #ifdef HAVE_CUPS_1_7
255   ppd_attr_t *attr;
256 #endif /* HAVE_CUPS_1_7 */
257 
258   if (argc < 6 || argc > 7) {
259     fprintf(stderr, "ERROR: %s job-id user title copies options [file]\n",
260 	    argv[0]);
261     goto out;
262   }
263 
264   memset(&sa, 0, sizeof(sa));
265   /* Ignore SIGPIPE and have write return an error instead */
266   sa.sa_handler = SIG_IGN;
267   sigaction(SIGPIPE, &sa, NULL);
268 
269   num_options = cupsParseOptions(argv[5], 0, &options);
270 
271   t = getenv("PPD");
272   if (t && t[0] != '\0')
273     if ((ppd = ppdOpenFile(t)) == NULL) {
274       fprintf(stderr, "ERROR: Failed to open PPD: %s\n", t);
275     }
276 
277   if (ppd) {
278     ppdMarkDefaults (ppd);
279     cupsMarkOptions (ppd, num_options, options);
280   }
281 
282   if (argc == 6) {
283     /* stdin */
284 
285     fd = cupsTempFd(infilename, 1024);
286     if (fd < 0) {
287       fprintf(stderr, "ERROR: Can't create temporary file\n");
288       goto out;
289     }
290 
291     /* copy stdin to the tmp file */
292     while ((n = read(0,buf,BUFSIZ)) > 0) {
293       if (write(fd,buf,n) != n) {
294         fprintf(stderr, "ERROR: Can't copy stdin to temporary file\n");
295         close(fd);
296         goto out;
297       }
298     }
299     if (lseek(fd,0,SEEK_SET) < 0) {
300       fprintf(stderr, "ERROR: Can't rewind temporary file\n");
301       close(fd);
302       goto out;
303     }
304 
305     if ((fp = fdopen(fd,"rb")) == 0) {
306       fprintf(stderr, "ERROR: Can't fdopen temporary file\n");
307       close(fd);
308       goto out;
309     }
310   } else {
311     /* argc == 7 filename is specified */
312 
313     if ((fp = fopen(argv[6],"rb")) == 0) {
314       fprintf(stderr, "ERROR: Can't open input file %s\n",argv[6]);
315       goto out;
316     }
317     strncpy(infilename, argv[6], sizeof(infilename) - 1);
318   }
319 
320   /* If doc type is not PDF exit */
321   if(parse_doc_type(fp))
322      empty = 1;
323 
324   /*  Check status of color management in CUPS */
325   cm_calibrate = cmGetCupsColorCalibrateMode(options, num_options);
326 
327   if (cm_calibrate == CM_CALIBRATION_ENABLED)
328     cm_disabled = 1;
329   else
330     cm_disabled = cmIsPrinterCmDisabled(getenv("PRINTER"));
331 
332   if (!cm_disabled)
333     cmGetPrinterIccProfile(getenv("PRINTER"), &icc_profile, ppd);
334 
335   /* mutool parameters */
336   mupdf_args = cupsArrayNew(NULL, NULL);
337   if (!mupdf_args) {
338     fprintf(stderr, "ERROR: Unable to allocate memory for mutool arguments array\n");
339     goto out;
340   }
341 
342   fprintf(stderr,"command: %s\n",CUPS_MUTOOL);
343   snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_MUTOOL);
344   cupsArrayAdd(mupdf_args, strdup(tmpstr));
345   cupsArrayAdd(mupdf_args, strdup("draw"));
346   cupsArrayAdd(mupdf_args, strdup("-L"));
347   cupsArrayAdd(mupdf_args, strdup("-o-"));
348   cupsArrayAdd(mupdf_args, strdup("-smtf"));
349 
350   /* mutool output parameters */
351   cupsArrayAdd(mupdf_args, strdup("-Fpwg"));
352 
353   /* Note that MuPDF only creates PWG Raster and never CUPS Raster,
354      so we always set the PWG Raster flag in the cupsRasterParseIPPOptions()
355      calls.
356      Make also sure that the width and height of the page in pixels is
357      the size of the full page (as PWG Raster and MuPDF require it) and not
358      only the printable area (as cupsRasterInterpretPPD() sets, to fulfill
359      CUPS Raster standard) */
360   if (ppd) {
361     cupsRasterInterpretPPD(&h, ppd, num_options, options, 0);
362     h.cupsWidth = h.HWResolution[0] * h.PageSize[0] / 72;
363     h.cupsHeight = h.HWResolution[1] * h.PageSize[1] / 72;
364 #ifdef HAVE_CUPS_1_7
365     cupsRasterParseIPPOptions(&h, num_options, options, 1, 0);
366 #endif /* HAVE_CUPS_1_7 */
367   } else {
368 #ifdef HAVE_CUPS_1_7
369     cupsRasterParseIPPOptions(&h, num_options, options, 1, 1);
370 #else
371     fprintf(stderr, "ERROR: No PPD file specified.\n");
372     goto out;
373 #endif /* HAVE_CUPS_1_7 */
374   }
375 
376   if ((h.HWResolution[0] == 100) && (h.HWResolution[1] == 100)) {
377     /* No "Resolution" option */
378     if (ppd && (attr = ppdFindAttr(ppd, "DefaultResolution", 0)) != NULL) {
379       /* "*DefaultResolution" keyword in the PPD */
380       const char *p = attr->value;
381       h.HWResolution[0] = atoi(p);
382       if ((p = strchr(p, 'x')) != NULL)
383 	h.HWResolution[1] = atoi(p);
384       else
385 	h.HWResolution[1] = h.HWResolution[0];
386       if (h.HWResolution[0] <= 0)
387 	h.HWResolution[0] = 300;
388       if (h.HWResolution[1] <= 0)
389 	h.HWResolution[1] = h.HWResolution[0];
390     } else {
391       h.HWResolution[0] = 300;
392       h.HWResolution[1] = 300;
393     }
394     h.cupsWidth = h.HWResolution[0] * h.PageSize[0] / 72;
395     h.cupsHeight = h.HWResolution[1] * h.PageSize[1] / 72;
396   }
397 
398   /* set PDF-specific options */
399   parse_pdf_header_options(fp, &h);
400 
401   /* fixed other values that pdftopdf handles */
402   h.MirrorPrint = CUPS_FALSE;
403   h.Orientation = CUPS_ORIENT_0;
404 
405   /* get all the data from the header and pass it to mutool */
406   add_pdf_header_options (&h, mupdf_args);
407 
408   snprintf(tmpstr, sizeof(tmpstr), "%s", infilename);
409   cupsArrayAdd(mupdf_args, strdup(tmpstr));
410 
411   /* Execute mutool command line ... */
412   snprintf(tmpstr, sizeof(tmpstr), "%s", CUPS_MUTOOL);
413 
414   /* call mutool */
415   status = mutool_spawn (tmpstr, mupdf_args, envp);
416   if (status != 0) status = 1;
417 
418   if(empty)
419   {
420      fprintf(stderr, "DEBUG: Input is empty, outputting empty file.\n");
421      status = 0;
422   }
423 out:
424   if (fp)
425     fclose(fp);
426   if (mupdf_args)
427     cupsArrayDelete(mupdf_args);
428 
429   free(icc_profile);
430   if (ppd)
431     ppdClose(ppd);
432   if (fd >= 0)
433     unlink(infilename);
434   return status;
435 }
436