]> mj.ucw.cz Git - paperjam.git/commitdiff
Remember bounding boxes of page contents
authorMartin Mares <mj@ucw.cz>
Sun, 1 Apr 2018 21:46:23 +0000 (23:46 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 1 Apr 2018 21:46:23 +0000 (23:46 +0200)
TODO
cmds.cc
jam.h
paperjam.cc
pdf-tools.cc
pdf-tools.h

diff --git a/TODO b/TODO
index 65580b06f56cd5db5ff637082c05ef0c55f7fe67..fea7831fd2c34412c7dfcff7aae70da220466e97 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
 - Integrate pdf-tools.cc with the rest of the code
 - What if an input page specifies /Rotate?
 - Better error messages from instantiation
-- Select should work without commands in the pipeline
 - page->index: use or remove
diff --git a/cmds.cc b/cmds.cc
index 61bfd892bb18d0e768f2d5ba76355689fd0dcc4e..1906623b61e7ffa0ac811cf008eb1c983a553cde 100644 (file)
--- a/cmds.cc
+++ b/cmds.cc
@@ -19,35 +19,52 @@ static cmd_exec *null_ctor(cmd *c UNUSED)
   return new null_cmd;
 }
 
-/*** move ***/
-
-class move_cmd : public cmd_exec {
-public:
-  double x, y;
-  vector<page *> process(vector<page *> &pages);
-};
+/*** Generic transformed page ***/
 
 class xform_page : public page {
   page *orig_page;
-public:
   pdf_matrix xform;
-  void render(page_out *out, pdf_matrix xform);
-  xform_page(page *_orig, double _w, double _h) : page(_w, _h), orig_page(_orig) { }
+public:
+  void render(out_context *out, pdf_matrix xform);
+  xform_page(page *p, pdf_matrix xf);
 };
 
-void xform_page::render(page_out *out, pdf_matrix parent_xform)
+xform_page::xform_page(page *p, pdf_matrix xf)
+{
+  orig_page = p;
+  index = p->index;
+  xform = xf;
+
+  BBox media(p->width, p->height);
+  media.transform(xf);
+  width = media.width();
+  height = media.height();
+
+  bbox = p->bbox;
+  bbox.transform(xf);
+}
+
+void xform_page::render(out_context *out, pdf_matrix parent_xform)
 {
   orig_page->render(out, xform * parent_xform);
 }
 
+/*** move ***/
+
+class move_cmd : public cmd_exec {
+public:
+  double x, y;
+  vector<page *> process(vector<page *> &pages);
+};
+
 vector<page *> move_cmd::process(vector<page *> &pages)
 {
   vector<page *> out;
   for (auto p: pages)
     {
-      xform_page *q = new xform_page(p, p->width, p->height);
-      q->xform.shift(x, y);
-      out.push_back(q);
+      pdf_matrix m;
+      m.shift(x, y);
+      out.push_back(new xform_page(p, m));
     }
   return out;
 }
@@ -79,9 +96,9 @@ vector<page *> scale_cmd::process(vector<page *> &pages)
   vector<page *> out;
   for (auto p: pages)
     {
-      xform_page *q = new xform_page(p, x_factor*p->width, y_factor*p->height);
-      q->xform.scale(x_factor, y_factor);
-      out.push_back(q);
+      pdf_matrix m;
+      m.scale(x_factor, y_factor);
+      out.push_back(new xform_page(p, m));
     }
   return out;
 }
@@ -113,29 +130,27 @@ vector<page *> rotate_cmd::process(vector<page *> &pages)
   vector<page *> out;
   for (auto p: pages)
     {
-      xform_page *q = new xform_page(p, p->width, p->height);
+      pdf_matrix m;
       switch (deg)
        {
        case 0:
          break;
        case 90:
-         q->xform.rotate_deg(-90);
-         q->xform.shift(0, p->width);
-         swap(q->width, q->height);
+         m.rotate_deg(-90);
+         m.shift(0, p->width);
          break;
        case 180:
-         q->xform.rotate_deg(180);
-         q->xform.shift(p->width, p->height);
+         m.rotate_deg(180);
+         m.shift(p->width, p->height);
          break;
        case 270:
-         q->xform.rotate_deg(90);
-         q->xform.shift(p->height, 0);
-         swap(q->width, q->height);
+         m.rotate_deg(90);
+         m.shift(p->height, 0);
          break;
        default:
          abort();
        }
-      out.push_back(q);
+      out.push_back(new xform_page(p, m));
     }
   return out;
 }
diff --git a/jam.h b/jam.h
index b18aa82a4f95f218f23f67479bd2a1e97faa1584..2917aef225a89473803db5c784929dfa2a29130c 100644 (file)
--- a/jam.h
+++ b/jam.h
@@ -37,25 +37,32 @@ public:
   virtual string dump() { return "<undef>"; }
 };
 
