]> mj.ucw.cz Git - paperjam.git/commitdiff
Implemented paper, scaleto, fit
authorMartin Mares <mj@ucw.cz>
Tue, 3 Apr 2018 20:36:37 +0000 (22:36 +0200)
committerMartin Mares <mj@ucw.cz>
Tue, 3 Apr 2018 20:36:37 +0000 (22:36 +0200)
Makefile
cmds.cc
jam.h

index 9344b44e8a3ab196d55073a838c1035081051203..7552ec07f9654cf6d92ae2ddc9ee9e62b14bc96b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ all: paperjam
 
 paperjam: paperjam.o pdf-tools.o parse.o cmds.o pdf.o
        $(LD) -o $@ $^ $(LDLIBS)
-paperjam: LDLIBS += -lqpdf
+paperjam: LDLIBS += -lqpdf -lpaper
 paperjam: LD=$(CXX)
 
 paperjam.o: jam.h pdf-tools.h
diff --git a/cmds.cc b/cmds.cc
index 4212ddf6373783789117ecda0fff3f6d2288af3b..3dcf02bf72e24e7ec9dd63038242b07dc9c1ff63 100644 (file)
--- a/cmds.cc
+++ b/cmds.cc
@@ -7,6 +7,7 @@
 #include <cassert>
 #include <cstdlib>
 #include <cstdio>
+#include <paper.h>
 
 #include "jam.h"
 
@@ -22,7 +23,9 @@ static const arg_def no_args[] = {
   { NULL,      0 }
 };
 
-/*** Generic transformed page ***/
+/*** Generic routines ***/
+
+// Transformed page
 
 class xform_page : public page {
   page *orig_page;
@@ -52,6 +55,8 @@ void xform_page::render(out_context *out, pdf_matrix parent_xform)
   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;
@@ -65,6 +70,166 @@ vector<page *> cmd_exec_simple::process(vector<page *> &pages)
   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 {
@@ -410,7 +575,7 @@ public:
            }
          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);
            }
@@ -431,6 +596,104 @@ vector<page *> merge_cmd::process(vector<page *> &pages)
   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); }
@@ -446,5 +709,8 @@ const cmd_def cmd_table[] = {
   { "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    }
 };
diff --git a/jam.h b/jam.h
index 0c52b3b42db0f574c3088557a8bc358331033d85..31510d8c2b5f3469d3e6ab831919df33b92d231a 100644 (file)
--- a/jam.h
+++ b/jam.h
@@ -7,6 +7,7 @@
 #include <vector>
 #include <list>
 #include <unordered_map>
+#include <cmath>
 
 using namespace std;
 
@@ -16,6 +17,16 @@ typedef unsigned int uint;
 #define UNUSED __attribute__((unused))
 #define FORMAT_CHECK(x,y,z) __attribute__((format(x,y,z)))
 
+static inline bool is_zero(double z)
+{
+  return fabs(z) < 0.001;
+}
+
+static inline bool is_equal(double x, double y)
+{
+  return is_zero(x-y);
+}
+
 #include "pdf-tools.h"
 
 /*** Representation of commands ***/