From: Martin Mares Date: Sun, 1 Apr 2018 21:46:23 +0000 (+0200) Subject: Remember bounding boxes of page contents X-Git-Tag: v0.1~38 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=bc0c38a2fd19e04770b25536e1eb6549b48ce1f3;p=paperjam.git Remember bounding boxes of page contents --- diff --git a/TODO b/TODO index 65580b0..fea7831 100644 --- 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 61bfd89..1906623 100644 --- 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 process(vector &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 process(vector &pages); +}; + vector move_cmd::process(vector &pages) { vector 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 scale_cmd::process(vector &pages) vector 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 rotate_cmd::process(vector &pages) vector 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 b18aa82..2917aef 100644 --- a/jam.h +++ b/jam.h @@ -37,25 +37,32 @@ public: virtual string dump() { return ""; } }; -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) { }; }; diff --git a/paperjam.cc b/paperjam.cc index 0a95acf..22dd9f5 100644 --- a/paperjam.cc +++ b/paperjam.cc @@ -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 &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 &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); diff --git a/pdf-tools.cc b/pdf-tools.cc index 9d89ad8..71dde6c 100644 --- a/pdf-tools.cc +++ b/pdf-tools.cc @@ -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 diff --git a/pdf-tools.h b/pdf-tools.h index a4004d3..5dfd95b 100644 --- a/pdf-tools.h +++ b/pdf-tools.h @@ -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); };