all: paperjam
-paperjam: paperjam.o pdf-tools.o parse.o cmds.o
+paperjam: paperjam.o pdf-tools.o parse.o cmds.o gs.o
$(LD) -o $@ $^ $(LDLIBS)
paperjam: LDLIBS += -lqpdf
paperjam: LD=$(CXX)
pdf-tools.o: jam.h pdf-tools.h
parse.o: jam.h pdf-tools.h
cmds.o: jam.h pdf-tools.h
+gs.o: jam.h pdf-tools.h
--- /dev/null
+#include <cassert>
+#include <cstdlib>
+#include <cstdio>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "jam.h"
+
+vector<BBox> gs_bboxes(const char *in)
+{
+ int pipes[2];
+ if (pipe(pipes) < 0)
+ die("Cannot create pipe: %m");
+
+ pid_t pid = fork();
+ if (pid < 0)
+ die("Cannot fork: %m");
+
+ if (!pid)
+ {
+ close(pipes[0]);
+ dup2(pipes[1], 1);
+ dup2(pipes[1], 2);
+ close(pipes[1]);
+ execlp("gs", "gs", "-sDEVICE=bbox", "-dSAFER", "-dBATCH", "-dNOPAUSE", "-q", in, NULL);
+ die("Cannot execute gs: %m");
+ }
+
+ close(pipes[1]);
+ FILE *f = fdopen(pipes[0], "r");
+ if (!f)
+ die("fdopen failed: %m");
+
+ char line[1024];
+ vector<BBox> bboxes;
+ while (fgets(line, sizeof(line), f))
+ {
+ char *eol = strchr(line, '\n');
+ if (!eol)
+ die("Ghostscript produced too long lines");
+ *eol = 0;
+
+ if (!strncmp(line, "%%HiResBoundingBox: ", 20))
+ {
+ double x1, y1, x2, y2;
+ if (sscanf(line+20, "%lf%lf%lf%lf", &x1, &y1, &x2, &y2) != 4)
+ die("Cannot parse Ghostscript output: %s", line);
+ bboxes.push_back(BBox(x1, y1, x2, y2));
+ }
+ else if (line[0] != '%')
+ fprintf(stderr, "%s\n", line);
+ }
+ fclose(f);
+
+ int stat;
+ if (waitpid(pid, &stat, 0) < 0)
+ die("wait failed: %m");
+ if (!WIFEXITED(stat) || WEXITSTATUS(stat))
+ die("Ghostscript failed");
+
+ return bboxes;
+}
// cmds.cc
extern const cmd_def cmd_table[];
+
+// gs.cc
+
+vector<BBox> gs_bboxes(const char *in);
return;
for (auto pg: pages)
- debug("Page #%d: w=%.3f h=%.3f", pg->index, pg->width, pg->height);
+ debug("Page #%d: media[%.3f %.3f] bbox[%.3f %.3f %.3f %.3f]",
+ pg->index,
+ pg->width, pg->height,
+ pg->bbox.x_min, pg->bbox.y_min, pg->bbox.x_max, pg->bbox.y_max);
}
vector<page *> run_command_list(list<cmd *> &cmds, vector<page *> &pages)
return pages;
}
+static void find_bboxes(vector<page *> &pages, const char *in_name)
+{
+ vector<BBox> bboxes = gs_bboxes(in_name);
+ if (pages.size() != bboxes.size())
+ die("Ghostscript failed to produce the right number of bboxes");
+
+ for (size_t i=0; i<pages.size(); i++)
+ pages[i]->bbox = bboxes[i];
+}
+
static void process(list<cmd *> &cmds, const char *in_name, const char *out_name)
{
in_pdf.processFile(in_name);
for (auto inpg: in_pages)
pages.push_back(new in_page(inpg, ++cnt));
+ find_bboxes(pages, in_name);
+
pages = run_command_list(cmds, pages);
for (auto pg: pages)
QPDFObjectHandle out_page = out_pdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
out_page.replaceKey("/Type", QPDFObjectHandle::newName("/Page"));
out_page.replaceKey("/MediaBox", BBox(pg->width, pg->height).to_array());
- out_page.replaceKey("/CropBox", pg->bbox.to_array());
+ // FIXME:
+ // out_page.replaceKey("/CropBox", pg->bbox.to_array());
out_page.replaceKey("/Contents", contents);
out_page.replaceKey("/Resources", out.resources);
out_pdf.addPage(out_page, false);