• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* pdf.c
2  *
3  * Copyright (C) 2008 Till Kamppeter <till.kamppeter@gmail.com>
4  * Copyright (C) 2008 Lars Karlitski (formerly Uebernickel) <lars@karlitski.net>
5  *
6  * This file is part of foomatic-rip.
7  *
8  * Foomatic-rip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Foomatic-rip is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include "foomaticrip.h"
25 #include "util.h"
26 #include "options.h"
27 #include "process.h"
28 #include "renderer.h"
29 
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 #include <errno.h>
35 
36 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
37 
38 
39 static int wait_for_renderer();
40 
41 
pdf_count_pages(const char * filename)42 int pdf_count_pages(const char *filename)
43 {
44     char gscommand[CMDLINE_MAX];
45     char output[63] = "";
46     int pagecount;
47     size_t bytes;
48     char *p;
49 
50     snprintf(gscommand, CMDLINE_MAX, "%s -dNODISPLAY -dNOSAFER -dNOPAUSE -q -c "
51 	     "'/pdffile (%s) (r) file runpdfbegin (PageCount: ) print "
52 	     "pdfpagecount = quit'",
53 	     gspath, filename);
54 
55     FILE *pd = popen(gscommand, "r");
56     if (!pd)
57       rip_die(EXIT_STARVED, "Failed to execute ghostscript to determine number of input pages!\n");
58 
59     bytes = fread_or_die(output, 1, sizeof(output), pd);
60     pclose(pd);
61 
62     p = output;
63     pagecount = -1;
64     while (bytes > 0) {
65       if (sscanf(p, "PageCount: %d", &pagecount) >= 1)
66 	break;
67       p = memchr(p, '\n', bytes);
68       if (p == NULL)
69 	break;
70       p ++;
71       bytes = sizeof(output) - (p - output);
72     }
73 
74     return pagecount;
75 }
76 
77 pid_t kid3 = 0;
78 
79 
start_renderer(const char * cmd)80 static int start_renderer(const char *cmd)
81 {
82     if (kid3 != 0)
83         wait_for_renderer();
84 
85     _log("Starting renderer with command: %s\n", cmd);
86     kid3 = start_process("kid3", exec_kid3, (void *)cmd, NULL, NULL);
87     if (kid3 < 0)
88         rip_die(EXIT_STARVED, "Could not start renderer\n");
89 
90     return 1;
91 }
92 
wait_for_renderer()93 static int wait_for_renderer()
94 {
95     int status;
96 
97     waitpid(kid3, &status, 0);
98 
99     if (!WIFEXITED(status)) {
100         _log("Kid3 did not finish normally.\n");
101         exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
102     }
103 
104     _log("Kid3 exit status: %d\n", WEXITSTATUS(status));
105     if (WEXITSTATUS(status) != 0)
106         exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
107 
108     kid3 = 0;
109     return 1;
110 }
111 
112 /*
113  * Extract pages 'first' through 'last' from the pdf and write them into a
114  * temporary file.
115  */
pdf_extract_pages(char filename[PATH_MAX],const char * pdffilename,int first,int last)116 static int pdf_extract_pages(char filename[PATH_MAX],
117                              const char *pdffilename,
118                              int first,
119                              int last)
120 {
121     char gscommand[CMDLINE_MAX];
122     char filename_arg[PATH_MAX], first_arg[50], last_arg[50];
123     int fd;
124 
125     _log("Extracting pages %d through %d\n", first, last);
126 
127     snprintf(filename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
128     if ((fd = mkstemp(filename)) == -1)
129         rip_die(EXIT_STARVED, "Unable to create temporary file!\n");
130     close (fd);
131 
132     snprintf(filename_arg, PATH_MAX, "-sOutputFile=%s", filename);
133 
134     first_arg[0] = '\0';
135     last_arg[0] = '\0';
136     if (first > 1)
137     {
138         snprintf(first_arg, 50, "-dFirstPage=%d", first);
139         if (last >= first)
140             snprintf(last_arg, 50, "-dLastPage=%d", last);
141     }
142 
143     snprintf(gscommand, CMDLINE_MAX, "%s -q -dNOPAUSE -dBATCH -dSAFER -dNOINTERPOLATE -dNOMEDIAATTRS"
144 	     "-sDEVICE=pdfwrite -dShowAcroForm %s %s %s %s",
145 	     gspath, filename_arg, first_arg, last_arg, pdffilename);
146 
147     FILE *pd = popen(gscommand, "r");
148     if (!pd)
149         rip_die(EXIT_STARVED, "Could not run ghostscript to extract the pages!\n");
150     pclose(pd);
151 
152     return 1;
153 }
154 
render_pages_with_generic_command(dstr_t * cmd,const char * filename,int firstpage,int lastpage)155 static int render_pages_with_generic_command(dstr_t *cmd,
156                                              const char *filename,
157                                              int firstpage,
158                                              int lastpage)
159 {
160     char tmpfile[PATH_MAX];
161     int result;
162 
163     /* TODO it might be a good idea to give pdf command lines the possibility
164      * to get the file on the command line rather than piped through stdin
165      * (maybe introduce a &filename; ??) */
166 
167     if (lastpage < 0)  /* i.e. print the whole document */
168         dstrcatf(cmd, " < %s", filename);
169     else
170     {
171         if (!pdf_extract_pages(tmpfile, filename, firstpage, lastpage))
172             rip_die(EXIT_STARVED, "Could not run ghostscript to extract the pages!\n");
173         dstrcatf(cmd, " < %s", tmpfile);
174     }
175 
176     result = start_renderer(cmd->data);
177 
178     if (lastpage > 0)
179         unlink(tmpfile);
180 
181     return result;
182 }
183 
render_pages_with_ghostscript(dstr_t * cmd,size_t start_gs_cmd,size_t end_gs_cmd,const char * filename,int firstpage,int lastpage)184 static int render_pages_with_ghostscript(dstr_t *cmd,
185                                          size_t start_gs_cmd,
186                                          size_t end_gs_cmd,
187                                          const char *filename,
188                                          int firstpage,
189                                          int lastpage)
190 {
191     char *p;
192 
193     /* No need to create a temporary file, just give ghostscript the file and
194      * first/last page on the command line */
195 
196     /* Some command lines want to read from stdin */
197     for (p = &cmd->data[end_gs_cmd -1]; isspace(*p); p--)
198         ;
199     if (*p == '-')
200         *p = ' ';
201 
202     dstrinsertf(cmd, end_gs_cmd, " %s ", filename);
203 
204     dstrinsertf(cmd, start_gs_cmd + 2, " -dShowAcroForm ");
205 
206     if (firstpage > 1)
207     {
208         if (lastpage >= firstpage)
209             dstrinsertf(cmd, start_gs_cmd +2,
210                         " -dFirstPage=%d -dLastPage=%d ",
211                         firstpage, lastpage);
212         else
213             dstrinsertf(cmd, start_gs_cmd +2,
214                         " -dFirstPage=%d ", firstpage);
215     }
216 
217     return start_renderer(cmd->data);
218 }
219 
render_pages(const char * filename,int firstpage,int lastpage)220 static int render_pages(const char *filename, int firstpage, int lastpage)
221 {
222     dstr_t *cmd = create_dstr();
223     size_t start, end;
224     int result;
225 
226     build_commandline(optionset("currentpage"), cmd, 1);
227 
228     extract_command(&start, &end, cmd->data, "gs");
229     if (start == end)
230         /* command is not Ghostscript */
231         result = render_pages_with_generic_command(cmd,
232                                                    filename,
233                                                    firstpage,
234                                                    lastpage);
235     else
236         /* Ghostscript command, tell it which pages we want to render */
237         result = render_pages_with_ghostscript(cmd,
238                                                start,
239                                                end,
240                                                filename,
241                                                firstpage,
242                                                lastpage);
243 
244     free_dstr(cmd);
245     return result;
246 }
247 
print_pdf_file(const char * filename)248 static int print_pdf_file(const char *filename)
249 {
250     int page_count, i;
251     int firstpage;
252 
253     page_count = pdf_count_pages(filename);
254 
255     if (page_count < 0)
256         rip_die(EXIT_JOBERR, "Unable to determine number of pages, page count: %d\n", page_count);
257     _log("File contains %d pages\n", page_count);
258     if (page_count == 0)
259     {
260         _log("No pages left, outputting empty file.\n");
261         return 1;
262     }
263 
264     optionset_copy_values(optionset("header"), optionset("currentpage"));
265     optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
266     firstpage = 1;
267     for (i = 1; i <= page_count; i++)
268     {
269         set_options_for_page(optionset("currentpage"), i);
270         if (!optionset_equal(optionset("currentpage"), optionset("previouspage"), 1))
271         {
272             render_pages(filename, firstpage, i);
273             firstpage = i;
274         }
275         optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
276     }
277     if (firstpage == 1)
278         render_pages(filename, 1, -1); /* Render the whole document */
279     else
280         render_pages(filename, firstpage, page_count);
281 
282     wait_for_renderer();
283 
284     return 1;
285 }
286 
print_pdf(FILE * s,const char * alreadyread,size_t len,const char * filename,size_t startpos)287 int print_pdf(FILE *s,
288               const char *alreadyread,
289               size_t len,
290               const char *filename,
291               size_t startpos)
292 {
293     char tmpfilename[PATH_MAX] = "";
294     int result;
295 
296     /* If reading from stdin, write everything into a temporary file */
297     /* TODO don't do this if there aren't any pagerange-limited options */
298     if (s == stdin)
299     {
300         int fd;
301         FILE *tmpfile;
302 
303         snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
304         fd = mkstemp(tmpfilename);
305         if (fd < 0) {
306             _log("Could not create temporary file: %s\n", strerror(errno));
307             return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
308         }
309 
310         tmpfile = fdopen(fd, "r+");
311         copy_file(tmpfile, stdin, alreadyread, len);
312         fclose(tmpfile);
313 
314         filename = tmpfilename;
315     }
316 
317     result = print_pdf_file(filename);
318 
319     if (!isempty(tmpfilename))
320         unlink(tmpfilename);
321 
322     return result;
323 }
324 
325