8 #include <qpdf/QPDFWriter.hh>
13 string out_context::new_resource(const string type)
15 return "/" + type + to_string(++res_cnt);
18 class in_page : public page {
19 QPDFObjectHandle pdf_page;
20 QPDFObjectHandle xobject;
23 void render(out_context *out, pdf_matrix xform);
24 in_page(QPDFObjectHandle inpg, int idx);
27 in_page::in_page(QPDFObjectHandle inpg, int idx)
30 xobject = QPDFObjectHandle::newNull();
33 media_box = BBox(inpg.getKey("/MediaBox"));
34 width = media_box.width();
35 height = media_box.height();
37 QPDFObjectHandle art_box = inpg.getKey("/ArtBox");
39 art_box = inpg.getKey("/CropBox");
41 bbox = BBox(width, height);
45 bbox.x_min -= media_box.x_min;
46 bbox.x_max -= media_box.x_min;
47 bbox.y_min -= media_box.y_min;
48 bbox.y_max -= media_box.y_min;
52 void in_page::render(out_context *out, pdf_matrix xform)
54 // Convert page to xobject
56 xobject = out_pdf.makeIndirectObject( page_to_xobject(&out_pdf, out_pdf.copyForeignObject(pdf_page)) );
57 string xobj_res = out->new_resource("XO");
58 out->xobjects.replaceKey(xobj_res, xobject);
61 m.shift(-media_box.x_min, -media_box.y_min);
64 out->contents += "q " + m.to_string() + " cm " + xobj_res + " Do Q ";
67 static void debug_pages(vector<page *> &pages)
73 debug("Page #%d: media[%.3f %.3f] bbox[%.3f %.3f %.3f %.3f]",
75 pg->width, pg->height,
76 pg->bbox.x_min, pg->bbox.y_min, pg->bbox.x_max, pg->bbox.y_max);
79 vector<page *> run_command_list(list<cmd *> &cmds, vector<page *> &pages)
86 debug("# Executing %s", c->def->name);
88 pages = c->exec->process(pages);
96 static void find_bboxes(vector<page *> &pages, const char *in_name)
98 vector<BBox> bboxes = gs_bboxes(in_name);
99 if (pages.size() != bboxes.size())
100 die("Ghostscript failed to produce the right number of bboxes");
102 for (size_t i=0; i<pages.size(); i++)
103 pages[i]->bbox = bboxes[i];
106 static void process(list<cmd *> &cmds, const char *in_name, const char *out_name)
108 in_pdf.processFile(in_name);
109 in_pdf.pushInheritedAttributesToPage();
112 vector<QPDFObjectHandle> const &in_pages = in_pdf.getAllPages();
113 vector<page *> pages;
115 QPDFObjectHandle page_copy = out_pdf.copyForeignObject(in_pages[0]);
118 for (auto inpg: in_pages)
119 pages.push_back(new in_page(inpg, ++cnt));
121 find_bboxes(pages, in_name);
123 pages = run_command_list(cmds, pages);
128 out.resources = QPDFObjectHandle::newDictionary();
129 out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF]"));
130 out.xobjects = QPDFObjectHandle::newDictionary();
131 out.resources.replaceKey("/XObject", out.xobjects);
132 pg->render(&out, pdf_matrix());
134 QPDFObjectHandle contents = QPDFObjectHandle::newStream(&out_pdf, out.contents);
136 // Create the page object
137 QPDFObjectHandle out_page = out_pdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
138 out_page.replaceKey("/Type", QPDFObjectHandle::newName("/Page"));
139 out_page.replaceKey("/MediaBox", BBox(pg->width, pg->height).to_array());
141 // out_page.replaceKey("/CropBox", pg->bbox.to_array());
142 out_page.replaceKey("/Contents", contents);
143 out_page.replaceKey("/Resources", out.resources);
144 out_pdf.addPage(out_page, false);
147 // Produce info dictionary
148 QPDFObjectHandle trailer = out_pdf.getTrailer();
149 QPDFObjectHandle info = trailer.getKey("/Info");
152 info = QPDFObjectHandle::newDictionary();
153 trailer.replaceKey("/Info", info);
156 assert(info.isDictionary());
157 // FIXME: More meta-data
158 info.replaceKey("/Producer", unicode_string("PaperJam"));
160 // Write the output file
161 QPDFWriter writer(out_pdf, out_name);
165 int main(int argc, char **argv)
167 if (argc <= 1 || argc > 1 && !strcmp(argv[1], "--help"))
175 fprintf(stderr, "Usage: paperjam <commands> <input> <output>\n");
182 parse(argv[1], cmds);
186 process(cmds, argv[2], argv[3]);