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