]> mj.ucw.cz Git - paperjam.git/blob - paperjam.cc
Flip and switch parameters
[paperjam.git] / paperjam.cc
1 #include <cassert>
2 #include <cstdarg>
3 #include <cstdlib>
4 #include <cstdio>
5
6 #include "jam.h"
7
8 #include <qpdf/QPDFWriter.hh>
9
10 static QPDF in_pdf;
11 static QPDF out_pdf;
12
13 string out_context::new_resource(const string type)
14 {
15   return "/" + type + to_string(++res_cnt);
16 }
17
18 class in_page : public page {
19   QPDFObjectHandle pdf_page;
20   QPDFObjectHandle xobject;
21 public:
22   BBox media_box;
23   void render(out_context *out, pdf_matrix xform);
24   in_page(QPDFObjectHandle inpg, int idx);
25 };
26
27 in_page::in_page(QPDFObjectHandle inpg, int idx)
28 {
29   pdf_page = inpg;
30   xobject = QPDFObjectHandle::newNull();
31   index = idx;
32
33   media_box = BBox(inpg.getKey("/MediaBox"));
34   width = media_box.width();
35   height = media_box.height();
36
37   QPDFObjectHandle art_box = inpg.getKey("/ArtBox");
38   if (art_box.isNull())
39     art_box = inpg.getKey("/CropBox");
40   if (art_box.isNull())
41     bbox = BBox(width, height);
42   else
43     {
44       bbox = BBox(art_box);
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;
49     }
50 }
51
52 void in_page::render(out_context *out, pdf_matrix xform)
53 {
54   // Convert page to xobject
55   if (xobject.isNull())
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);
59
60   pdf_matrix m;
61   m.shift(-media_box.x_min, -media_box.y_min);
62   m.concat(xform);
63
64   out->contents += "q " + m.to_string() + " cm " + xobj_res + " Do Q ";
65 }
66
67 static void debug_pages(vector<page *> &pages)
68 {
69   if (!debug_mode)
70     return;
71
72   for (auto pg: pages)
73     debug("Page #%d: media[%.3f %.3f] bbox[%.3f %.3f %.3f %.3f]",
74       pg->index,
75       pg->width, pg->height,
76       pg->bbox.x_min, pg->bbox.y_min, pg->bbox.x_max, pg->bbox.y_max);
77 }
78
79 vector<page *> run_command_list(list<cmd *> &cmds, vector<page *> &pages)
80 {
81   debug("# Input");
82   debug_pages(pages);
83
84   for (auto c: cmds)
85     {
86       debug("# Executing %s", c->def->name);
87       debug_indent += 4;
88       pages = c->exec->process(pages);
89       debug_indent -= 4;
90       debug_pages(pages);
91     }
92
93   return pages;
94 }
95
96 static void find_bboxes(vector<page *> &pages, const char *in_name)
97 {
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");
101
102   for (size_t i=0; i<pages.size(); i++)
103     pages[i]->bbox = bboxes[i];
104 }
105
106 static void process(list<cmd *> &cmds, const char *in_name, const char *out_name)
107 {
108   in_pdf.processFile(in_name);
109   in_pdf.pushInheritedAttributesToPage();
110   out_pdf.emptyPDF();
111
112   vector<QPDFObjectHandle> const &in_pages = in_pdf.getAllPages();
113   vector<page *> pages;
114
115   QPDFObjectHandle page_copy = out_pdf.copyForeignObject(in_pages[0]);
116
117   int cnt = 0;
118   for (auto inpg: in_pages)
119     pages.push_back(new in_page(inpg, ++cnt));
120
121   find_bboxes(pages, in_name);
122
123   pages = run_command_list(cmds, pages);
124
125   for (auto pg: pages)
126     {
127       out_context out;
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());
133
134       QPDFObjectHandle contents = QPDFObjectHandle::newStream(&out_pdf, out.contents);
135
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());
140       // FIXME:
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);
145     }
146
147   // Produce info dictionary
148   QPDFObjectHandle trailer = out_pdf.getTrailer();
149   QPDFObjectHandle info = trailer.getKey("/Info");
150   if (info.isNull())
151     {
152       info = QPDFObjectHandle::newDictionary();
153       trailer.replaceKey("/Info", info);
154     }
155   else
156     assert(info.isDictionary());
157   // FIXME: More meta-data
158   info.replaceKey("/Producer", unicode_string("PaperJam"));
159
160   // Write the output file
161   QPDFWriter writer(out_pdf, out_name);
162   writer.write();
163 }
164
165 int main(int argc, char **argv)
166 {
167   if (argc <= 1 || argc > 1 && !strcmp(argv[1], "--help"))
168     {
169       help();
170       return 0;
171     }
172
173   if (argc != 4)
174     {
175       fprintf(stderr, "Usage: paperjam <commands> <input> <output>\n");
176       return 1;
177     }
178
179   debug_mode = 100;
180
181   list<cmd *> cmds;
182   parse(argv[1], cmds);
183
184   try
185     {
186       process(cmds, argv[2], argv[3]);
187     }
188   catch (exception& e)
189     {
190       die("%s", e.what());
191     }
192
193   return 0;
194 }