1/*
2 * PDF merge tool: Tool for merging pdf content.
3 *
4 * Simple test bed to work with merging pages from multiple PDFs into a single PDF.
5 */
6
7#include "mupdf/fitz.h"
8#include "mupdf/pdf.h"
9
10#include <stdlib.h>
11#include <stdio.h>
12
13static void usage(void)
14{
15 fprintf(stderr,
16 "usage: mutool merge [-o output.pdf] [-O options] input.pdf [pages] [input2.pdf] [pages2] ...\n"
17 "\t-o -\tname of PDF file to create\n"
18 "\t-O -\tcomma separated list of output options\n"
19 "\tinput.pdf\tname of input file from which to copy pages\n"
20 "\tpages\tcomma separated list of page numbers and ranges\n\n"
21 );
22 fputs(fz_pdf_write_options_usage, stderr);
23 exit(1);
24}
25
26static fz_context *ctx = NULL;
27static pdf_document *doc_des = NULL;
28static pdf_document *doc_src = NULL;
29
30static void page_merge(int page_from, int page_to, pdf_graft_map *graft_map)
31{
32 pdf_obj *page_ref;
33 pdf_obj *page_dict = NULL;
34 pdf_obj *obj;
35 pdf_obj *ref = NULL;
36 int i;
37
38 /* Copy as few key/value pairs as we can. Do not include items that reference other pages. */
39 static pdf_obj * const copy_list[] = {
40 PDF_NAME(Contents),
41 PDF_NAME(Resources),
42 PDF_NAME(MediaBox),
43 PDF_NAME(CropBox),
44 PDF_NAME(BleedBox),
45 PDF_NAME(TrimBox),
46 PDF_NAME(ArtBox),
47 PDF_NAME(Rotate),
48 PDF_NAME(UserUnit)
49 };
50
51 fz_var(ref);
52 fz_var(page_dict);
53
54 fz_try(ctx)
55 {
56 page_ref = pdf_lookup_page_obj(ctx, doc_src, page_from - 1);
57 pdf_flatten_inheritable_page_items(ctx, page_ref);
58
59 /* Make a new page object dictionary to hold the items we copy from the source page. */
60 page_dict = pdf_new_dict(ctx, doc_des, 4);
61
62 pdf_dict_put(ctx, page_dict, PDF_NAME(Type), PDF_NAME(Page));
63
64 for (i = 0; i < nelem(copy_list); i++)
65 {
66 obj = pdf_dict_get(ctx, page_ref, copy_list[i]);
67 if (obj != NULL)
68 pdf_dict_put_drop(ctx, page_dict, copy_list[i], pdf_graft_mapped_object(ctx, graft_map, obj));
69 }
70
71 /* Add the page object to the destination document. */
72 ref = pdf_add_object(ctx, doc_des, page_dict);
73
74 /* Insert it into the page tree. */
75 pdf_insert_page(ctx, doc_des, page_to - 1, ref);
76 }
77 fz_always(ctx)
78 {
79 pdf_drop_obj(ctx, page_dict);
80 pdf_drop_obj(ctx, ref);
81 }
82 fz_catch(ctx)
83 {
84 fz_rethrow(ctx);
85 }
86}
87
88static void merge_range(const char *range)
89{
90 int start, end, i, count;
91 pdf_graft_map *graft_map;
92
93 count = pdf_count_pages(ctx, doc_src);
94 graft_map = pdf_new_graft_map(ctx, doc_des);
95
96 fz_try(ctx)
97 {
98 while ((range = fz_parse_page_range(ctx, range, &start, &end, count)))
99 {
100 if (start < end)
101 for (i = start; i <= end; ++i)
102 page_merge(i, -1, graft_map);
103 else
104 for (i = start; i >= end; --i)
105 page_merge(i, -1, graft_map);
106 }
107 }
108 fz_always(ctx)
109 {
110 pdf_drop_graft_map(ctx, graft_map);
111 }
112 fz_catch(ctx)
113 {
114 fz_rethrow(ctx);
115 }
116}
117
118int pdfmerge_main(int argc, char **argv)
119{
120 pdf_write_options opts = pdf_default_write_options;
121 char *output = "out.pdf";
122 char *flags = "";
123 char *input;
124 int c;
125
126 while ((c = fz_getopt(argc, argv, "o:O:")) != -1)
127 {
128 switch (c)
129 {
130 case 'o': output = fz_optarg; break;
131 case 'O': flags = fz_optarg; break;
132 default: usage(); break;
133 }
134 }
135
136 if (fz_optind == argc)
137 usage();
138
139 ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);
140 if (!ctx)
141 {
142 fprintf(stderr, "error: Cannot initialize MuPDF context.\n");
143 exit(1);
144 }
145
146 pdf_parse_write_options(ctx, &opts, flags);
147
148 fz_try(ctx)
149 {
150 doc_des = pdf_create_document(ctx);
151 }
152 fz_catch(ctx)
153 {
154 fprintf(stderr, "error: Cannot create destination document.\n");
155 fz_flush_warnings(ctx);
156 fz_drop_context(ctx);
157 exit(1);
158 }
159
160 /* Step through the source files */
161 while (fz_optind < argc)
162 {
163 input = argv[fz_optind++];
164 doc_src = pdf_open_document(ctx, input);
165
166 fz_try(ctx)
167 {
168 if (fz_optind == argc || !fz_is_page_range(ctx, argv[fz_optind]))
169 merge_range("1-N");
170 else
171 merge_range(argv[fz_optind++]);
172 }
173 fz_always(ctx)
174 pdf_drop_document(ctx, doc_src);
175 fz_catch(ctx)
176 fprintf(stderr, "error: Cannot merge document '%s'.\n", input);
177 }
178
179 if (fz_optind == argc)
180 {
181 fz_try(ctx)
182 pdf_save_document(ctx, doc_des, output, &opts);
183 fz_catch(ctx)
184 fprintf(stderr, "error: Cannot save output file: '%s'.\n", output);
185 }
186
187 pdf_drop_document(ctx, doc_des);
188 fz_flush_warnings(ctx);
189 fz_drop_context(ctx);
190 return 0;
191}
192