1 /*---------------------------------------------------------------------------*
2  |              PDFlib - A library for generating PDF on the fly             |
3  +---------------------------------------------------------------------------+
4  | Copyright (c) 2000-2006 Thomas Merz and PDFlib GmbH. All rights reserved. |
5  +---------------------------------------------------------------------------+
6  |                                                                           |
7  |    This software is subject to the PDFlib license. It is NOT in the       |
8  |    public domain. Extended versions and commercial licenses are           |
9  |    available, please check http://www.pdflib.com.                         |
10  |                                                                           |
11  *---------------------------------------------------------------------------*/
12 
13 /* $Id: pdfimpose.c,v 1.22.2.2 2008/05/27 09:10:37 kurt Exp $
14  *
15  * Impose multiple PDF documents on a single sheet,
16  * or concatenate multiple PDFs (if no -g option is supplied),
17  * or create PDF/A from one or more PDF/A input documents.
18  *
19  */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #if defined(__CYGWIN32__)
26 #include <getopt.h>
27 #elif defined(WIN32)
28 int getopt(int argc, char * const argv[], const char *optstring);
29 extern char *optarg;
30 extern int optind;
31 #elif !defined(WIN32) && !defined(MAC)
32 #include <unistd.h>
33 #endif
34 
35 #include "pdflib.h"
36 
37 static void
usage(void)38 usage(void)
39 {
40     fprintf(stderr,
41 
42 "\npdfimpose: impose multiple PDF documents on a single sheet.\n"
43 "(c) PDFlib GmbH 2001-2006 www.pdflib.com\n"
44 "\n"
45 "usage: pdfimpose [options] pdffiles(s)\n"
46 "\n"
47 "Available options:\n"
48 "-a level      PDF/A compatibility level: \"PDF/A-1b:2005\" or \"none\""
49 "              default: none\n"
50 "-b            print boxes around imposed pages\n"
51 "-g wxh        number of columns and rows per sheet (default: 1x1)\n"
52 "-I <filename> name of a PDF/A file from which to copy the output intent\n"
53 "              default: first PDF/A input file\n"
54 "-l            landscape mode\n"
55 "-n            start each document on a new page\n"
56 "-o <filename> PDF output file name\n"
57 "-p <pagesize> page format (a0-a6, letter, legal, etc.)\n"
58 "-q            quiet mode: do not emit info messages\n"
59 "-v <version>  PDF output version: 1.3, 1.4, 1.5, 1.6, 1.7\n");
60 
61     exit(1);
62 }
63 
64 int
main(int argc,char * argv[])65 main(int argc, char *argv[])
66 {
67     char	*pdffilename = NULL;
68     char	*pdfversion = NULL;
69     PDF		*p;
70     int		opt;
71     int		doc, page;
72     int		pageno, docpages;
73     char	*filename, *intent=NULL;
74     int		quiet = 0, landscape = 0, boxes = 0, newpage = 0;
75     int		cols = 1, rows = 1;
76     int		c = 0, r = 0;
77     double	sheetwidth = 595.0, sheetheight = 842.0;
78     double	width, height, scale = 1.0f;
79     double	rowheight = 0.0f, colwidth = 0.0f;
80     char	*pagesize = NULL;
81     char 	*pdfalevel = NULL;
82     char	optlist[1024];
83 
84     while ((opt = getopt(argc, argv, "a:bg:I:lnp:o:qv:")) != -1)
85 	switch (opt) {
86 	    case 'a':
87 		if (strcmp(optarg, "none"))
88 		    pdfalevel = optarg;
89 		break;
90 
91 	    case 'b':
92 		boxes = 1;
93 		break;
94 
95 	    case 'g':
96 		if (sscanf(optarg, "%dx%d", &rows, &cols) != 2) {
97 		    fprintf(stderr, "Error: Couldn't parse -g option.\n");
98 		    usage();
99 		}
100 		if (rows <= 0 || cols <= 0) {
101 		    fprintf(stderr, "Bad row or column number.\n");
102 		    usage();
103 		}
104 		break;
105 
106 	    case 'I':
107 		intent = optarg;
108 		break;
109 
110 	    case 'l':
111 		landscape = 1;
112 		break;
113 
114 	    case 'n':
115 		newpage = 1;
116 		break;
117 
118 	    case 'p':
119 		pagesize = optarg;
120 		break;
121 
122 	    case 'o':
123 		pdffilename = optarg;
124 		break;
125 
126 	    case 'v':
127 		pdfversion = optarg;
128 		break;
129 
130 	    case 'q':
131 		quiet = 1;
132 		break;
133 
134 	    case '?':
135 	    default:
136 		usage();
137 	}
138 
139     if (optind == argc)
140     {
141 	fprintf(stderr, "Error: no PDF files given.\n");
142 	usage();
143     }
144 
145     if (pdffilename == NULL)
146     {
147 	fprintf(stderr, "Error: no PDF output file given.\n");
148 	usage();
149     }
150 
151     p = PDF_new();
152 
153     PDF_TRY(p)
154     {
155 
156 	optlist[0] = 0;
157 
158 	if (pdfversion)
159 	    sprintf(optlist, "compatibility=%s ", pdfversion);
160 
161 	if (pdfalevel)
162 	    sprintf(optlist+strlen(optlist), "pdfa=%s ", pdfalevel);
163 
164 	if (PDF_begin_document(p, pdffilename, 0, optlist) == -1)
165 	{
166 	    fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
167 	    exit(1);
168 	}
169 
170 	if (pdfalevel)
171 	{
172 	    int res;
173 
174 	    /* Use intent from the first PDF if none specified */
175 	    if (!intent)
176 		intent = argv[optind];
177 
178 	    if (!quiet)
179 		fprintf(stderr, "Using output intent from file '%s'.\n",
180 		    intent);
181 
182 	    if ((doc = PDF_open_pdi_document(p, intent, 0, "infomode")) == -1)
183 	    {
184 		if (!quiet)
185 		    fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
186 		exit(99);
187 	    }
188 
189 	    res = (int) PDF_pcos_get_number(p, doc, "type:/Root/OutputIntents");
190 
191 	    if (res == pcos_ot_array)
192 	    {
193 		res = PDF_process_pdi(p, doc, 0, "action=copyoutputintent");
194 		if (res == -1)
195 		{
196 		    if (!quiet)
197 			fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
198 		    exit(99);
199 		}
200 		PDF_close_pdi_document(p, doc);
201 	    }
202 	    else
203 	    {
204 		fprintf(stderr,
205 		    "No output intent found.\n");
206 	    }
207 	}
208 
209 	PDF_set_info(p, "Creator", "pdfimpose by PDFlib GmbH");
210 
211 	PDF_set_parameter(p, "openaction", "fitpage");
212 
213 	/* multi-page imposition: calculate scaling factor and cell dimensions*/
214 	if (rows != 1 || cols != 1)
215 	{
216 	    if (landscape)
217 	    {
218 		height = sheetheight;
219 		sheetheight = sheetwidth;
220 		sheetwidth = height;
221 	    }
222 
223 	    if (rows > cols)
224 		scale = 1.0f / rows;
225 	    else
226 		scale = 1.0f / cols;
227 
228 	    rowheight = sheetheight * scale;
229 	    colwidth = sheetwidth * scale;
230 	}
231 
232 	/* process all PDF documents */
233 	while (optind++ < argc)
234 	{
235 	    filename = argv[optind-1];
236 
237 	    if (!quiet)
238 		fprintf(stderr, "Imposing '%s'...\n", filename);
239 
240 	    if ((doc = PDF_open_pdi_document(p, filename, 0, "")) == -1)
241 	    {
242 		if (!quiet)
243 		    fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
244 		continue;
245 	    }
246 
247 	    /* query number of pages in the document */
248 	    docpages = (int) PDF_pcos_get_number(p, doc, "/Root/Pages/Count");
249 
250 	    /* single cell only: concatenate */
251 	    if (rows == 1 && cols == 1)
252 	    {
253 		/* open all pages and add to the output file */
254 		for (pageno = 1; pageno <= docpages ; pageno++)
255 		{
256 
257 		    page = PDF_open_pdi_page(p, doc, pageno, "");
258 
259 		    if (page == -1)
260 		    {
261 			if (!quiet)
262 			    fprintf(stderr, "Couldn't open page %d (%s)\n",
263 				pageno, PDF_get_errmsg(p));
264 			break;
265 		    }
266 
267 		    sheetwidth = PDF_pcos_get_number(p, doc,
268 				    "pages[%d]/width", pageno-1);
269 		    sheetheight = PDF_pcos_get_number(p, doc,
270 				    "pages[%d]/height", pageno-1);
271 
272 		    optlist[0] = 0;
273 
274 		    if (pagesize)
275 		    {
276 			sprintf(optlist, landscape ?
277 				"width=%s.height height=%s.width " :
278 				"width=%s.width height=%s.height ",
279 				pagesize, pagesize);
280 		    }
281 
282 		    PDF_begin_page_ext(p, sheetwidth, sheetheight, optlist);
283 
284 		    /* define bookmark with filename */
285 		    if (pageno == 1)
286 			PDF_create_bookmark(p, argv[optind-1], 0, "");
287 
288 		    PDF_fit_pdi_page(p, page, 0.0f, 0.0f, "");
289 		    PDF_close_pdi_page(p, page);
290 		    PDF_end_page_ext(p, "");
291 		}
292 
293 	    }
294 	    else
295 	    {		/* impose multiple pages */
296 
297 		if (newpage)
298 		    r = c = 0;
299 
300 		/* open all pages and add to the output file */
301 		for (pageno = 1; pageno <= docpages ; pageno++) {
302 
303 		    page = PDF_open_pdi_page(p, doc, pageno, "");
304 
305 		    if (page == -1)
306 		    {
307 			if (!quiet)
308 			    fprintf(stderr, "Couldn't open page %d (%s)\n",
309 				pageno, PDF_get_errmsg(p));
310 			break;
311 		    }
312 
313 		    /* start a new page */
314 		    if (r == 0 && c == 0)
315 			PDF_begin_page_ext(p, sheetwidth, sheetheight, "");
316 
317 		    /* define bookmark with filename */
318 		    if (pageno == 1)
319 			PDF_create_bookmark(p, argv[optind-1], 0, "");
320 
321 		    width = PDF_pcos_get_number(p, doc,
322 				    "pages[%d]/width", pageno-1);
323 		    height = PDF_pcos_get_number(p, doc,
324 				    "pages[%d]/height", pageno-1);
325 
326 		    /*
327 		     * The save/restore pair is required to get the clipping
328 		     * right, and helps PostScript printing manage its memory
329 		     * efficiently.
330 		     */
331 		    PDF_save(p);
332 		    PDF_rect(p, c * colwidth, sheetheight - (r + 1) * rowheight,
333 			colwidth, rowheight);
334 		    PDF_clip(p);
335 
336 		    if (pdfalevel)
337 			PDF_setcolor(p,"stroke", "lab", 0.0f, 0.0f, 0.0f, 0.0f);
338 		    else
339 			PDF_setcolor(p,"stroke","gray", 0.0f, 0.0f, 0.0f, 0.0f);
340 
341 		    sprintf(optlist,
342 			    "boxsize {%f %f} position 0 fitmethod meet",
343 			    colwidth, rowheight);
344 
345 		    PDF_fit_pdi_page(p, page,
346 			c * colwidth, sheetheight - (r + 1) * rowheight,
347 			optlist);
348 
349 		    PDF_close_pdi_page(p, page);
350 
351 		    /* only half of the linewidth will be drawn due to clip() */
352 		    if (boxes) {
353 			PDF_setlinewidth(p, 1.0f * scale);
354 			PDF_rect(p, c * colwidth,
355 			    sheetheight - (r + 1) * rowheight,
356 			    colwidth, rowheight);
357 			PDF_stroke(p);
358 		    }
359 
360 		    PDF_restore(p);
361 
362 		    c++;
363 		    if (c == cols)
364 		    {
365 			c = 0;
366 			r++;
367 		    }
368 		    if (r == rows)
369 		    {
370 			r = 0;
371 			PDF_end_page_ext(p, "");
372 		    }
373 		}
374 	    }
375 
376 	    PDF_close_pdi_document(p, doc);
377 	}
378 
379 	/* finish last page if multi-page imposition */
380 	if ((rows != 1 || cols != 1) && (r != 0 || c != 0))
381 	    PDF_end_page_ext(p, "");
382 
383 	PDF_end_document(p, "");
384     }
385 
386     PDF_CATCH(p)
387     {
388         printf("\npdfimpose: error while creating PDF output (%s(): %s)\n",
389 		PDF_get_apiname(p), PDF_get_errmsg(p));
390         PDF_delete(p);
391         exit(99);
392     }
393 
394     PDF_delete(p);
395     exit(0);
396 }
397