From 014f0e14a3c6a044d99a67c8f4e1c4065452479e Mon Sep 17 00:00:00 2001 From: Didier Raboud Date: Sun, 23 May 2010 00:05:04 +0200 Subject: Imported Upstream version 4.0-20090301 --- pdf.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 pdf.c (limited to 'pdf.c') 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 +#include +#include +#include +#include + +#include +#include + +#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; +} + -- cgit v1.2.3