org.jaffa.modules.printing.services.MultiFormPrintEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.jaffa.modules.printing.services.MultiFormPrintEngine.java

Source

/*
 * ====================================================================
 * JAFFA - Java Application Framework For All
 *
 * Copyright (C) 2002 JAFFA Development Group
 *
 *     This library is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU Lesser General Public
 *     License as published by the Free Software Foundation; either
 *     version 2.1 of the License, or (at your option) any later version.
 *
 *     This library is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *     Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public
 *     License along with this library; if not, write to the Free Software
 *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Redistribution and use of this software and associated documentation ("Software"),
 * with or without modification, are permitted provided that the following conditions are met:
 * 1.   Redistributions of source code must retain copyright statements and notices.
 *         Redistributions must also contain a copy of this document.
 * 2.   Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3.   The name "JAFFA" must not be used to endorse or promote products derived from
 *    this Software without prior written permission. For written permission,
 *    please contact mail to: jaffagroup@yahoo.com.
 * 4.   Products derived from this Software may not be called "JAFFA" nor may "JAFFA"
 *    appear in their names without prior written permission.
 * 5.   Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 */

package org.jaffa.modules.printing.services;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PRAcroForm;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.SimpleBookmark;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.jaffa.modules.printing.services.exceptions.EngineInstantiationException;
import org.jaffa.modules.printing.services.exceptions.EngineProcessingException;
import org.jaffa.modules.printing.services.exceptions.FormPrintException;
import org.jaffa.modules.printing.services.exceptions.PdfProcessingException;
import org.jaffa.session.ContextManagerFactory;
import org.jaffa.util.StringHelper;

/**
 * Generate Multiple forms at the same time and merge the results.
 * Each form can have a different template and data source, but they
 * all must use the same print engine.
 * @author PaulE
 */
public class MultiFormPrintEngine {

    private static final Logger log = Logger.getLogger(MultiFormPrintEngine.class);

    // Default form engine to use
    private String m_engineType = FormPrintFactory.ENGINE_TYPE_ITEXT;

    // Array of generated documents
    private List<byte[]> m_documents = new ArrayList<byte[]>();

    // Generated output document
    private byte[] m_output = null;

    // Array of print engines
    private List<IFormPrintEngine> m_engines = new ArrayList<IFormPrintEngine>();

    private boolean m_canPrint;

    private boolean m_canCopy;

    private boolean m_canModify;

    // has the pdf content been generated yet?
    private boolean m_processed = false;

    // has the pdf engine been initialized yet?
    private boolean m_initialized = false;

    // Store document properties
    private Properties m_documentProperties = null;

    // List of the pdf template documents
    private List<String> m_templateFilenames = new ArrayList<String>();

    // List of the object models, one per template
    private List m_objectModels = new ArrayList();

    // Search path to find the templates
    private String m_searchPath = null;

    private int m_totalPages = 0;

    // Override the page size for which to generate the PDF document.
    private String m_pageSize = null;

    /**
     * The core method in the engine is the generate() method, it must only be
     * called once, and must be called before you call any method that
     * accessed the generated PDF ( like writeForm() or getGeneratedForm() )
     * 
     * 
     * @throws FormPrintException This is thrown if there is any error in generating the PDF document.
     * The details will be in the message text
     */
    public void generate() throws FormPrintException {
        if (m_processed)
            throw new IllegalStateException("The form has already been processed");

        int pageOffset = 0;

        // Create a factory for each object, and initialize
        int index = -1;
        for (String templateName : m_templateFilenames) {
            index++;

            // Create a print engine
            IFormPrintEngine engine = FormPrintFactory.newInstance(m_engineType);
            if (engine == null) {
                String err = "No Engine Created. Type=" + m_engineType;
                if (log.isDebugEnabled())
                    log.error(err);
                throw new EngineInstantiationException(err);
            }
            m_engines.add(engine);

            // Set all values on print engine
            engine.setTemplateName(templateName);
            engine.setDataSource(m_objectModels.get(index));
            engine.setPermissions(m_canPrint, m_canCopy, m_canModify);
            if (m_documentProperties != null)
                engine.setDocumentProperties(m_documentProperties);
            if (m_searchPath != null)
                engine.setTemplateSearchPath(m_searchPath);

            engine.initialize();

            m_totalPages += engine.getTotalPages();
        }

        // Generate output from each engine
        //for(Iterator<IFormPrintEngine> it = m_engines.iterator();it.hasNext();) {
        //    IFormPrintEngine engine = it.next();
        for (IFormPrintEngine engine : m_engines) {

            // Set all values on print engine
            engine.setTotalPagesOverride(m_totalPages);
            engine.setCurrentPageOffset(pageOffset);
            engine.generate();
            m_documents.add(engine.getGeneratedForm());
            pageOffset += engine.getTotalPages();
        }

        // Now merge the outputs
        try {
            if (FormPrintFactory.ENGINE_TYPE_ITEXT.equals(m_engineType)
                    || FormPrintFactory.ENGINE_TYPE_FOP.equals(m_engineType)
                    || FormPrintFactory.ENGINE_TYPE_PDFLIB.equals(m_engineType)) {
                m_output = mergePdf(m_documents);
                if (getPageSize() != null) {
                    m_output = PdfHelper.scalePdfPages(m_output, getPageSize(), false, true);
                }
            } else {
                m_output = mergeText(m_documents);
            }
        } catch (FormPrintException fpe) {
            throw fpe;
        } catch (Exception e) {
            log.error("Failed to merge PDF documents", e);
            throw new EngineProcessingException("Merge Failed", e);
        }
        m_processed = true;
    }

