+ CROPMARK_ARGS("c"),
+ { "tpos", AT_STRING, "Position of images inside tiles (default: tl)" },
+ { "space", AT_DIMEN, "Space between tiles (default: 0)" },
+ { "hspace", AT_DIMEN, "Horizontal space between tiles (default: space)" },
+ { "vspace", AT_DIMEN, "Vertical space between tiles (default: space)" },
+ { NULL, 0, NULL }
+};
+
+/*** cropmarks ***/
+
+class cropmarks_page : public page {
+ page *orig_page;
+ cropmark_spec *cm;
+public:
+ void render(out_context *out, pdf_matrix xform) override
+ {
+ orig_page->render(out, xform);
+ out->contents += cm->pdf_stream(out, image_box, xform);
+ }
+ void debug_dump() override
+ {
+ debug("Add cropmarks");
+ orig_page->debug_dump();
+ }
+ cropmarks_page(page *p, cropmark_spec *cms) : page(p), orig_page(p), cm(cms) { }
+};
+
+class cropmarks_cmd : public cmd_exec_simple {
+ cropmark_spec cm;
+ page *process_page(page *p) override
+ {
+ return new cropmarks_page(p, &cm);
+ }
+public:
+ cropmarks_cmd(cmd *c) : cm(c) { }
+};
+
+static const arg_def cropmarks_args[] = {
+ CROPMARK_ARGS(""),
+ { NULL, 0, NULL }
+};
+
+/*** clip ***/
+
+class clip_page : public page {
+ page *orig_page;
+ BBox clip_to;
+public:
+ void render(out_context *out, pdf_matrix xform) override
+ {
+ out->contents += "q " + clip_to.transformed(xform).to_rect() + " re W n ";
+ orig_page->render(out, xform);
+ out->contents += "Q ";
+ }
+ void debug_dump() override
+ {
+ debug("Clip [%.3f %.3f %.3f %.3f]", clip_to.x_min, clip_to.y_min, clip_to.x_max, clip_to.y_max);
+ orig_page->debug_dump();
+ }
+ clip_page(page *p, BBox &to) : page(p), orig_page(p), clip_to(to) { }
+};
+
+class clip_cmd : public cmd_exec_simple {
+ double bleed;
+ page *process_page(page *p) override
+ {
+ BBox to = p->image_box.enlarged(bleed);
+ return new clip_page(p, to);
+ }
+public:
+ clip_cmd(cmd *c)
+ {
+ bleed = c->arg("bleed")->as_double(0);
+ }
+};
+
+static const arg_def clip_args[] = {
+ { "bleed", AT_DIMEN, "Allow bleeding of image outside its box" },
+ { NULL, 0, NULL }
+};
+
+/*** common ***/
+
+class common_cmd : public cmd_exec {
+ vector<page *> process(vector<page *> &pages) override
+ {
+ if (!pages.size())
+ return pages;
+
+ const page *first = pages[0];
+ BBox pbox(first->width, first->height);
+ BBox ibox = first->image_box;
+ for (auto p: pages)
+ {
+ BBox pg(p->width, p->height);
+ pbox.join(pg);
+ ibox.join(p->image_box);
+ }
+
+ vector<page *> out;
+ for (auto p: pages)
+ {
+ page *q = new xform_page(p, pdf_matrix());
+ q->width = pbox.width();
+ q->height = pbox.height();
+ q->image_box = ibox;
+ out.push_back(q);
+ }
+
+ return out;
+ }
+public:
+ common_cmd(cmd *c UNUSED) { }
+};
+
+/*** slice ***/
+
+class slice_cmd : public cmd_exec {
+ paper_spec paper;
+ pos_spec pos;
+ margin_spec margin;
+ double bleed;
+public:
+ slice_cmd(cmd *c) : paper(c), pos(c), margin(c, "margin", "margin")
+ {
+ if (is_zero(paper.w) || is_zero(paper.h))
+ err("Paper format must be given");
+ bleed = c->arg("bleed")->as_double(0);
+ }
+ vector<page *> process(vector<page *> &pages) override;
+};
+
+vector<page *> slice_cmd::process(vector<page *> &pages)
+{
+ vector<page *> out;
+
+ for (auto p: pages)
+ {
+ double pw = paper.w - margin.l - margin.r;
+ double ph = paper.h - margin.t - margin.b;
+ if (pw < 0 || ph < 0)
+ err("Margins larger than paper");
+
+ int cols = (int) ceil((p->image_box.width() - 2*mm) / pw);
+ int rows = (int) ceil((p->image_box.height() - 2*mm) / ph);
+ BBox big_box(cols*pw, rows*ph);
+ pdf_matrix placement = pos.place(p->image_box, big_box);
+
+ debug("Slicing [%.3f,%.3f] to %dx%d [%.3f,%.3f]", p->image_box.width(), p->image_box.height(), rows, cols, pw, ph);
+ for (int r=0; r<rows; r++)
+ for (int c=0; c<cols; c++)
+ {
+ pdf_matrix xf = placement;
+ xf.shift(-c*pw, -(rows-1-r)*ph);
+ xf.shift(margin.l, margin.t);
+ page *q = new xform_page(p, xf);
+ q->width = paper.w;
+ q->height = paper.h;
+ BBox slice_box = BBox(margin.l, margin.t, paper.w - margin.r, paper.h - margin.b);
+ q->image_box = p->image_box.transformed(xf);
+ q->image_box.intersect(slice_box);
+ BBox bleeding_slice = slice_box.enlarged(bleed);
+ out.push_back(new clip_page(q, bleeding_slice));
+ }
+ }
+
+ return out;
+}
+
+static const arg_def slice_args[] = {
+ PAPER_ARGS,
+ POS_ARGS,
+ MARGIN_ARGS1_NAMED("margin"),
+ MARGIN_ARGS2("margin"),
+ { "bleed", AT_DIMEN, "Allow bleeding of image outside its box" },
+ { NULL, 0, NULL }