+// 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 }
+