    /**
     * Set the permissions on the generated PDF file. <B>NOTE: These must be
     * set prior to generating the PDF</B>
     * @param canPrint Allow generated PDF to be printed?
     * @param canCopy Allow generated PDF contents to be copied?
     * @param canModify Allow generated PDF contents to be modified?
     * @throws FormPrintException Throw if there was an error setting these properties.
     */
    public void setPermissions(boolean canPrint, boolean canCopy, boolean canModify) throws FormPrintException {
        m_canPrint = canPrint;
        m_canCopy = canCopy;
        m_canModify = canModify;
    }

    /** Return the generated document. The generate() method MUST be called
     * prior to this else an exception will be thrown
     * @throws FormPrintException thrown if any pdf access error occurs
     * @return the generated pdf as a byte array
     */
    public byte[] getGeneratedForm() throws FormPrintException {
        if (!m_processed)
            throw new IllegalStateException("The form not been processed yet");
        return m_output;
    }

    /** Write the Form to a temp file, returns the file handle, or null if it failed!
     * The file will typically end up in the java temp folder
     * ( <CODE>System.getProperty("java.io.tempdir")</CODE> )
     * @throws FormPrintException thrown if any pdf access error occurs
     * @return return the file handle to the file that was written containing
     * the generated PDF document. This will be null if there were any
     * write errors.
     */
    public File writeForm() throws FormPrintException {
        return writeForm(null);
    }

    /** Write the Form to a specified file
     * @param fileout file handle to use to write out the pdf
     * @throws FormPrintException thrown if any pdf access error occurs
     * @return return the file handle to the file that was written containing
     * the generated PDF document. This will be null if there were any
     * write errors.
     */
    public File writeForm(File fileout) throws FormPrintException {
        if (!m_processed)
            throw new IllegalStateException("The form not been processed yet");
        try {
            if (fileout == null) {
                String extn = ".txt";
                if (FormPrintFactory.ENGINE_TYPE_ITEXT.equals(m_engineType)
                        || FormPrintFactory.ENGINE_TYPE_FOP.equals(m_engineType)
                        || FormPrintFactory.ENGINE_TYPE_PDFLIB.equals(m_engineType))
                    extn = ".pdf";
                fileout = File.createTempFile("form_", extn);
            }
            OutputStream bos = new FileOutputStream(fileout);
            bos.write(m_output);
            bos.close();
        } catch (IOException e) {
            log.error("Error Writing out PDF", e);
            return null;
        }
        return fileout;
    }

    /**
     * Get template name being used to generate the final PDF
     * @return Template Name
     */
    public List<String> getTemplateNames() {
        return m_templateFilenames;
    }

    /**
     * Get object Models being used to generate the final PDF
     * @return Template Name
     */
    public List getObjectModels() {
        return m_objectModels;
    }

    /**
     * Get search path being used to locate the template
     * @return Search Path
     */
    public String getTemplateSearchPath() {
        return m_searchPath;
    }

    /**
     * Set template names
     * 
     * @param objectModel 
     * @param templateName Template Name
     */
    public void addDocument(String templateName, Object objectModel) {
        if (m_templateFilenames == null)
            m_templateFilenames = new ArrayList<String>();
        m_templateFilenames.add(templateName);
        if (m_objectModels == null)
            m_objectModels = new ArrayList();
        m_objectModels.add(objectModel);
    }

    /**
     * Set Search Path
     * @param templateSearchPath Search Path
     */
    public void setTemplateSearchPath(String templateSearchPath) {
        m_searchPath = templateSearchPath;
    }

    /**
     * Set Form Print Engine Type. See contants on the FormPrintFactory
     * @param engineType Type of engine to create for this printing
     */
    public void setEngineType(String engineType) {
        m_engineType = engineType;
    }

    /**
     * Get the type of engine being used to generate the output
     * @return engine being used, see FormPrintFactory for constant values
     */
    public String getEngineType() {
        return m_engineType;
    }

