- 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
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;
}
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;
}
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;
}
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) { };
};
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);
}
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);
};
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())
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());
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);
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
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)
{
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);
};