summaryrefslogtreecommitdiff
path: root/pdf.c
diff options
context:
space:
mode:
authorDidier Raboud <didier@raboud.com>2010-05-23 00:05:04 +0200
committerDidier Raboud <didier@raboud.com>2010-05-23 00:05:04 +0200
commit014f0e14a3c6a044d99a67c8f4e1c4065452479e (patch)
tree300200f9bf0f3ebb0878741ddb287caf2e0d862e /pdf.c
parent6fefeecb6abbb1f7dfe07ade1a0abef06dac5d32 (diff)
Imported Upstream version 4.0-20090301upstream/4.0-20090301
Diffstat (limited to 'pdf.c')
-rw-r--r--pdf.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/pdf.c b/pdf.c
new file mode 100644
index 0000000..53a02b0
--- /dev/null
+++ b/pdf.c
@@ -0,0 +1,359 @@
+
+#include "foomaticrip.h"
+#include "util.h"
+#include "options.h"
+#include "process.h"
+#include "renderer.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <ghostscript/iapi.h>
+#include <ghostscript/ierrors.h>
+
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+
+const char *pagecountcode =
+ "/pdffile (%s) (r) file def\n"
+ "pdfdict begin\n"
+ "pdffile pdfopen begin\n"
+ "(PageCount: ) print\n"
+ "pdfpagecount == flush\n" /* 'flush' makes sure that gs_stdout is called
+ before gsapi_run_string returns */
+ "currentdict pdfclose\n"
+ "end end\n";
+
+char gsout [256];
+
+static int wait_for_renderer();
+
+
+static const char * temp_dir()
+{
+ static const char *tmpdir = NULL;
+
+ if (!tmpdir)
+ {
+ const char *dirs[] = { getenv("TMPDIR"), P_tmpdir, "/tmp", NULL };
+ const char **dir;
+
+ for (dir = dirs; *dir; dir++)
+ if (access(*dir, W_OK) == 0) {
+ tmpdir = *dir;
+ break;
+ }
+ if (tmpdir)
+ {
+ _log("Storing temporary files in %s\n", tmpdir);
+ setenv("TMPDIR", tmpdir, 1); /* for child processes */
+ }
+ else
+ rip_die(EXIT_PRNERR_NORETRY_BAD_SETTINGS,
+ "Cannot find a writable temp dir.");
+ }
+
+ return tmpdir;
+}
+
+int gs_stdout(void *instance, const char *str, int len)
+{
+ int last;
+ if (isempty(gsout)) {
+ last = len < 256 ? len : 255;
+ strncpy(gsout, str, last +1);
+ gsout[last] = '\0';
+ }
+ return len; /* ignore everything after the first few chars */
+}
+
+int gs_stderr(void *instance, const char *str, int len)
+{
+ char *buf = malloc(len +1);
+ strncpy(buf, str, len);
+ buf[len] = '\0';
+ _log("Ghostscript: %s", buf);
+ free(buf);
+ return len;
+}
+
+static int pdf_count_pages(const char *filename)
+{
+ void *minst;
+ int gsargc = 3;
+ char *gsargv[] = { "", "-dNODISPLAY", "-q" };
+ int pagecount;
+ int exit_code;
+ char code[2048];
+
+ if (gsapi_new_instance(&minst, NULL) < 0) {
+ _log("Could not create ghostscript instance\n");
+ return -1;
+ }
+ gsapi_set_stdio(minst, NULL, gs_stdout, gs_stderr);
+ if (gsapi_init_with_args(minst, gsargc, gsargv) < 0) {
+ _log("Could not init ghostscript\n");
+ gsapi_exit(minst);
+ gsapi_delete_instance(minst);
+ return -1;
+ }
+
+ snprintf(code, 2048, pagecountcode, filename);
+ if (gsapi_run_string(minst, code, 0, &exit_code) == 0) {
+ if (sscanf(gsout, "PageCount: %d", &pagecount) < 1)
+ pagecount = -1;
+ }
+
+ gsapi_exit(minst);
+ gsapi_delete_instance(minst);
+ return pagecount;
+}
+
+pid_t kid3 = 0;
+
+
+static int start_renderer(const char *cmd)
+{
+ if (kid3 != 0)
+ wait_for_renderer();
+
+ _log("Starting renderer with command: %s\n", cmd);
+ kid3 = start_process("kid3", exec_kid3, (void *)cmd, NULL, NULL);
+ if (kid3 < 0)
+ return 0;
+
+ return 1;
+}
+
+static int wait_for_renderer()
+{
+ int status;
+
+ waitpid(kid3, &status, 0);
+
+ if (!WIFEXITED(status)) {
+ _log("Kid3 did not finish normally.\n");
+ exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
+ }
+
+ _log("Kid3 exit status: %d\n", WEXITSTATUS(status));
+ if (WEXITSTATUS(status) != 0)
+ exit(EXIT_PRNERR_NORETRY_BAD_SETTINGS);
+
+ kid3 = 0;
+ return 1;
+}
+
+/*
+ * Extract pages 'first' through 'last' from the pdf and write them into a
+ * temporary file.
+ */
+static int pdf_extract_pages(char filename[PATH_MAX],
+ const char *pdffilename,
+ int first,
+ int last)
+{
+ void *minst;
+ char filename_arg[PATH_MAX], first_arg[50], last_arg[50];
+ const char *gs_args[] = { "", "-q", "-dNOPAUSE", "-dBATCH",
+ "-dPARANOIDSAFER", "-sDEVICE=pdfwrite", filename_arg, first_arg,
+ last_arg, pdffilename };
+
+ _log("Extracting pages %d through %d\n", first, last);
+
+ snprintf(filename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
+ mktemp(filename);
+ if (!filename[0])
+ return 0;
+
+ if (gsapi_new_instance(&minst, NULL) < 0)
+ {
+ _log("Could not create ghostscript instance\n");
+ return 0;
+ }
+
+ snprintf(filename_arg, PATH_MAX, "-sOutputFile=%s", filename);
+ snprintf(first_arg, 50, "-dFirstPage=%d", first);
+ if (last > 0)
+ snprintf(last_arg, 50, "-dLastPage=%d", last);
+ else
+ first_arg[0] = '\0';
+
+ gsapi_set_stdio(minst, NULL, NULL, gs_stderr);
+
+ if (gsapi_init_with_args(minst, ARRAY_LEN(gs_args), (char **)gs_args) < 0)
+ {
+ _log("Could not run ghostscript to extract the pages\n");
+ gsapi_exit(minst);
+ gsapi_delete_instance(minst);
+ return 0;
+ }
+
+ gsapi_exit(minst);
+ gsapi_delete_instance(minst);
+ return 1;
+}
+
+static int render_pages_with_generic_command(dstr_t *cmd,
+ const char *filename,
+ int firstpage,
+ int lastpage)
+{
+ char tmpfile[PATH_MAX];
+ int result;
+
+ /* TODO it might be a good idea to give pdf command lines the possibility
+ * to get the file on the command line rather than piped through stdin
+ * (maybe introduce a &filename; ??) */
+
+ if (lastpage < 0) /* i.e. print the whole document */
+ dstrcatf(cmd, " < %s", filename);
+ else
+ {
+ if (!pdf_extract_pages(tmpfile, filename, firstpage, lastpage))
+ return 0;
+ dstrcatf(cmd, " < %s", tmpfile);
+ }
+
+ result = start_renderer(cmd->data);
+
+ if (lastpage > 0)
+ unlink(tmpfile);
+
+ return result;
+}
+
+static int render_pages_with_ghostscript(dstr_t *cmd,
+ size_t start_gs_cmd,
+ size_t end_gs_cmd,
+ const char *filename,
+ int firstpage,
+ int lastpage)
+{
+ char *p;
+
+ /* No need to create a temporary file, just give ghostscript the file and
+ * first/last page on the command line */
+
+ /* Some command lines want to read from stdin */
+ for (p = &cmd->data[end_gs_cmd -1]; isspace(*p); p--)
+ ;
+ if (*p == '-')
+ *p = ' ';
+
+ dstrinsertf(cmd, end_gs_cmd, " %s ", filename);
+
+ if (lastpage > 0)
+ dstrinsertf(cmd, start_gs_cmd +2,
+ " -dFirstPage=%d -dLastPage=%d ",
+ firstpage, lastpage);
+ else
+ dstrinsertf(cmd, start_gs_cmd +2,
+ " -dFirstPage=%d ", firstpage);
+
+ return start_renderer(cmd->data);
+}
+
+static int render_pages(const char *filename, int firstpage, int lastpage)
+{
+ dstr_t *cmd = create_dstr();
+ size_t start, end;
+ int result;
+
+ build_commandline(optionset("currentpage"), cmd, 1);
+
+ extract_command(&start, &end, cmd->data, "gs");
+ if (start == end)
+ /* command is not Ghostscript */
+ result = render_pages_with_generic_command(cmd,
+ filename,
+ firstpage,
+ lastpage);
+ else
+ /* Ghostscript command, tell it which pages we want to render */
+ result = render_pages_with_ghostscript(cmd,
+ start,
+ end,
+ filename,
+ firstpage,
+ lastpage);
+
+ free_dstr(cmd);
+ return result;
+}
+
+static int print_pdf_file(const char *filename)
+{
+ int page_count, i;
+ int firstpage;
+
+ page_count = pdf_count_pages(filename);
+
+ if (page_count <= 0)
+ return 0;
+ _log("File contains %d pages\n", page_count);
+
+ optionset_copy_values(optionset("header"), optionset("currentpage"));
+ optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
+ firstpage = 1;
+ for (i = 1; i <= page_count; i++)
+ {
+ set_options_for_page(optionset("currentpage"), i);
+ if (!optionset_equal(optionset("currentpage"), optionset("previouspage"), 1))
+ {
+ render_pages(filename, firstpage, i);
+ firstpage = i;
+ }
+ optionset_copy_values(optionset("currentpage"), optionset("previouspage"));
+ }
+ if (firstpage == 1)
+ render_pages(filename, 1, -1); /* Render the whole document */
+ else
+ render_pages(filename, firstpage, page_count);
+
+ wait_for_renderer();
+
+ return 1;
+}
+
+int print_pdf(FILE *s,
+ const char *alreadyread,
+ size_t len,
+ const char *filename,
+ size_t startpos)
+{
+ char tmpfilename[PATH_MAX] = "";
+ int result;
+
+ /* If reading from stdin, write everything into a temporary file */
+ /* TODO don't do this if there aren't any pagerange-limited options */
+ if (s == stdin)
+ {
+ int fd;
+ FILE *tmpfile;
+
+ snprintf(tmpfilename, PATH_MAX, "%s/foomatic-XXXXXX", temp_dir());
+ fd = mkstemp(tmpfilename);
+ if (fd < 0) {
+ _log("Could not create temporary file: %s\n", strerror(errno));
+ return EXIT_PRNERR_NORETRY_BAD_SETTINGS;
+ }
+
+ tmpfile = fdopen(fd, "r+");
+ copy_file(tmpfile, stdin, alreadyread, len);
+ fclose(tmpfile);
+
+ filename = tmpfilename;
+ }
+
+ result = print_pdf_file(filename);
+
+ if (!isempty(tmpfilename))
+ unlink(tmpfilename);
+
+ return result;
+}
+