#include <cassert>
#include <cstdlib>
#include <cstdio>
+#include <paper.h>
#include "jam.h"
{ NULL, 0 }
};
-/*** Generic transformed page ***/
+/*** Generic routines ***/
+
+// Transformed page
class xform_page : public page {
page *orig_page;
orig_page->render(out, xform * parent_xform);
}
+// Commands acting on individual pages
+
class cmd_exec_simple : public cmd_exec {
virtual page *process_page(page *p) = 0;
vector<page *> process(vector<page *> &pages) override;
return out;
}
+// Paper specifications
+
+class paper_spec {
+public:
+ double w, h;
+ paper_spec(cmd *c, bool maybe=true)
+ {
+ arg_val *aname = c->arg("paper");
+ arg_val *aw = c->arg("w");
+ arg_val *ah = c->arg("h");
+ if (!aname->given() && !aw->given() && !ah->given() && maybe)
+ {
+ w = h = 0;
+ return;
+ }
+ if (aw->given() != ah->given() || aname->given() == aw->given())
+ die("Either paper format name or width and height must be given");
+ if (aname->given())
+ {
+ const char *name = aname->as_string("").c_str();
+ const paper *pap = paperinfo(name);
+ if (!pap)
+ die("No paper called %s is known", name);
+ w = paperpswidth(pap);
+ h = paperpsheight(pap);
+ }
+ else
+ {
+ w = aw->as_double(0);
+ h = ah->as_double(0);
+ }
+ }
+};
+
+#define PAPER_ARGS \
+ { "paper", AT_STRING | AT_POSITIONAL }, \
+ { "w", AT_DIMEN }, \
+ { "h", AT_DIMEN }
+
+// Position specification
+
+class pos_spec {
+public:
+ int h, v;
+ pos_spec() { v = h = 0; }
+ pos_spec(string s)
+ {
+ if (s.size() != 2)
+ die("Value of pos must have two characters");
+ if (s[0] == 't')
+ v = 1;
+ else if (s[0] == 'c')
+ v = 0;
+ else if (s[0] == 'b')
+ v = -1;
+ else
+ die("First character of pos must be t/c/b");
+ if (s[1] == 'l')
+ h = -1;
+ else if (s[1] == 'c')
+ h = 0;
+ else if (s[1] == 'r')
+ h = 1;
+ else
+ die("Second character of pos must be l/c/r");
+ }
+ pos_spec(cmd *c) : pos_spec(c->arg("pos")->as_string("cc")) { }
+ pdf_matrix place(BBox &inner, BBox &outer)
+ {
+ pdf_matrix m;
+ m.shift(-inner.x_min, -inner.y_min);
+ switch (h)
+ {
+ case -1:
+ break;
+ case 0:
+ m.shift((outer.width() - inner.width()) / 2, 0);
+ break;
+ case 1:
+ m.shift(outer.width() - inner.width(), 0);
+ break;
+ default:
+ abort();
+ }
+ switch (v)
+ {
+ case -1:
+ break;
+ case 0:
+ m.shift(0, (outer.height() - inner.height()) / 2);
+ break;
+ case 1:
+ m.shift(0, outer.height() - inner.height());
+ break;
+ default:
+ abort();
+ }
+ m.shift(outer.x_min, outer.y_min);
+ return m;
+ }
+};
+
+#define POS_ARGS \
+ { "pos", AT_STRING }
+
+// Margins
+
+class margin_spec {
+public:
+ double l, r, t, b;
+ margin_spec(cmd *c, string basic, string sx)
+ {
+ double m, h, v;
+ m = c->arg(basic)->as_double(0);
+ h = c->arg("h" + sx)->as_double(m);
+ v = c->arg("v" + sx)->as_double(m);
+ l = c->arg("l" + sx)->as_double(h);
+ r = c->arg("r" + sx)->as_double(h);
+ t = c->arg("t" + sx)->as_double(v);
+ b = c->arg("b" + sx)->as_double(v);
+ }
+ void shrink_box(BBox *bb)
+ {
+ bb->x_min += l;
+ bb->x_max -= r;
+ bb->y_min += b;
+ bb->y_max -= t;
+ if (bb->x_min >= bb->x_max || bb->y_min >= bb->y_max)
+ die("Margins cannot be larger than the whole page");
+ }
+ void expand_box(BBox *bb)
+ {
+ bb->x_min -= l;
+ bb->x_max += r;
+ bb->y_min -= b;
+ bb->y_max += t;
+ }
+};
+
+#define MARGIN_ARGS(basic, sx) \
+ { basic, AT_DIMEN }, \
+ { "h" sx, AT_DIMEN }, \
+ { "v" sx, AT_DIMEN }, \
+ { "l" sx, AT_DIMEN }, \
+ { "r" sx, AT_DIMEN }, \
+ { "t" sx, AT_DIMEN }, \
+ { "b" sx, AT_DIMEN }
+
+// Scaling preserving aspect ratio
+
+double scale_to_fit(BBox &from, BBox &to)
+{
+ double fw = from.width(), fh = from.height();
+ double tw = to.width(), th = to.height();
+ if (is_zero(fw) || is_zero(fh) || is_zero(tw) || is_zero(th))
+ return 1;
+ else
+ return min(tw/fw, th/fh);
+}
+
/*** move ***/
class move_cmd : public cmd_exec_simple {
}
else
{
- if (abs(width-p->width) > 0.001 || abs(height-p->height) > 0.001)
+ if (!is_equal(width, p->width) || !is_equal(height, p->height))
die("All pages participating in a merge must have the same dimensions");
bbox.join(p->bbox);
}
return out;
}
+/*** paper ***/
+
+class paper_cmd : public cmd_exec_simple {
+ paper_spec paper;
+ pos_spec pos;
+public:
+ paper_cmd(cmd *c) : paper(c), pos(c) { }
+ page *process_page(page *p) override
+ {
+ BBox paper_box = BBox(paper.w, paper.h);
+ pdf_matrix xf = pos.place(p->bbox, paper_box);
+ page *q = new xform_page(p, xf);
+ q->width = paper.w;
+ q->height = paper.h;
+ return q;
+ }
+};
+
+static const arg_def paper_args[] = {
+ PAPER_ARGS,
+ POS_ARGS,
+ { NULL, 0 }
+};
+
+/*** scaleto ***/
+
+class scaleto_cmd : public cmd_exec_simple {
+ paper_spec paper;
+ pos_spec pos;
+public:
+ scaleto_cmd(cmd *c) : paper(c), pos(c) { }
+ page *process_page(page *p) override
+ {
+ BBox orig_box = BBox(p->width, p->height);
+ BBox paper_box = BBox(paper.w, paper.h);
+ pdf_matrix xf;
+ xf.scale(scale_to_fit(orig_box, paper_box));
+ orig_box.transform(xf);
+ xf.concat(pos.place(orig_box, paper_box));
+ page *q = new xform_page(p, xf);
+ q->width = paper.w;
+ q->height = paper.h;
+ return q;
+ }
+};
+
+static const arg_def scaleto_args[] = {
+ PAPER_ARGS,
+ POS_ARGS,
+ { NULL, 0 }
+};
+
+/*** fit ***/
+
+class fit_cmd : public cmd_exec_simple {
+ paper_spec paper;
+ pos_spec pos;
+ margin_spec marg;
+public:
+ fit_cmd(cmd *c) : paper(c, true), pos(c), marg(c, "margin", "margin") { }
+ page *process_page(page *p) override
+ {
+ pdf_matrix xf;
+ page *q;
+
+ if (!is_zero(paper.w) && !is_zero(paper.h))
+ {
+ // Paper given: scale image to fit paper
+ BBox orig_box = p->bbox;
+ BBox paper_box = BBox(paper.w, paper.h);
+ marg.shrink_box(&paper_box);
+ xf.scale(scale_to_fit(orig_box, paper_box));
+ orig_box.transform(xf);
+ xf.concat(pos.place(orig_box, paper_box));
+ q = new xform_page(p, xf);
+ q->width = paper.w;
+ q->height = paper.h;
+ }
+ else
+ {
+ // No paper given: adjust paper to fit image
+ xf.shift(-p->bbox.x_min, -p->bbox.y_min);
+ xf.shift(marg.l, marg.b);
+ q = new xform_page(p, xf);
+ q->width = p->bbox.width() + marg.l + marg.r;
+ q->height = p->bbox.height() + marg.t + marg.b;
+ }
+ return q;
+ }
+};
+
+static const arg_def fit_args[] = {
+ PAPER_ARGS,
+ POS_ARGS,
+ MARGIN_ARGS("margin", "margin"),
+ { NULL, 0 }
+};
+
/*** Command table ***/
template<typename T> cmd_exec *ctor(cmd *c) { return new T(c); }
{ "modulo", modulo_args, 1, &ctor<modulo_cmd> },
{ "draw_bbox",no_args, 0, &ctor<draw_bbox_cmd> },
{ "merge", no_args, 0, &ctor<merge_cmd> },
+ { "paper", paper_args, 0, &ctor<paper_cmd> },
+ { "scaleto", scaleto_args, 0, &ctor<scaleto_cmd> },
+ { "fit", fit_args, 0, &ctor<fit_cmd> },
{ NULL, NULL, 0, NULL }
};