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 | |
13 | static 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 | |
26 | static fz_context *ctx = NULL; |
27 | static pdf_document *doc_des = NULL; |
28 | static pdf_document *doc_src = NULL; |
29 | |
30 | static 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 | |
88 | static 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 | |
118 | int 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 | |