]> mj.ucw.cz Git - paperjam.git/blob - paperjam.cc
Store duplicate pages only once
[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 page_out::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(page_out *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   media_box = BBox(inpg.getKey("/MediaBox"));
33   width = media_box.width();
34   height = media_box.height();
35 }
36
37 void in_page::render(page_out *out, pdf_matrix xform)
38 {
39   // Convert page to xobject
40   if (xobject.isNull())
41     xobject = out_pdf.makeIndirectObject( page_to_xobject(&out_pdf, out_pdf.copyForeignObject(pdf_page)) );
42   string xobj_res = out->new_resource("XO");
43   out->xobjects.replaceKey(xobj_res, xobject);
44
45   pdf_matrix m;
46   m.shift(-media_box.x_min, -media_box.y_min);
47   m.concat(xform);
48
49   out->contents += "q " + m.to_string() + " cm " + xobj_res + " Do Q";
50 }
51
52 static int run_indent;
53
54 static void debug_pages(vector<page *> &pages)
55 {
56   if (!debug_mode)
57     return;
58
59   for (auto pg: pages)
60     debug("%*sPage #%d: w=%.3f h=%.3f", run_indent, "", pg->index, pg->width, pg->height);
61 }
62
63 vector<page *> run_command_list(list<cmd *> &cmds, vector<page *> &pages)
64 {
65   debug("%*s# Input", run_indent, "");
66   debug_pages(pages);
67
68   for (auto c: cmds)
69     {
70       debug("%*s# Executing %s", run_indent, "", c->def->name);
71       run_indent += 4;
72       pages = c->exec->process(pages);
73       run_indent -= 4;
74       debug_pages(pages);
75     }
76
77   return pages;
78 }
79
80 static void process(list<cmd *> &cmds, const char *in_name, const char *out_name)
81 {
82   in_pdf.processFile(in_name);
83   in_pdf.pushInheritedAttributesToPage();
84   out_pdf.emptyPDF();
85
86   vector<QPDFObjectHandle> const &in_pages = in_pdf.getAllPages();
87   vector<page *> pages;
88
89   QPDFObjectHandle page_copy = out_pdf.copyForeignObject(in_pages[0]);
90
91   int cnt = 0;
92   for (auto inpg: in_pages)
93     pages.push_back(new in_page(inpg, ++cnt));
94
95   pages = run_command_list(cmds, pages);
96
97   for (auto pg: pages)
98     {
99       page_out out;
100       out.resources = QPDFObjectHandle::newDictionary();
101       // FIXME: What if the source page requires a broader ProcSet?
102       out.resources.replaceKey("/ProcSet", QPDFObjectHandle::parse("[/PDF /Text]"));
103       out.xobjects = QPDFObjectHandle::newDictionary();
104       out.resources.replaceKey("/XObject", out.xobjects);
105       pg->render(&out, pdf_matrix());
106
107       QPDFObjectHandle contents = QPDFObjectHandle::newStream(&out_pdf, out.contents);
108
109       // Create the page object
110       QPDFObjectHandle out_page = out_pdf.makeIndirectObject(QPDFObjectHandle::newDictionary());
111       out_page.replaceKey("/Type", QPDFObjectHandle::newName("/Page"));
112       out_page.replaceKey("/MediaBox", BBox(pg->width, pg->height).to_array());
113       out_page.replaceKey("/Contents", contents);
114       out_page.replaceKey("/Resources", out.resources);
115       out_pdf.addPage(out_page, false);
116     }
117
118   // Produce info dictionary
119   QPDFObjectHandle trailer = out_pdf.getTrailer();
120   QPDFObjectHandle info = trailer.getKey("/Info");
121   if (info.isNull())
122     {
123       info = QPDFObjectHandle::newDictionary();
124       trailer.replaceKey("/Info", info);
125     }
126   else
127     assert(info.isDictionary());
128   // FIXME: More meta-data
129   info.replaceKey("/Producer", unicode_string("PaperJam"));
130
131   // Write the output file
132   QPDFWriter writer(out_pdf, out_name);
133   writer.write();
134 }
135
136 int main(int argc, char **argv)
137 {
138   if (argc != 4)
139     {
140       fprintf(stderr, "Usage: paperjam <commands> <input> <output>\n");
141       return 1;
142     }
143
144   debug_mode = 100;
145
146   list<cmd *> cmds;
147   parse(argv[1], cmds);
148
149   try
150     {
151       process(cmds, argv[2], argv[3]);
152     }
153   catch (exception& e)
154     {
155       die("%s", e.what());
156     }
157
158   return 0;
159 }