/*---------------------------------------------------------------------------*
 |              PDFlib - A library for generating PDF on the fly             |
 +---------------------------------------------------------------------------+
 | Copyright (c) 1997-2002 PDFlib GmbH and Thomas Merz. All rights reserved. |
 +---------------------------------------------------------------------------+
 |    This software is NOT in the public domain.  It can be used under two   |
 |    substantially different licensing terms:                               |
 |                                                                           |
 |    The commercial license is available for a fee, and allows you to       |
 |    - ship a commercial product based on PDFlib                            |
 |    - implement commercial Web services with PDFlib                        |
 |    - distribute (free or commercial) software when the source code is     |
 |      not made available                                                   |
 |    Details can be found in the file PDFlib-license.pdf.                   |
 |                                                                           |
 |    The "Aladdin Free Public License" doesn't require any license fee,     |
 |    and allows you to                                                      |
 |    - develop and distribute PDFlib-based software for which the complete  |
 |      source code is made available                                        |
 |    - redistribute PDFlib non-commercially under certain conditions        |
 |    - redistribute PDFlib on digital media for a fee if the complete       |
 |      contents of the media are freely redistributable                     |
 |    Details can be found in the file aladdin-license.pdf.                  |
 |                                                                           |
 |    These conditions extend to ports to other programming languages.       |
 |    PDFlib is distributed with no warranty of any kind. Commercial users,  |
 |    however, will receive warranty and support statements in writing.      |
 *---------------------------------------------------------------------------*/

/* $Id: pdfimpose.c,v 1.13.2.1 2002/01/07 18:26:29 tm Exp $
 *
 * Impose multiple PDF documents on a single sheet,
 * or concatenate multiple PDFs (if no -g option is supplied)
 * (requires the PDF import library PDI)
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(__CYGWIN32__)
#include <getopt.h>
#elif defined(WIN32)
int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind;
#elif !defined(WIN32) && !defined(MAC)
#include <unistd.h>
#endif

#include "pdflib.h"

/* Array of known page sizes including name, width, and height */

typedef struct { const char *name; float width; float height; } PageSize_s;

PageSize_s PageSizes[] = {
    {"a0",      2380.0f, 3368.0f},
    {"a1",      1684.0f, 2380.0f},
    {"a2",      1190.0f, 1684.0f},
    {"a3",      842.0f, 1190.0f},
    {"a4",      595.0f, 842.0f},
    {"a5",      421.0f, 595.0f},
    {"a6",      297.0f, 421.0f},
    {"b5",      501.0f, 709.0f},
    {"letter",  612.0f, 792.0f},
    {"legal",   612.0f, 1008.0f},
    {"ledger",  1224.0f, 792.0f},
    {"p11x17",  792.0f, 1224.0f}
};

#define PAGESIZELISTLEN    (sizeof(PageSizes)/sizeof(PageSizes[0]))

static void
usage(void)
{
    fprintf(stderr,
	"pdfimpose: impose multiple PDF documents on a single sheet.\n");
    fprintf(stderr, "(C) PDFlib GmbH and Thomas Merz 2001\n");
    fprintf(stderr, "usage: pdfimpose [options] pdffiles(s)\n");
    fprintf(stderr, "Available options:\n");
    fprintf(stderr, "-b            print boxes around imposed pages\n");
    fprintf(stderr,
	"-g wxh        number of columns and rows per sheet (default: 1x1)\n");
    fprintf(stderr, "-l            landscape mode\n");
    fprintf(stderr, "-n            start each document on a new page\n");
    fprintf(stderr, "-p <pagesize> page format (a0-a6, letter, legal, etc.)\n");
    fprintf(stderr, "-o <file>     output file\n");
    fprintf(stderr, "-q            quiet mode: do not emit info messages\n");

    exit(1);
}

