org.kuali.coeus.common.impl.print.PrintingServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.coeus.common.impl.print.PrintingServiceImpl.java

Source

/*
 * Kuali Coeus, a comprehensive research administration system for higher education.
 * 
 * Copyright 2005-2015 Kuali, Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.coeus.common.impl.print;

import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.kuali.coeus.common.framework.print.Printable;
import org.kuali.coeus.common.framework.print.PrintableAttachment;
import org.kuali.coeus.common.framework.print.PrintingException;
import org.kuali.coeus.common.framework.print.PrintingService;
import org.kuali.coeus.common.framework.print.watermark.WatermarkService;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.coeus.common.framework.print.AttachmentDataSource;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.datetime.DateTimeService;
import org.kuali.rice.krad.util.KRADConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.*;
import java.awt.Color;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;

/**
 * This class provides the functionality for printing any {@link Printable} object. It uses the methods available in Printable
 * object to generate XML, fetch XSL style-sheets, then transforms the XML to a PDF after applying the style sheet.
 * 
 */
@Component("printingService")
public class PrintingServiceImpl implements PrintingService {

    private static final Log LOG = LogFactory.getLog(PrintingServiceImpl.class);

    @Autowired
    @Qualifier("dateTimeService")
    private DateTimeService dateTimeService = null;

    @Autowired
    @Qualifier("watermarkService")
    private WatermarkService watermarkService;

    @Autowired
    @Qualifier("kualiConfigurationService")
    private ConfigurationService kualiConfigurationService;

    /**
     * This method receives a {@link Printable} object, generates XML for it, transforms into PDF after applying style-sheet and
     * returns the PDF bytes as {@link AttachmentDataSource}
     * 
     * @param printableArtifact to be printed
     * @return {@link AttachmentDataSource} PDF bytes
     * @throws PrintingException in case of any errors occur during printing process
     */
    protected Map<String, byte[]> getPrintBytes(Printable printableArtifact) throws PrintingException {
        try {
            Map<String, byte[]> streamMap = printableArtifact.renderXML();
            try {
                String loggingEnable = kualiConfigurationService
                        .getPropertyValueAsString(Constants.PRINT_LOGGING_ENABLE);
                if (loggingEnable != null && Boolean.parseBoolean(loggingEnable))
                    logPrintDetails(streamMap);
            } catch (Exception ex) {
                LOG.error(ex.getMessage());
            }

            Map<String, byte[]> pdfByteMap = new LinkedHashMap<String, byte[]>();

            FopFactory fopFactory = FopFactory.newInstance();

            int xslCount = 0;
            // Apply all the style sheets to the xml document and generate the
            // PDF bytes
            if (printableArtifact.getXSLTemplates() != null) {
                for (Source source : printableArtifact.getXSLTemplates()) {
                    xslCount++;
                    StreamSource xslt = (StreamSource) source;
                    if (xslt.getInputStream() == null || xslt.getInputStream().available() <= 0) {
                        LOG.error("Stylesheet is not available");
                    } else {
                        createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt, printableArtifact);
                    }
                }
            } else if (printableArtifact.getXSLTemplateWithBookmarks() != null) {
                Map<String, Source> templatesWithBookmarks = printableArtifact.getXSLTemplateWithBookmarks();
                for (Map.Entry<String, Source> templatesWithBookmark : templatesWithBookmarks.entrySet()) {
                    StreamSource xslt = (StreamSource) templatesWithBookmark.getValue();
                    createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt,
                            templatesWithBookmark.getKey(), printableArtifact);
                }

            }

