1/*---------------------------------------------------------------------------*
2 |              PDFlib - A library for generating PDF on the fly             |
3 +---------------------------------------------------------------------------+
4 | Copyright (c) 1997-2004 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 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * Impose multiple PDF documents on a single sheet,
16 * or concatenate multiple PDFs (if no -g option is supplied)
17 * (requires the PDF import library PDI)
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)
28int getopt(int argc, char * const argv[], const char *optstring);
29extern char *optarg;
30extern int optind;
31#elif !defined(WIN32) && !defined(MAC)
32#include <unistd.h>
33#endif
34
35#include "pdflib.h"
36
37/* Array of known page sizes including name, width, and height */
38
39typedef struct { const char *name; float width; float height; } PageSize_s;
40
41PageSize_s PageSizes[] = {
42    {"a0",      2380.0f, 3368.0f},
43    {"a1",      1684.0f, 2380.0f},
44    {"a2",      1190.0f, 1684.0f},
45    {"a3",      842.0f, 1190.0f},
46    {"a4",      595.0f, 842.0f},
47    {"a5",      421.0f, 595.0f},
48    {"a6",      297.0f, 421.0f},
49    {"b5",      501.0f, 709.0f},
50    {"letter",  612.0f, 792.0f},
51    {"legal",   612.0f, 1008.0f},
52    {"ledger",  1224.0f, 792.0f},
53    {"p11x17",  792.0f, 1224.0f}
54};
55
56#define PAGESIZELISTLEN    (sizeof(PageSizes)/sizeof(PageSizes[0]))
57
58static void
59usage(void)
60{
61    fprintf(stderr,
62	"\npdfimpose: impose multiple PDF documents on a single sheet.\n");
63    fprintf(stderr, "(C) PDFlib GmbH and Thomas Merz 2001-2004\n");
64    fprintf(stderr, "usage: pdfimpose [options] pdffiles(s)\n\n");
65    fprintf(stderr, "Available options:\n");
66    fprintf(stderr, "-b            print boxes around imposed pages\n");
67    fprintf(stderr,
68	"-g wxh        number of columns and rows per sheet (default: 1x1)\n");
69    fprintf(stderr, "-l            landscape mode\n");
70    fprintf(stderr, "-n            start each document on a new page\n");
71    fprintf(stderr, "-o <file>     output file\n");
72    fprintf(stderr, "-p <pagesize> page format (a0-a6, letter, legal, etc.)\n");
73    fprintf(stderr, "-q            quiet mode: do not emit info messages\n");
74    fprintf(stderr, "-v <version>  PDF output version: 1.3, 1.4, or 1.5\n");
75
76    exit(1);
77}
78
79int
80main(int argc, char *argv[])
81{
82    char	*pdffilename = NULL;
83    char	*pdfversion = NULL;
84    PDF		*p;
85    int		opt;
86    int		doc, page;
87    int		pageno, docpages;
88    char	*filename;
89    int		quiet = 0, landscape = 0, boxes = 0, newpage = 0;
90    int		cols = 1, rows = 1;
91    int		c = 0, r = 0;
92    float	sheetwidth = 595.0f, sheetheight = 842.0f;
93    float	width, height, scale = 1.0f;
94    float	rowheight = 0.0f, colwidth = 0.0f;
95
96    while ((opt = getopt(argc, argv, "bg:lnp:o:qv:")) != -1)
97	switch (opt) {
98	    case 'b':
99		boxes = 1;
100		break;
101
102	    case 'g':
103		if (sscanf(optarg, "%dx%d", &rows, &cols) != 2) {
104		    fprintf(stderr, "Error: Couldn't parse -g option.\n");
105		    usage();
106		}
107		if (rows <= 0 || cols <= 0) {
108		    fprintf(stderr, "Bad row or column number.\n");
109		    usage();
110		}
111		break;
112
113	    case 'l':
114		landscape = 1;
115		break;
116
117	    case 'n':
118		newpage = 1;
119		break;
120
121	    case 'p':
122		for(c = 0; c < PAGESIZELISTLEN; c++)
123		if (!strcmp((const char *) optarg, PageSizes[c].name)) {
124		    sheetheight = PageSizes[c].height;
125		    sheetwidth = PageSizes[c].width;
126		    break;
127		}
128		if (c == PAGESIZELISTLEN) {  /* page size name not found */
129		    fprintf(stderr, "Error: Unknown page size '%s'.\n", optarg);
130		    usage();
131		}
132		break;
133
134	    case 'o':
135		pdffilename = optarg;
136		break;
137
138	    case 'v':
139		pdfversion = optarg;
140		if (strcmp(pdfversion, "1.3") && strcmp(pdfversion, "1.4") &&
141		    strcmp(pdfversion, "1.5")) {
142		    fprintf(stderr, "Error: bad PDF version number '%s'.\n",
143		    	optarg);
144		    usage();
145		}
146
147		break;
148
149	    case 'q':
150		quiet = 1;
151		break;
152
153	    case '?':
154	    default:
155		usage();
156	}
157
158    if (optind == argc) {
159	fprintf(stderr, "Error: no PDF files given.\n");
160	usage();
161    }
162
163    if (pdffilename == NULL) {
164	fprintf(stderr, "Error: no PDF output file given.\n");
165	usage();
166    }
167
168    p = PDF_new();
169
170    if (pdfversion)
171	PDF_set_parameter(p, "compatibility", pdfversion);
172
173    if (PDF_open_file(p, pdffilename) == -1) {
174	fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
175	exit(1);
176    }
177
178    PDF_set_info(p, "Creator", "pdfimpose by PDFlib GmbH");
179
180    PDF_set_parameter(p, "openaction", "fitpage");
181
182    if (!quiet)
183	PDF_set_parameter(p, "pdiwarning", "true"); /* report PDI problems */
184
185    /* multi-page imposition: calculate scaling factor and cell dimensions */
186    if (rows != 1 || cols != 1) {
187	if (landscape) {
188	    height = sheetheight;
189	    sheetheight = sheetwidth;
190	    sheetwidth = height;
191	}
192
193	if (rows > cols)
194	    scale = 1.0f / rows;
195	else
196	    scale = 1.0f / cols;
197
198	rowheight = sheetheight * scale;
199	colwidth = sheetwidth * scale;
200    }
201
202    /* process all PDF documents */
203    while (optind++ < argc) {
204	filename = argv[optind-1];
205
206	if (!quiet)
207	    fprintf(stderr, "Imposing '%s'...\n", filename);
208
209	if ((doc = PDF_open_pdi(p, filename, "", 0)) == -1) {
210	    if (quiet)
211		fprintf(stderr, "Error: %s.\n", PDF_get_errmsg(p));
212	    continue;
213	}
214
215	/* query number of pages in the document */
216	docpages = (int) PDF_get_pdi_value(p, "/Root/Pages/Count", doc, -1, 0);
217
218	/* single cell only: concatenate, using original page dimensions */
219	if (rows == 1 && cols == 1) {
220	    /* open all pages and add to the output file */
221	    for (pageno = 1; pageno <= docpages ; pageno++) {
222
223		page = PDF_open_pdi_page(p, doc, pageno, "");
224
225		if (page == -1) {
226		    /* we'll get an exception in verbose mode anyway */
227		    if (quiet)
228			fprintf(stderr,
229			    "Couldn't open page %d of PDF file '%s' (%s)\n",
230			    pageno, filename, PDF_get_errmsg(p));
231		    break;
232		}
233
234		sheetwidth = PDF_get_pdi_value(p, "width", doc, page, 0);
235		sheetheight = PDF_get_pdi_value(p, "height", doc, page, 0);
236
237		PDF_begin_page(p, sheetwidth, sheetheight);
238
239		/* define bookmark with filename */
240		if (pageno == 1)
241		    PDF_add_bookmark(p, argv[optind-1], 0, 0);
242
243		PDF_place_pdi_page(p, page, 0.0f, 0.0f, 1.0f, 1.0f);
244		PDF_close_pdi_page(p, page);
245		PDF_end_page(p);
246	    }
247
248	} else {		/* impose multiple pages */
249
250	    if (newpage)
251		r = c = 0;
252
253	    /* open all pages and add to the output file */
254	    for (pageno = 1; pageno <= docpages ; pageno++) {
255
256		page = PDF_open_pdi_page(p, doc, pageno, "");
257
258		if (page == -1) {
259		    /* we'll get an exception in verbose mode anyway */
260		    if (quiet)
261			fprintf(stderr,
262			    "Couldn't open page %d of PDF file '%s' (%s)\n",
263			    pageno, filename, PDF_get_errmsg(p));
264		    break;
265		}
266
267		/* start a new page */
268		if (r == 0 && c == 0)
269		    PDF_begin_page(p, sheetwidth, sheetheight);
270
271		/* define bookmark with filename */
272		if (pageno == 1)
273		    PDF_add_bookmark(p, argv[optind-1], 0, 0);
274
275		width = PDF_get_pdi_value(p, "width", doc, page, 0);
276		height = PDF_get_pdi_value(p, "height", doc, page, 0);
277
278		/*
279		 * The save/restore pair is required to get the clipping right,
280		 * and helps PostScript printing manage its memory efficiently.
281		 */
282		PDF_save(p);
283		PDF_rect(p, c * colwidth, sheetheight - (r + 1) * rowheight,
284		    colwidth, rowheight);
285		PDF_clip(p);
286
287		PDF_setcolor(p, "stroke", "gray", 0.0f, 0.0f, 0.0f, 0.0f);
288
289		/* TODO: adjust scaling factor if page doesn't fit into  cell */
290		PDF_place_pdi_page(p, page,
291		    c * colwidth, sheetheight - (r + 1) * rowheight,
292		    scale, scale);
293
294		PDF_close_pdi_page(p, page);
295
296		/* only half of the linewidth will be drawn due to clip() */
297		if (boxes) {
298		    PDF_setlinewidth(p, 1.0f * scale);
299		    PDF_rect(p, c * colwidth,
300			sheetheight - (r + 1) * rowheight,
301			colwidth, rowheight);
302		    PDF_stroke(p);
303		}
304
305		PDF_restore(p);
306
307		c++;
308		if (c == cols) {
309		    c = 0;
310		    r++;
311		}
312		if (r == rows) {
313		    r = 0;
314		    PDF_end_page(p);
315		}
316	    }
317	}
318
319	PDF_close_pdi(p, doc);
320    }
321
322    /* finish last page if multi-page imposition */
323    if ((rows != 1 || cols != 1) && (r != 0 || c != 0))
324	PDF_end_page(p);
325
326    PDF_close(p);
327    PDF_delete(p);
328    exit(0);
329}
330