-struct page_out {
+struct out_context {
   QPDFObjectHandle resources;
   QPDFObjectHandle xobjects;
   string contents;
   int res_cnt;
   string new_resource(const string type);
-  page_out() { res_cnt = 0; }
+  out_context() { res_cnt = 0; }
 };
 
 struct page {
   int index;
-  double width;
+  double width;                // Physical dimensions of media
   double height;
-  virtual void render(page_out *out UNUSED, pdf_matrix xform UNUSED) { abort(); }
+  BBox bbox;           // Bounds useful contents
+  virtual void render(out_context *out UNUSED, pdf_matrix xform UNUSED) { abort(); }
   page(double _w=0, double _h=0) : width(_w), height(_h) { }
+  page(page *p) {
+    index = p->index;
+    width = p->width;
+    height = p->height;
+    bbox = p->bbox;
+  }
 };
 
 struct empty_page : public page {
-  void render(page_out *out UNUSED, pdf_matrix xform UNUSED) { }
+  void render(out_context *out UNUSED, pdf_matrix xform UNUSED) { }
   empty_page(double _w=0, double _h=0) : page(_w, _h) { };
 };
 
index 0a95acf26abd49b8c06ba2b110bf8703611005af..22dd9f5e8d47068bd8c169d3f6f9252d297166f2 100644 (file)
@@ -10,7 +10,7 @@
 static QPDF in_pdf;
 static QPDF out_pdf;
 
-string page_out::new_resource(const string type)
+string out_context::new_resource(const string type)
 {
   return "/" + type + to_string(++res_cnt);
 }
@@ -20,7 +20,7 @@ class in_page : public page {
   QPDFObjectHandle xobject;
 public:
   BBox media_box;
-  void render(page_out *out, pdf_matrix xform);
+  void render(out_context *out, pdf_matrix xform);
   in_page(QPDFObjectHandle inpg, int idx);
 };
 
@@ -29,12 +29,27 @@ in_page::in_page(QPDFObjectHandle inpg, int idx)
   pdf_page = inpg;
   xobject = QPDFObjectHandle::newNull();
   index = idx;
+
   media_box = BBox(inpg.getKey("/MediaBox"));
   width = media_box.width();
   height = media_box.height();
+
+  QPDFObjectHandle art_box = inpg.getKey("/ArtBox");
+  if (art_box.isNull())
+    art_box = inpg.getKey("/CropBox");
+  if (art_box.isNull())
+    bbox = BBox(width, height);
+  else
+    {
+      bbox = BBox(art_box);
+      bbox.x_min -= media_box.x_min;
+      bbox.x_max -= media_box.x_min;
+      bbox.y_min -= media_box.y_min;
+      bbox.y_max -= media_box.y_min;
+    }
 }
 
-void in_page::render(page_out *out, pdf_matrix xform)
+void in_page::render(out_context *out, pdf_matrix xform)
 {
   // Convert page to xobject
   if (xobject.isNull())
@@ -94,10 +109,9 @@ static void process(list<cmd *> &cmds, const char *in_name, const char *out_name
 
   for (auto pg: pages)
     {
-      page_out out;
+      out_context out;
       out.resources = QPDFObjectHandle::newDictionary();
-      // FIXME: What if the source page requires a broader ProcSet?
-      out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF /Text]"));
+      out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF]"));
       out.xobjects = QPDFObjectHandle::newDictionary();
       out.resources.replaceKey("/XObject", out.xobjects);
       pg->render(&out, pdf_matrix());
@@ -108,6 +122,7 @@ static void process(list<cmd *> &cmds, const char *in_name, const char *out_name
       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());
       out_page.replaceKey("/Contents", contents);
       out_page.replaceKey("/Resources", out.resources);
       out_pdf.addPage(out_page, false);
index 9d89ad8153e7d02f3a160346cfea38b5d3718863..71dde6c9b7744d1d2c0b8176824ba8d519324143 100644 (file)
@@ -112,6 +112,16 @@ bool BBox::parse(QPDFObjectHandle h)
        return true;
 }
 
+void BBox::transform(pdf_matrix &m)
+{
+       m.apply(&x_min, &y_min);
+       m.apply(&x_max, &y_max);
+       if (x_max < x_min)
+               swap(x_min, x_max);
+       if (y_max < y_min)
+               swap(y_min, y_max);
+}
+
 /*** Unicode strings ***/
 
 // Construct PDF representation of a UTF-8 string
index a4004d3d8c2e03dccdf9beed4a42bc4202d15875..5dfd95b923c33aeacaa1b16abffbbef4cf090229 100644 (file)
@@ -65,6 +65,13 @@ struct pdf_matrix {
                m[5] = f;
        }
 
+       void apply(double *px, double *py)
+       {
+               double x=*px, y=*py;
+               *px = m[0]*x + m[2]*y + m[4];
+               *py = m[1]*x + m[3]*y + m[5];
+       }
+
        // A*B is a matrix which transforms first by A and then by B
        pdf_matrix operator *(pdf_matrix y)
        {
@@ -140,6 +147,7 @@ struct BBox {
        QPDFObjectHandle to_array();
        double width() { return x_max - x_min; }
        double height() { return y_max - y_min; }
+       void transform(pdf_matrix &m);
 private:
        bool parse(QPDFObjectHandle h);
 };