            // Add all the attachments.
            if (printableArtifact.getAttachments() != null) {
                pdfByteMap.putAll(printableArtifact.getAttachments());
            }
            return pdfByteMap;
        } catch (FOPException e) {
            LOG.error(e.getMessage(), e);
            throw new PrintingException(e.getMessage(), e);
        } catch (TransformerConfigurationException e) {
            LOG.error(e.getMessage(), e);
            throw new PrintingException(e.getMessage(), e);
        } catch (TransformerException e) {
            LOG.error(e.getMessage(), e);
            throw new PrintingException(e.getMessage(), e);
        } catch (IOException e) {
            LOG.error(e.getMessage(), e);
            throw new PrintingException(e.getMessage(), e);
        }

    }

    protected void createPdfWithFOP(Map<String, byte[]> streamMap, Map<String, byte[]> pdfByteMap,
            FopFactory fopFactory, int xslCount, StreamSource xslt, Printable printableArtifact)
            throws FOPException, TransformerException {
        createPdfWithFOP(streamMap, pdfByteMap, fopFactory, xslCount, xslt, null, printableArtifact);
    }

    protected void createPdfWithFOP(Map<String, byte[]> streamMap, Map<String, byte[]> pdfByteMap,
            FopFactory fopFactory, int xslCount, StreamSource xslt, String bookmark, Printable printableArtifact)
            throws FOPException, TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer(xslt);
        String externalizableImagesUrl = getKualiConfigurationService()
                .getPropertyValueAsString(Constants.KRA_EXTERNALIZABLE_IMAGES_URI_KEY);
        transformer.setParameter("externalImagesUrl", externalizableImagesUrl);
        String applicationUrl = getKualiConfigurationService()
                .getPropertyValueAsString(KRADConstants.APPLICATION_URL_KEY);
        FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
        foUserAgent.setBaseURL(applicationUrl);
        for (Map.Entry<String, byte[]> xmlData : streamMap.entrySet()) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlData.getValue());
            Source src = new StreamSource(inputStream);
            //Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, outputStream);
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outputStream);
            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(src, res);
            byte[] pdfBytes = outputStream.toByteArray();
            if (pdfBytes != null && pdfBytes.length > 0) {
                String pdfMapKey = bookmark == null ? createBookMark(xslCount, xmlData.getKey()) : bookmark;
                pdfByteMap.put(pdfMapKey, pdfBytes);
            }
        }

    }

    protected String createBookMark(int xslCount, String bookmarkKey) {
        String pdfMapKey = bookmarkKey + (xslCount == 1 ? "" : " " + xslCount);
        return pdfMapKey;
    }

    /**
     * This method receives a {@link Printable} object, generates XML for it, transforms into PDF after applying style-sheet and
     * returns the PDF bytes as {@link AttachmentDataSource}
     * 
     * @param printableArtifacts to be printed
     * @return {@link AttachmentDataSource} PDF bytes
     * @throws PrintingException in case of any errors occur during printing process
     */
    public AttachmentDataSource print(Printable printableArtifacts) throws PrintingException {
        List<Printable> printables = new ArrayList<Printable>();
        printables.add(printableArtifacts);
        return print(printables);
    }

    /**
     * This method receives a {@link List} of {@link Printable} object, generates XML for it, transforms into PDF after applying
     * style-sheet and returns the PDF bytes as {@link AttachmentDataSource}
     * 
     * @param printableArtifactList List of printableArtifact to be printed
     * @return {@link AttachmentDataSource} PDF bytes
     * @throws PrintingException in case of any errors occur during printing process
     */
    public AttachmentDataSource print(List<Printable> printableArtifactList) throws PrintingException {
        return print(printableArtifactList, false);
    }

    public AttachmentDataSource print(List<Printable> printableArtifactList, boolean headerFooterRequired)
            throws PrintingException {
        PrintableAttachment printablePdf = null;
        List<String> bookmarksList = new ArrayList<String>();
        List<byte[]> pdfBaosList = new ArrayList<byte[]>();
        for (Printable printableArtifact : printableArtifactList) {
            Map<String, byte[]> printBytes = getPrintBytes(printableArtifact);
            for (String bookmark : printBytes.keySet()) {
                byte[] pdfBytes = printBytes.get(bookmark);
                if (isPdfGoodToMerge(pdfBytes)) {
                    bookmarksList.add(bookmark);
                    pdfBaosList.add(pdfBytes);
                }
            }
        }

        printablePdf = new PrintableAttachment();
        byte[] mergedPdfBytes = mergePdfBytes(pdfBaosList, bookmarksList, headerFooterRequired);
        // If there is a stylesheet issue, the pdf bytes will be null. To avoid an exception
        // initialize to an empty array before sending the content back
        if (mergedPdfBytes == null) {
            mergedPdfBytes = new byte[0];
        }

        printablePdf.setData(mergedPdfBytes);
        StringBuilder fileName = new StringBuilder();
        fileName.append(getReportName());
        fileName.append(Constants.PDF_FILE_EXTENSION);
        printablePdf.setName(fileName.toString());
        printablePdf.setType(Constants.PDF_REPORT_CONTENT_TYPE);
        return printablePdf;
    }

    protected boolean isPdfGoodToMerge(byte[] pdfBytes) {
        try {
            new PdfReader(pdfBytes);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    protected String getReportName() {
        String dateString = getDateTimeService().getCurrentDate().toString();
        return StringUtils.deleteWhitespace(dateString);
    }

    /**
     * @param pdfBytesList List containing the PDF data bytes
     * @param bookmarksList List of bookmarks corresponding to the PDF bytes.
     * @return
     * @throws PrintingException
     */

    protected byte[] mergePdfBytes(List<byte[]> pdfBytesList, List<String> bookmarksList,
            boolean headerFooterRequired) throws PrintingException {
        Document document = null;
        PdfWriter writer = null;
        ByteArrayOutputStream mergedPdfReport = new ByteArrayOutputStream();
        int totalNumOfPages = 0;
        PdfReader[] pdfReaderArr = new PdfReader[pdfBytesList.size()];
        int pdfReaderCount = 0;
        for (byte[] fileBytes : pdfBytesList) {
            LOG.debug("File Size " + fileBytes.length + " For " + bookmarksList.get(pdfReaderCount));
            PdfReader reader = null;
            try {
                reader = new PdfReader(fileBytes);
                pdfReaderArr[pdfReaderCount] = reader;
                pdfReaderCount = pdfReaderCount + 1;
                totalNumOfPages += reader.getNumberOfPages();
            } catch (IOException e) {
                LOG.error(e.getMessage(), e);
            }
        }
        HeaderFooter footer = null;
        if (headerFooterRequired) {
            Calendar calendar = dateTimeService.getCurrentCalendar();
            String dateString = formateCalendar(calendar);
            StringBuilder footerPhStr = new StringBuilder();
            footerPhStr.append(" of ");
            footerPhStr.append(totalNumOfPages);
            footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_76));
            footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_76));
            footerPhStr.append(getWhitespaceString(WHITESPACE_LENGTH_60));
            footerPhStr.append(dateString);
            Font font = FontFactory.getFont(FontFactory.TIMES, 8, Font.NORMAL, Color.BLACK);
            Phrase beforePhrase = new Phrase("Page ", font);
            Phrase afterPhrase = new Phrase(footerPhStr.toString(), font);
            footer = new HeaderFooter(beforePhrase, afterPhrase);
            footer.setAlignment(Element.ALIGN_BASELINE);
            footer.setBorderWidth(0f);
        }
        for (int count = 0; count < pdfReaderArr.length; count++) {
            PdfReader reader = pdfReaderArr[count];
            int nop;
            if (reader == null) {
                LOG.debug("Empty PDF byetes found for " + bookmarksList.get(count));
                continue;
            } else {
                nop = reader.getNumberOfPages();
            }

            if (count == 0) {
                document = nop > 0 ? new com.lowagie.text.Document(reader.getPageSizeWithRotation(1))
                        : new com.lowagie.text.Document();
                try {
                    writer = PdfWriter.getInstance(document, mergedPdfReport);
                } catch (DocumentException e) {
                    LOG.error(e.getMessage(), e);
                    throw new PrintingException(e.getMessage(), e);
                }
                if (footer != null) {
                    document.setFooter(footer);
                }
                // writer.setPageEvent(new Watermark()); // add watermark object here
                document.open();
            }

            PdfContentByte cb = writer.getDirectContent();
            int pageCount = 0;
            while (pageCount < nop) {
                document.setPageSize(reader.getPageSize(++pageCount));
                document.newPage();
                if (footer != null) {
                    document.setFooter(footer);
                }
                PdfImportedPage page = writer.getImportedPage(reader, pageCount);

                cb.addTemplate(page, 1, 0, 0, 1, 0, 0);

                PdfOutline root = cb.getRootOutline();
                if (pageCount == 1) {
                    String pageName = bookmarksList.get(count);
                    cb.addOutline(new PdfOutline(root, new PdfDestination(PdfDestination.FITH), pageName),
                            pageName);
                }
            }
        }
        if (document != null) {
            try {
                document.close();
                return mergedPdfReport.toByteArray();
            } catch (Exception e) {
                LOG.error("Exception occured because the generated PDF document has no pages", e);
            }
        }
        return null;
    }

    protected String formateCalendar(Calendar calendar) {
        DateFormat dateFormat = new SimpleDateFormat("M/d/yy h:mm a");
        return dateFormat.format(calendar.getTime());
    }

    /**
     * @return the dateTimeService
     */
    public DateTimeService getDateTimeService() {
        return dateTimeService;
    }

    /**
     * @param dateTimeService the dateTimeService to set
     */
    public void setDateTimeService(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    protected String getWhitespaceString(int length) {
        StringBuffer sb = new StringBuffer();
        char[] whiteSpace = new char[length];
        Arrays.fill(whiteSpace, Constants.SPACE_SEPARATOR);
        sb.append(whiteSpace);
        return sb.toString();
    }

    protected void logPrintDetails(Map<String, byte[]> xmlStreamMap) throws PrintingException {
        byte[] xmlBytes = null;
        String xmlString = null;
        String loggingDirectory = kualiConfigurationService
                .getPropertyValueAsString(Constants.PRINT_LOGGING_DIRECTORY);
        Iterator<String> it = xmlStreamMap.keySet().iterator();
        if (loggingDirectory != null) {
            BufferedWriter out = null;
            try {
                while (it.hasNext()) {
                    String key = (String) it.next();
                    xmlBytes = xmlStreamMap.get(key);
                    xmlString = new String(xmlBytes);
                    String dateString = getDateTimeService().getCurrentTimestamp().toString();
                    String reportName = StringUtils.deleteWhitespace(key);
                    String createdTime = StringUtils.replaceChars(StringUtils.deleteWhitespace(dateString), ":",
                            "_");
                    File dir = new File(loggingDirectory);
                    if (!dir.exists() || !dir.isDirectory()) {
                        dir.mkdirs();
                    }
                    File file = new File(dir, reportName + createdTime + ".xml");

                    out = new BufferedWriter(new FileWriter(file));
                    out.write(xmlString);
                }
            } catch (IOException e) {
                LOG.error(e.getMessage(), e);
            } finally {
                try {
                    if (out != null) {
                        out.flush();
                        out.close();
                    }
                } catch (IOException e) {
                    LOG.error(e.getMessage(), e);
                }
            }
        }
    }

    public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) {
        this.kualiConfigurationService = kualiConfigurationService;
    }

    public ConfigurationService getKualiConfigurationService() {
        return kualiConfigurationService;
    }

    /**
     * Gets the watermarkService attribute.
     * 
     * @return Returns the watermarkService.
     */
    public WatermarkService getWatermarkService() {
        return watermarkService;
    }

    /**
     * Sets the watermarkService attribute value.
     * 
     * @param watermarkService The watermarkService to set.
     */
    public void setWatermarkService(WatermarkService watermarkService) {
        this.watermarkService = watermarkService;
    }
}