1/*
2 * PDF posteriser; split pages within a PDF file into smaller lumps.
3 */
4
5#include "mupdf/fitz.h"
6#include "mupdf/pdf.h"
7
8#include <string.h>
9#include <stdlib.h>
10#include <stdio.h>
11
12static int x_factor = 0;
13static int y_factor = 0;
14
15static void usage(void)
16{
17 fprintf(stderr,
18 "usage: mutool poster [options] input.pdf [output.pdf]\n"
19 "\t-p -\tpassword\n"
20 "\t-x\tx decimation factor\n"
21 "\t-y\ty decimation factor\n");
22 exit(1);
23}
24
25static void
26intersect_box(fz_context *ctx, pdf_document *doc, pdf_obj *page, pdf_obj *box_name, fz_rect mb)
27{
28 pdf_obj *box = pdf_dict_get(ctx, page, box_name);
29 pdf_obj *newbox;
30 fz_rect old_rect;
31
32 if (box == NULL)
33 return;
34
35 old_rect.x0 = pdf_array_get_real(ctx, box, 0);
36 old_rect.y0 = pdf_array_get_real(ctx, box, 1);
37 old_rect.x1 = pdf_array_get_real(ctx, box, 2);
38 old_rect.y1 = pdf_array_get_real(ctx, box, 3);
39
40 if (old_rect.x0 < mb.x0)
41 old_rect.x0 = mb.x0;
42 if (old_rect.y0 < mb.y0)
43 old_rect.y0 = mb.y0;
44 if (old_rect.x1 > mb.x1)
45 old_rect.x1 = mb.x1;
46 if (old_rect.y1 > mb.y1)
47 old_rect.y1 = mb.y1;
48
49 newbox = pdf_new_array(ctx, doc, 4);
50 pdf_array_push_real(ctx, newbox, old_rect.x0);
51 pdf_array_push_real(ctx, newbox, old_rect.y0);
52 pdf_array_push_real(ctx, newbox, old_rect.x1);
53 pdf_array_push_real(ctx, newbox, old_rect.y1);
54 pdf_dict_put_drop(ctx, page, box_name, newbox);
55}
56
57/*
58 * Recreate page tree with our posterised pages in.
59 */
60
61static void decimatepages(fz_context *ctx, pdf_document *doc)
62{
63 pdf_obj *oldroot, *root, *pages, *kids;
64 int num_pages = pdf_count_pages(ctx, doc);
65 int page, kidcount;
66 fz_rect mediabox;
67 fz_matrix page_ctm;
68
69 oldroot = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
70 pages = pdf_dict_get(ctx, oldroot, PDF_NAME(Pages));
71
72 root = pdf_new_dict(ctx, doc, 2);
73 pdf_dict_put(ctx, root, PDF_NAME(Type), pdf_dict_get(ctx, oldroot, PDF_NAME(Type)));
74 pdf_dict_put(ctx, root, PDF_NAME(Pages), pdf_dict_get(ctx, oldroot, PDF_NAME(Pages)));
75
76 pdf_update_object(ctx, doc, pdf_to_num(ctx, oldroot), root);
77
78 pdf_drop_obj(ctx, root);
79
80 /* Create a new kids array with our new pages in */
81 kids = pdf_new_array(ctx, doc, 1);
82
83 kidcount = 0;
84 for (page=0; page < num_pages; page++)
85 {
86 pdf_page *page_details = pdf_load_page(ctx, doc, page);
87 int xf = x_factor, yf = y_factor;
88 float w, h;
89 int x, y;
90
91 pdf_page_transform(ctx, page_details, &mediabox, &page_ctm);
92 fz_drop_page(ctx, (fz_page *) page_details);
93
94 w = mediabox.x1 - mediabox.x0;
95 h = mediabox.y1 - mediabox.y0;
96
97 if (xf == 0 && yf == 0)
98 {
99 /* Nothing specified, so split along the long edge */
100 if (w > h)
101 xf = 2, yf = 1;
102 else
103 xf = 1, yf = 2;
104 }
105 else if (xf == 0)
106 xf = 1;
107 else if (yf == 0)
108 yf = 1;
109
110 for (y = yf-1; y >= 0; y--)
111 {
112 for (x = 0; x < xf; x++)
113 {
114 pdf_obj *newpageobj, *newpageref, *newmediabox;
115 fz_rect mb;
116
117 newpageobj = pdf_copy_dict(ctx, pdf_lookup_page_obj(ctx, doc, page));
118 pdf_flatten_inheritable_page_items(ctx, newpageobj);
119 newpageref = pdf_add_object(ctx, doc, newpageobj);
120
121 newmediabox = pdf_new_array(ctx, doc, 4);
122
123 mb.x0 = mediabox.x0 + (w/xf)*x;
124 if (x == xf-1)
125 mb.x1 = mediabox.x1;
126 else
127 mb.x1 = mediabox.x0 + (w/xf)*(x+1);
128 mb.y0 = mediabox.y0 + (h/yf)*y;
129 if (y == yf-1)
130 mb.y1 = mediabox.y1;
131 else
132 mb.y1 = mediabox.y0 + (h/yf)*(y+1);
133
134 pdf_array_push_real(ctx, newmediabox, mb.x0);
135 pdf_array_push_real(ctx, newmediabox, mb.y0);
136 pdf_array_push_real(ctx, newmediabox, mb.x1);
137 pdf_array_push_real(ctx, newmediabox, mb.y1);
138
139 pdf_dict_put(ctx, newpageobj, PDF_NAME(Parent), pages);
140 pdf_dict_put_drop(ctx, newpageobj, PDF_NAME(MediaBox), newmediabox);
141
142 intersect_box(ctx, doc, newpageobj, PDF_NAME(CropBox), mb);
143 intersect_box(ctx, doc, newpageobj, PDF_NAME(BleedBox), mb);
144 intersect_box(ctx, doc, newpageobj, PDF_NAME(TrimBox), mb);
145 intersect_box(ctx, doc, newpageobj, PDF_NAME(ArtBox), mb);
146
147 /* Store page object in new kids array */
148 pdf_drop_obj(ctx, newpageobj);
149 pdf_array_push_drop(ctx, kids, newpageref);
150
151 kidcount++;
152 }
153 }
154 }
155
156 /* Update page count and kids array */
157 pdf_dict_put_int(ctx, pages, PDF_NAME(Count), kidcount);
158 pdf_dict_put_drop(ctx, pages, PDF_NAME(Kids), kids);
159}
160
161int pdfposter_main(int argc, char **argv)
162{
163 char *infile;
164 char *outfile = "out.pdf";
165 char *password = "";
166 int c;
167 pdf_write_options opts = pdf_default_write_options;
168 pdf_document *doc;
169 fz_context *ctx;
170
171 while ((c = fz_getopt(argc, argv, "x:y:")) != -1)
172 {
173 switch (c)
174 {
175 case 'p': password = fz_optarg; break;
176 case 'x': x_factor = atoi(fz_optarg); break;
177 case 'y': y_factor = atoi(fz_optarg); break;
178 default: usage(); break;
179 }
180 }
181
182 if (argc - fz_optind < 1)
183 usage();
184
185 infile = argv[fz_optind++];
186
187 if (argc - fz_optind > 0 &&
188 (strstr(argv[fz_optind], ".pdf") || strstr(argv[fz_optind], ".PDF")))
189 {
190 outfile = argv[fz_optind++];
191 }
192
193 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
194 if (!ctx)
195 {
196 fprintf(stderr, "cannot initialise context\n");
197 exit(1);
198 }
199
200 doc = pdf_open_document(ctx, infile);
201 if (pdf_needs_password(ctx, doc))
202 if (!pdf_authenticate_password(ctx, doc, password))
203 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot authenticate password: %s", infile);
204
205 decimatepages(ctx, doc);
206
207 pdf_save_document(ctx, doc, outfile, &opts);
208
209 pdf_drop_document(ctx, doc);
210 fz_drop_context(ctx);
211 return 0;
212}
213