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