{ "t" sx, AT_DIMEN }, \
{ "b" sx, AT_DIMEN }
+// Cropmarks
+
+class cropmark_spec {
+ enum mark_type {
+ MARK_NONE,
+ MARK_BOX,
+ MARK_CROSS,
+ MARK_OUT,
+ MARK_IN,
+ } type;
+ double pen_width;
+ double arm_length;
+ double offset;
+ string crop_cross(double x, double y, uint mask);
+ QPDFObjectHandle egstate;
+public:
+ cropmark_spec(cmd *c, string prefix="", string def_type="cross")
+ {
+ string t = c->arg(prefix + "mark")->as_string(def_type);
+ if (t == "none")
+ type = MARK_NONE;
+ else if (t == "box")
+ type = MARK_BOX;
+ else if (t == "cross")
+ type = MARK_CROSS;
+ else if (t == "in")
+ type = MARK_IN;
+ else if (t == "out")
+ type = MARK_OUT;
+ else
+ die("Invalid cropmark type %s", t.c_str());
+
+ pen_width = c->arg(prefix + "pen")->as_double(1);
+ arm_length = c->arg(prefix + "len")->as_double(5*mm);
+ offset = c->arg(prefix + "offset")->as_double(0);
+ egstate = QPDFObjectHandle::newNull();
+ }
+ string pdf_stream(out_context *out, BBox &box, pdf_matrix &xform);
+};
+
+string cropmark_spec::crop_cross(double x, double y, uint mask)
+{
+ string s = "";
+
+ for (uint i=0; i<4; i++)
+ if (mask & (1U << i))
+ {
+ double x2 = x, y2 = y;
+ switch (i)
+ {
+ case 3: x2 -= arm_length; break;
+ case 2: x2 += arm_length; break;
+ case 1: y2 += arm_length; break;
+ case 0: y2 -= arm_length; break;
+ }
+ s += pdf_coord(x) + " " + pdf_coord(y) + " m " + pdf_coord(x2) + " " + pdf_coord(y2) + " l S ";
+ }
+
+ return s;
+}
+
+string cropmark_spec::pdf_stream(out_context *out, BBox &box, pdf_matrix &xform)
+{
+ string s = "q ";
+ s += "0 0 0 RG ";
+ s += xform.to_string() + " cm ";
+
+ if (egstate.isNull())
+ {
+ auto egs = QPDFObjectHandle::newDictionary();
+ egs.replaceKey("/Type", QPDFObjectHandle::newName("/ExtGState"));
+ egs.replaceKey("/LW", QPDFObjectHandle::newReal(pen_width, 1));
+ egs.replaceKey("/LC", QPDFObjectHandle::newInteger(2));
+ egs.replaceKey("/LJ", QPDFObjectHandle::newInteger(0));
+ egstate = out->pdf->makeIndirectObject(egs);
+ }
+
+ string egs_res = out->new_resource("GS");
+ out->egstates.replaceKey(egs_res, egstate);
+ s += egs_res + " gs ";
+
+ BBox b = box;
+ b.x_min -= offset;
+ b.x_max += offset;
+ b.y_min -= offset;
+ b.y_max += offset;
+
+ switch (type)
+ {
+ case MARK_NONE:
+ break;
+ case MARK_BOX:
+ s += b.to_rect() + " re S ";
+ break;
+ case MARK_CROSS:
+ s += crop_cross(b.x_min, b.y_min, 0b1111);
+ s += crop_cross(b.x_max, b.y_min, 0b1111);
+ s += crop_cross(b.x_max, b.y_max, 0b1111);
+ s += crop_cross(b.x_min, b.y_max, 0b1111);
+ break;
+ case MARK_IN:
+ s += crop_cross(b.x_min, b.y_min, 0b0110);
+ s += crop_cross(b.x_max, b.y_min, 0b1010);
+ s += crop_cross(b.x_max, b.y_max, 0b1001);
+ s += crop_cross(b.x_min, b.y_max, 0b0101);
+ break;
+ case MARK_OUT:
+ s += crop_cross(b.x_min, b.y_min, 0b1001);
+ s += crop_cross(b.x_max, b.y_min, 0b0101);
+ s += crop_cross(b.x_max, b.y_max, 0b0110);
+ s += crop_cross(b.x_min, b.y_max, 0b1010);
+ break;
+ }
+
+ s += "Q ";
+ return s;
+}
+
+#define CROPMARK_ARGS(px) \
+ { px "mark", AT_STRING }, \
+ { px "pen", AT_DIMEN }, \
+ { px "len", AT_DIMEN }, \
+ { px "offset",AT_DIMEN }
+
// Scaling preserving aspect ratio
double scale_to_fit(BBox &from, BBox &to)
/*** draw-bbox ***/
-class draw_bbox_cmd : public cmd_exec {
-public:
- draw_bbox_cmd(cmd *c UNUSED) { }
- vector<page *> process(vector<page *> &pages) override;
-};
-
class draw_bbox_page : public page {
page *orig_page;
public:
draw_bbox_page(page *p) : page(p) { orig_page = p; }
};
+class draw_bbox_cmd : public cmd_exec_simple {
+public:
+ draw_bbox_cmd(cmd *c UNUSED) { }
+ page *process_page(page *p) override
+ {
+ return new draw_bbox_page(p);
+ }
+};
+
void draw_bbox_page::render(out_context *out, pdf_matrix xform)
{
orig_page->render(out, xform);
"Q ";
}
-vector<page *> draw_bbox_cmd::process(vector<page *> &pages)
-{
- vector<page *> out;
- for (auto p: pages)
- out.push_back(new draw_bbox_page(p));
- return out;
-}
-
/*** merge ***/
class merge_cmd : public cmd_exec {
{ NULL, 0 }
};
+/*** cropmarks ***/
+
+class cropmarks_page : public page {
+ page *orig_page;
+ cropmark_spec *cm;
+public:
+ void render(out_context *out, pdf_matrix xform) override
+ {
+ orig_page->render(out, xform);
+ out->contents += cm->pdf_stream(out, image_box, xform);
+ }
+ cropmarks_page(page *p, cropmark_spec *cms) : page(p), orig_page(p), cm(cms) { }
+};
+
+class cropmarks_cmd : public cmd_exec_simple {
+ cropmark_spec cm;
+ page *process_page(page *p) override
+ {
+ return new cropmarks_page(p, &cm);
+ }
+
+public:
+ cropmarks_cmd(cmd *c) : cm(c) { }
+};
+
+static const arg_def cropmarks_args[] = {
+ CROPMARK_ARGS(""),
+ { NULL, 0 }
+};
+
/*** Command table ***/
template<typename T> cmd_exec *ctor(cmd *c) { return new T(c); }
{ "add-blank",add_blank_args, 0, &ctor<add_blank_cmd> },
{ "book", book_args, 0, &ctor<book_cmd> },
{ "nup", nup_args, 0, &ctor<nup_cmd> },
+ { "cropmarks",cropmarks_args, 0, &ctor<cropmarks_cmd> },
{ NULL, NULL, 0, NULL }
};
{
// Convert page to xobject
if (xobject.isNull())
- xobject = out_pdf.makeIndirectObject( page_to_xobject(&out_pdf, out_pdf.copyForeignObject(pdf_page)) );
+ xobject = out->pdf->makeIndirectObject( page_to_xobject(out->pdf, out->pdf->copyForeignObject(pdf_page)) );
string xobj_res = out->new_resource("XO");
out->xobjects.replaceKey(xobj_res, xobject);
for (auto pg: pages)
{
out_context out;
+ out.pdf = &out_pdf;
out.resources = QPDFObjectHandle::newDictionary();
out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF]"));
out.xobjects = QPDFObjectHandle::newDictionary();
- out.resources.replaceKey("/XObject", out.xobjects);
+ out.egstates = QPDFObjectHandle::newDictionary();
pg->render(&out, pdf_matrix());
QPDFObjectHandle contents = QPDFObjectHandle::newStream(&out_pdf, out.contents);
// FIXME:
// out_page.replaceKey("/CropBox", pg->image_box.to_array());
out_page.replaceKey("/Contents", contents);
+ if (!out.xobjects.getKeys().empty())
+ out.resources.replaceKey("/XObject", out.xobjects);
+ if (!out.egstates.getKeys().empty())
+ out.resources.replaceKey("/ExtGState", out.egstates);
out_page.replaceKey("/Resources", out.resources);
out_pdf.addPage(out_page, false);
}