    /**
     * Getter for property totalPages.
     * @return Value of property totalPages.
     */
    public int getTotalPages() {
        return m_totalPages;
    }

    /**
     * Set the properties for the document
     * @param documentProperties 
     */
    public void setDocumentProperties(Properties documentProperties) {
        m_documentProperties = documentProperties;
    }

    /**
     * An override page size for which to generate the PDF document.
     */
    public String getPageSize() {
        return m_pageSize;
    }

    /**
     * pageSize is an override page size for which to generate the PDF
     * document.  It is used to resize the PDF document pages.  If this
     * value is not set, then the normal PDF document size is used.
     */
    public void setPageSize(String pageSize) {
        this.m_pageSize = pageSize;
    }

    /**
     * Merge a list of generated Pdf Documents together
     * @param documents 
     * @throws java.io.IOException 
     * @throws com.lowagie.text.DocumentException 
     * @return byte[]
     */
    public static byte[] mergePdf(List<byte[]> documents) throws IOException, DocumentException {
        int pageOffset = 0;
        ArrayList master = new ArrayList();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        Document document = null;
        PdfCopy writer = null;
        boolean first = true;
        for (Iterator<byte[]> it = documents.iterator(); it.hasNext();) {
            // we create a reader for a certain document
            PdfReader reader = new PdfReader(it.next());
            reader.consolidateNamedDestinations();
            // we retrieve the total number of pages
            int n = reader.getNumberOfPages();
            List bookmarks = SimpleBookmark.getBookmark(reader);
            if (bookmarks != null) {
                if (pageOffset != 0)
                    SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null);
                master.addAll(bookmarks);
            }
            pageOffset += n;

            if (first) {
                first = false;

                // step 1: creation of a document-object
                document = new Document(reader.getPageSizeWithRotation(1));
                // step 2: we create a writer that listens to the document
                writer = new PdfCopy(document, output);
                // step 3: we open the document
                document.open();
            }
            // step 4: we add content
            PdfImportedPage page;
            for (int i = 0; i < n;) {
                ++i;
                page = writer.getImportedPage(reader, i);
                writer.addPage(page);
            }
            PRAcroForm form = reader.getAcroForm();
            if (form != null)
                writer.copyAcroForm(reader);
        }
        if (master.size() > 0)
            writer.setOutlines(master);
        // step 5: we close the document
        if (document != null)
            document.close();
        return output.toByteArray();
    }

    /**
     * Merge a list of generated Text Documents together
     * @param documents 
     * @return byte[]
     */
    public static byte[] mergeText(List<byte[]> documents) {
        int size = 0;
        for (Iterator<byte[]> it = documents.iterator(); it.hasNext();) {
            byte[] d = it.next();
            size += d.length;
        }
        byte[] out = new byte[size];
        int pos = 0;
        for (Iterator<byte[]> it = documents.iterator(); it.hasNext();) {
            byte[] d = it.next();
            for (int i = 0; i < d.length; i++)
                out[pos++] = d[i];
        }
        return out;
    }

    /**
    * This allows a test page to be printed where the form is genrated
    * using the field names as the data for each field. Each engine may also
    * do extra stuff in test mode for example iText will outline the field boxes.
    * <p>
    * Factory Types are <CODE>iText</CODE>, <CODE>PDFlib</CODE>, <CODE>Velocity</CODE> and <CODE>BSF</CODE>
    * @param args <Template Name> [<Output Name>] [<Factory Type>]
    */
    public static void main(String[] args) {
        org.jaffa.util.LoggerHelper.init();
        if (args.length < 2 || args.length > 3) {
            System.out.println(
                    MultiFormPrintEngine.class.getName() + " <Template Name> [<Output Name>] [<Factory Type>]");
            System.exit(1);
        }
        try {
            ContextManagerFactory.instance().setThreadContext(null);
            MultiFormPrintEngine engine = new MultiFormPrintEngine();
            engine.setEngineType(args.length > 2 ? args[2] : FormPrintFactory.ENGINE_TYPE_ITEXT);
            Properties documentProperties = new Properties();
            documentProperties.setProperty(IFormPrintEngine.DOCUMENT_PROPERTY_TEMPLATE_MODE, "true");
            engine.setDocumentProperties(documentProperties);
            engine.addDocument(args[0], null);
            engine.generate();
            File out = null;
            if (args.length > 1)
                out = new File(args[1]);
            File out3 = engine.writeForm(out);
            if (out3 == null || !out3.exists()) {
                System.out.println("writePdf(File) did not return a file");
                System.exit(2);
            }
            System.out.println("Generated File: " + out3.getAbsolutePath());
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(3);
        } finally {
            ContextManagerFactory.instance().unsetThreadContext();
        }
    }
}