int
main(int argc, char *argv[])
{
    char	*pdffilename = NULL;
    PDF		*p;
    int		opt;
    int		doc, page;
    int		pageno, docpages;
    char	*filename;
    int		quiet = 0, landscape = 0, boxes = 0, newpage = 0;
    int		cols = 1, rows = 1;
    int		c = 0, r = 0;
    float	sheetwidth = 595.0f, sheetheight = 842.0f;
    float	width, height, scale = 1.0f;
    float	rowheight = 0.0f, colwidth = 0.0f;
    
    while ((opt = getopt(argc, argv, "bg:lnp:o:q")) != -1)
	switch (opt) {
	    case 'b':
		boxes = 1;
		break;

	    case 'g':
		if (sscanf(optarg, "%dx%d", &rows, &cols) != 2) {
		    fprintf(stderr, "Error: Couldn't parse -g option.\n");
		    usage();
		}
		if (rows <= 0 || cols <= 0) {
		    fprintf(stderr, "Bad row or column number.\n");
		    usage();
		}
		break;

	    case 'l':
		landscape = 1;
		break;

	    case 'n':
		newpage = 1;
		break;

	    case 'p':
		for(c = 0; c < PAGESIZELISTLEN; c++)
		if (!strcmp((const char *) optarg, PageSizes[c].name)) {
		    sheetheight = PageSizes[c].height;
		    sheetwidth = PageSizes[c].width;
		    break;
		}
		if (c == PAGESIZELISTLEN) {  /* page size name not found */
		    fprintf(stderr, "Error: Unknown page size %s.\n", optarg);
		    usage();
		}
		break;

	    case 'o':
		pdffilename = optarg;
		break;

	    case 'q':
		quiet = 1;
		break;

	    case '?':
	    default:
		usage();
	}

    if (optind == argc) {
	fprintf(stderr, "Error: no PDF files given.\n");
	usage();
    }

    if (pdffilename == NULL) {
	fprintf(stderr, "Error: no PDF output file given.\n");
	usage();
    }

    p = PDF_new();

    if (PDF_open_file(p, pdffilename) == -1) {
	fprintf(stderr, "Error: cannot open PDF output file '%s'.\n",
	    pdffilename);
	exit(1);
    }

    PDF_set_info(p, "Creator", "pdfimpose by PDFlib GmbH");

    PDF_set_parameter(p, "openaction", "fitpage");

    if (!quiet)
	PDF_set_parameter(p, "pdiwarning", "true"); /* report PDI problems */

    /* multi-page imposition: calculate scaling factor and cell dimensions */
    if (rows != 1 || cols != 1) {
	if (landscape) {
	    height = sheetheight;
	    sheetheight = sheetwidth;
	    sheetwidth = height;
	}

	if (rows > cols)
	    scale = 1.0f / rows;
	else
	    scale = 1.0f / cols;

	rowheight = sheetheight * scale;
	colwidth = sheetwidth * scale;
    }

    /* process all PDF documents */
    while (optind++ < argc) {
	filename = argv[optind-1];

	if (!quiet)
	    fprintf(stderr, "Imposing '%s'...\n", filename);

	if ((doc = PDF_open_pdi(p, filename, "", 0)) == -1) {
	    if (quiet)
		fprintf(stderr, "Couldn't open PDF input file '%s'\n",
		    filename);
	    continue;
	}

	/* query number of pages in the document */
	docpages = (int) PDF_get_pdi_value(p, "/Root/Pages/Count", doc, -1, 0);

	/* single cell only: concatenate, using original page dimensions */
	if (rows == 1 && cols == 1) {
	    /* open all pages and add to the output file */
	    for (pageno = 1; pageno <= docpages ; pageno++) {

		page = PDF_open_pdi_page(p, doc, pageno, "");

		if (page == -1) {
		    /* we'll get an exception in verbose mode anyway */
		    if (quiet)
			fprintf(stderr,
			    "Couldn't open page %d of PDF file '%s'\n",
			    pageno, filename);
		    break;
		}

		sheetwidth = PDF_get_pdi_value(p, "width", doc, page, 0);
		sheetheight = PDF_get_pdi_value(p, "height", doc, page, 0);

		PDF_begin_page(p, sheetwidth, sheetheight);

		/* define bookmark with filename */
		if (pageno == 1)
		    PDF_add_bookmark(p, argv[optind-1], 0, 0);

		PDF_place_pdi_page(p, page, 0.0f, 0.0f, 1.0f, 1.0f);
		PDF_close_pdi_page(p, page);
		PDF_end_page(p);
	    }

	} else {		/* impose multiple pages */

	    if (newpage)
		r = c = 0;

	    /* open all pages and add to the output file */
	    for (pageno = 1; pageno <= docpages ; pageno++) {

		page = PDF_open_pdi_page(p, doc, pageno, "");

		if (page == -1) {
		    /* we'll get an exception in verbose mode anyway */
		    if (quiet)
			fprintf(stderr,
			    "Couldn't open page %d of PDF file '%s'\n",
			    pageno, filename);
		    break;
		}

		/* start a new page */
		if (r == 0 && c == 0)
		    PDF_begin_page(p, sheetwidth, sheetheight);
		
		/* define bookmark with filename */
		if (pageno == 1)
		    PDF_add_bookmark(p, argv[optind-1], 0, 0);

		width = PDF_get_pdi_value(p, "width", doc, page, 0);
		height = PDF_get_pdi_value(p, "height", doc, page, 0);

		/*
		 * The save/restore pair is required to get the clipping right,
		 * and helps PostScript printing manage its memory efficiently.
		 */
		PDF_save(p);
		PDF_rect(p, c * colwidth, sheetheight - (r + 1) * rowheight,
		    colwidth, rowheight);
		PDF_clip(p);

		PDF_setcolor(p, "stroke", "gray", 0.0f, 0.0f, 0.0f, 0.0f);

		/* TODO: adjust scaling factor if page doesn't fit into  cell */
		PDF_place_pdi_page(p, page,
		    c * colwidth, sheetheight - (r + 1) * rowheight,
		    scale, scale);

		PDF_close_pdi_page(p, page);

		/* only half of the linewidth will be drawn due to clip() */
		if (boxes) {
		    PDF_setlinewidth(p, 1.0f * scale);
		    PDF_rect(p, c * colwidth,
			sheetheight - (r + 1) * rowheight,
			colwidth, rowheight);
		    PDF_stroke(p);
		}

		PDF_restore(p);

		c++;
		if (c == cols) {
		    c = 0;
		    r++;
		}
		if (r == rows) {
		    r = 0;
		    PDF_end_page(p);
		}
	    }
	}

	PDF_close_pdi(p, doc);
    }

    /* finish last page if multi-page imposition */
    if ((rows != 1 || cols != 1) && (r != 0 || c != 0))
	PDF_end_page(p);

    PDF_close(p);
    PDF_delete(p);
    exit(0);
}
