/*
 * PDF impose:
 * Import all pages from one more existing PDFs and place col x row pages
 * on each sheet of the output PDF (imposition). If there are annotations
 * on an imported page these are also imported and scaled or rotated as
 * required.
 * 
 * Required software: PDFlib+PDI or PDFlib Personalization Server (PPS)
 * Required data: PDF documents
 */

using System;
using System.Text;
using PDFlib_dotnet;

class starter_pdfimpose {
    static void Main(string[] args)
    {
        PDFlib p = null;

        /* This is where the data files are. Adjust as necessary. */
        String searchpath = "../../../data";
        // By default annotations are also imported. In some cases this
        // requires the Noto fonts for creating annotation appearance streams.
        // We therefore set the searchpath to also point to the font directory.

        String fontpath = "../../../../resource/font";

        String outfile = "starter_pdfimpose.pdf";
        String title = "PDF Impose";

        string[] pdffiles =
            {
                "markup_annotations.pdf",
                "pCOS-datasheet.pdf"
            };
        int i, col = 0, row = 0;
        StringBuilder optlist = new StringBuilder();
        double scale = 1;          // scaling factor of a page
        double rowheight;          // row height for the page to be placed
        double colwidth;           // column width for the page to be placed

        double sheetwidth = 595;   // width of the sheet
        double sheetheight = 842;  // height of the sheet
        int maxcol=2, maxrow=2;    // maxcol x maxrow pages will be placed on one sheet

        try {
            /* create a new PDFlib object */
            p = new PDFlib();
            
            p.set_option("searchpath={" + searchpath + "}");
            p.set_option("searchpath={" + fontpath + "}");

            /* This means we must check return values of load_font() etc. */
            p.set_option("errorpolicy=return");

            if (p.begin_document(outfile, "") == -1)
            throw new Exception("Error: " + p.get_errmsg());

            p.set_info("Creator", "PDFlib starter sample");
            p.set_info("Title", title);
            
            /* ---------------------------------------------------------------------
            * Define the sheet width and height,  the number of rows and columns
            * and calculate the scaling factor and cell dimensions for the 
            * multi-page imposition.
            * ---------------------------------------------------------------------
            */
            if (maxrow > maxcol)
            scale = 1.0 / maxrow;
            else
            scale = 1.0 / maxcol;

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

            Boolean pageopen = false; // is a page open that must be closed?
            
            /* Loop over all input documents */
            for (i = 0; i < pdffiles.Length; i++)
            {
            int indoc, maxpage, pageno, page;

            /* Open the input PDF */
            indoc = p.open_pdi_document(pdffiles[i], "");
            if (indoc == -1){
                Console.WriteLine("Error: " + p.get_errmsg());
                continue;
            }
        
            maxpage = (int) p.pcos_get_number(indoc, "length:pages");
            
            /* Loop over all pages of the input document */
            for (pageno = 1; pageno <= maxpage; pageno++)
            {
                page = p.open_pdi_page(indoc, pageno, "");

                if (page == -1) {
                        
                continue;
                }
                
                /* Start a new page */
                if (!pageopen) {
                p.begin_page_ext(sheetwidth, sheetheight, "");
                pageopen = true;
                }
                
                /* The save/restore pair is required to get an independent
                * clipping area for each mini page. Note that clipping
                * is not required for the imported pages, but affects
                * the rectangle around each page. Without clipping we
                * would have to move the rectangle a bit inside the
                * imported page to avoid drawing outside its area.
                */
                p.save();
                
                // Clipping path for the rectangle
                p.rect(col * colwidth, sheetheight - (row + 1) * rowheight,
                colwidth, rowheight);
                p.clip();

                optlist.Length = 0;
                optlist.Append("boxsize={");
                optlist.AppendFormat("{0} {1} ", colwidth, rowheight);
                optlist.Append("} fitmethod=meet");
                
                // Fit the imported page in the box
                p.fit_pdi_page(page, col * colwidth,
                sheetheight - (row + 1) * rowheight, optlist.ToString());

                p.close_pdi_page(page);
                
                /* Draw a frame around the mini page */ 
                p.set_graphics_option("linewidth=" + scale);
                p.rect(col * colwidth, sheetheight - (row + 1) * rowheight,
                colwidth, rowheight);
                p.stroke();
            
                p.restore();

                col++;
                
                // Start a new row if the current row is full
                if (col == maxcol) {
                col = 0;
                row++;
                }
                
                // Close the page if it is full
                if (row == maxrow) {
                row = 0;
                p.end_page_ext("");
                pageopen = false;
                }
            }
            p.close_pdi_document(indoc);
            }
            
            if (pageopen) {
            p.end_page_ext("");
            }
            
            p.end_document("");
        }

        catch (PDFlibException e)
        {
            // caught exception thrown by PDFlib
            Console.WriteLine("PDFlib exception occurred in starter_pdfimpose sample:");
            Console.WriteLine("[{0}] {1}: {2}\n", e.get_errnum(),
                    e.get_apiname(), e.get_errmsg());
        } finally {
            if (p != null) {
                p.Dispose();
            }
        }
    }
}
