org.kuali.kra.printing.service.impl.PersonSignatureServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kra.printing.service.impl.PersonSignatureServiceImpl.java

Source

/*
 * Copyright 2005-2013 The Kuali Foundation
 * 
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.kra.printing.service.impl;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.Overlay;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.util.ImageParameters;
import org.kuali.kra.bo.PersonSignature;
import org.kuali.kra.dao.PersonSignatureDao;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.kra.infrastructure.RoleConstants;
import org.kuali.kra.kim.bo.KcKimAttributes;
import org.kuali.kra.printing.PersonSignatureLocationHelper;
import org.kuali.kra.printing.PersonSignaturePrintHelper;
import org.kuali.kra.printing.service.PersonSignatureService;
import org.kuali.rice.coreservice.framework.parameter.ParameterConstants;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kim.api.role.RoleMembership;
import org.kuali.rice.kim.api.role.RoleService;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;

public class PersonSignatureServiceImpl implements PersonSignatureService {

    private static final String CORRESPONDENCE_SIGNATURE_TYPE = "CORRESPONDENCE_SIGNATURE_TYPE";
    private static final String CORRESPONDENCE_SIGNATURE_TAG = "CORRESPONDENCE_SIGNATURE_TAG";
    private BusinessObjectService businessObjectService;
    private ParameterService parameterService;
    private RoleService roleService;
    private PersonSignatureDao personSignatureDao;

    private static final float ADDITIONAL_SPACE_BETWEEN_TAG_AND_IMAGE = 10f;

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

    private String leadUnitNumber;
    private String administratorType;
    private String moduleNameSpace;

    /**
     * Configure signature type required
     * D - Always use Default signature
     * S - Always use signed in user signature, if not found use the default signature
     * N - No signature is required
     */
    public enum SignatureTypes {
        DEFAULT_SIGNATURE("D"), SIGNED_IN_USER_SIGNATURE("S"), NO_SIGNATURE_REQURIED("N");

        private String signatureType;

        private SignatureTypes(String signatureType) {
            this.signatureType = signatureType;
        }

        public String getSignatureType() {
            return signatureType;
        }

        public static SignatureTypes getSignatureMode(String signatureType) {
            for (SignatureTypes sType : values()) {
                if (sType.signatureType.equals(signatureType)) {
                    return sType;
                }
            }
            return null;
        }

    }

    /**
     * @see org.kuali.kra.printing.service.PersonSignatureService#applySignature(java.lang.String, java.lang.String, byte[])
     */
    public byte[] applySignature(String leadUnitNumber, String administratorType, String moduleNameSpace,
            byte[] pdfBytes) throws Exception {
        this.leadUnitNumber = leadUnitNumber;
        this.administratorType = administratorType;
        this.moduleNameSpace = moduleNameSpace;
        byte[] pdfFileData = pdfBytes;
        ByteArrayOutputStream byteArrayOutputStream = getOriginalPdfDocumentAsOutputsStream(pdfBytes); //getFlattenedPdfForm(pdfFileData);
        byteArrayOutputStream = identifyModeAndApplySignature(byteArrayOutputStream);
        if (ObjectUtils.isNotNull(byteArrayOutputStream)) {
            pdfFileData = byteArrayOutputStream.toByteArray();
        }
        return pdfFileData;
    }

    /**
     * This method is to remove interactive fields from the form.
     * @param pdfBytes
     * @return
     * @throws Exception
     */
    protected ByteArrayOutputStream getFlattenedPdfForm(byte[] pdfBytes) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        InputStream is = new ByteArrayInputStream(pdfBytes);
        PDDocument pdDoc = PDDocument.load(is);
        PDDocumentCatalog pdCatalog = pdDoc.getDocumentCatalog();
        PDAcroForm acroForm = pdCatalog.getAcroForm();
        COSDictionary acroFormDict = acroForm.getDictionary();
        COSArray fields = (COSArray) acroFormDict.getDictionaryObject("Fields");
        fields.clear();
        pdDoc.save(byteArrayOutputStream);
        return byteArrayOutputStream;
    }

    /**
     * This method is to identify signature mode and invoke appropriate method
     * If signature is not available, return the original.
     * to sign the document.
     * @param originalByteArrayOutputStream
     * @return
     * @throws Exception
     */
    protected ByteArrayOutputStream identifyModeAndApplySignature(
            ByteArrayOutputStream originalByteArrayOutputStream) throws Exception {
        ByteArrayOutputStream outputStream = originalByteArrayOutputStream;
        String signatureTypeParam = getSignatureTypeParameter();
        SignatureTypes signatureType = SignatureTypes.NO_SIGNATURE_REQURIED;
        if (ObjectUtils.isNotNull(signatureTypeParam)) {
            signatureType = SignatureTypes.getSignatureMode(signatureTypeParam);
        }

        if (signatureType != null) {
            switch (signatureType) {
            case DEFAULT_SIGNATURE:
                outputStream = printDefaultSignature(outputStream);
                break;
            case SIGNED_IN_USER_SIGNATURE:
                String personId = GlobalVariables.getUserSession().getPerson().getPrincipalId();
                outputStream = printLoggedInUserSignature(personId, outputStream);
                break;
            case NO_SIGNATURE_REQURIED:
                break;
            }
        } else {
            LOG.warn("Invalid signature type defined in parameter");
        }

        return outputStream;
    }

    /**
     * This method is to print default module administrator signature.
     * Original document is returned if signature is not available.
     * @param originalByteArrayOutputStream
     * @return
     * @throws Exception
     */
    protected ByteArrayOutputStream printDefaultSignature(ByteArrayOutputStream originalByteArrayOutputStream)
            throws Exception {
        ByteArrayOutputStream outputStream = originalByteArrayOutputStream;
        PersonSignature adminSignature = getAdminSignature();
        if (ObjectUtils.isNotNull(adminSignature)) {
            if (ObjectUtils.isNotNull(adminSignature.getAttachmentContent())) {
                outputStream = applyAutographInDocument(adminSignature, outputStream);
            }
        }
        return outputStream;
    }

    /**
     * This method is to print logged in user signature.
     * If logged in user signature is not available, get the administrator signature.
     * Original document is returned if signature is not available.
     * @param personId
     * @param originalByteArrayOutputStream
     * @return
     * @throws Exception
     */
    protected ByteArrayOutputStream printLoggedInUserSignature(String personId,
            ByteArrayOutputStream originalByteArrayOutputStream) throws Exception {
        PersonSignature userSignature = getLoggedinPersonSignature(personId);
        ByteArrayOutputStream outputStream = originalByteArrayOutputStream;
        if (ObjectUtils.isNull(userSignature)) {
            userSignature = getAdminSignature();
        }
        if (ObjectUtils.isNotNull(userSignature)) {
            if (ObjectUtils.isNotNull(userSignature.getAttachmentContent())) {
                outputStream = applyAutographInDocument(userSignature, outputStream);
            }
        }
        return outputStream;
    }

    /**
     * This method is to apply signature in the document.
     * @param personSignature
     * @param originalByteArrayOutputStream
     * @return
     */
    protected ByteArrayOutputStream applyAutographInDocument(PersonSignature personSignature,
            ByteArrayOutputStream originalByteArrayOutputStream) {
        ByteArrayOutputStream outputStream = originalByteArrayOutputStream;
        try {
            if (personSignature.getAttachmentContent() != null) {
                outputStream = scanAndApplyAutographInEachPage(personSignature.getAttachmentContent(),
                        outputStream);
            }
        } catch (Exception ex) {
            LOG.error("Exception while applying signature : Method applyAutographInDocument : " + ex.getMessage());
            ex.printStackTrace();
        }
        return outputStream;
    }

    /**
     * This method is to scan for signature tag in each page and apply the signature
     * at desired location.
     * @param imageData
     * @param originalByteArrayOutputStream
     */
    @SuppressWarnings("unchecked")
    protected ByteArrayOutputStream scanAndApplyAutographInEachPage(byte[] imageData,
            ByteArrayOutputStream originalByteArrayOutputStream) throws Exception {
        ByteArrayOutputStream outputStream = originalByteArrayOutputStream;
        byte[] pdfFileData = originalByteArrayOutputStream.toByteArray();
        PDDocument originalDocument = getPdfDocument(pdfFileData); //PDDocument.load(is);
        PDDocument signatureDocument = new PDDocument();
        List<PDPage> originalDocumentPages = originalDocument.getDocumentCatalog().getAllPages();
        for (PDPage page : originalDocumentPages) {
            List<String> signatureTags = new ArrayList<String>(getSignatureTagParameter());
            PersonSignatureLocationHelper printer = new PersonSignatureLocationHelper(signatureTags);
            PDStream contents = page.getContents();
            if (contents != null) {
                printer.processStream(page, page.findResources(), page.getContents().getStream());
            }
            PDPage signaturePage = new PDPage();
            if (printer.isSignatureTagExists()) {
                PDJpeg signatureImage = new PDJpeg(signatureDocument, getBufferedImage(imageData));
                PDPageContentStream stream = new PDPageContentStream(signatureDocument, signaturePage, true, true);
                for (PersonSignaturePrintHelper signatureHelper : printer.getPersonSignatureLocations()) {
                    float coordinateX = signatureHelper.getCoordinateX();
                    float coordinateY = signatureHelper.getCoordinateY() - signatureImage.getHeight()
                            - ADDITIONAL_SPACE_BETWEEN_TAG_AND_IMAGE;
                    stream.drawImage(signatureImage, coordinateX, coordinateY);
                    stream.close();
                }
            } else {
                signaturePage = page;
            }
            signatureDocument.addPage(signaturePage);
        }

        Overlay overlay = new Overlay();
        overlay.overlay(signatureDocument, originalDocument);

        originalDocument.save(outputStream);
        originalDocument.close();
        signatureDocument.close();
        return outputStream;
    }

    private PDDocument getPdfDocument(byte[] pdfFileData) throws Exception {
        InputStream is = new ByteArrayInputStream(pdfFileData);
        PDDocument originalDocument = PDDocument.load(is);
        return originalDocument;
    }

    private ByteArrayOutputStream getOriginalPdfDocumentAsOutputsStream(byte[] pdfFileData) throws Exception {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        InputStream is = new ByteArrayInputStream(pdfFileData);
        PDDocument originalDocument = PDDocument.load(is);
        originalDocument.save(outputStream);
        originalDocument.close();
        return outputStream;
    }

    /**
     * This method is to get buffered image based on predefined parameters
     * @param params
     * @param imageData
     * @return
     */
    protected BufferedImage getBufferedImage(ImageParameters params, byte[] imageData) {
        byte[] transparentColors = new byte[] { (byte) 0xFF, (byte) 0xFF };
        byte[] colors = new byte[] { 0, (byte) 0xFF };
        IndexColorModel colorModel = new IndexColorModel(1, 2, colors, colors, colors, transparentColors);
        BufferedImage image = new BufferedImage(params.getWidth(), params.getHeight(),
                BufferedImage.TYPE_BYTE_BINARY, colorModel);
        DataBufferByte buffer = new DataBufferByte(imageData, 1);
        WritableRaster raster = Raster.createPackedRaster(buffer, params.getWidth(), params.getHeight(),
                params.getBitsPerComponent(), new Point(0, 0));
        image.setData(raster);
        return image;
    }

    /**
     * This method is to get buffered image from image data
     * @param imageData
     * @return
     * @throws Exception
     */
    protected BufferedImage getBufferedImage(byte[] imageData) throws Exception {
        InputStream in = new ByteArrayInputStream(imageData);
        BufferedImage image = ImageIO.read(in);
        return image;
    }

    /**
     * This method is to get the default administrator signature
     * @return
     */
    protected PersonSignature getAdminSignature() {
        List<RoleMembership> moduleAdministrators = getAdministrators();
        Set<String> personIdsForSignature = new HashSet<String>();
        for (RoleMembership roleMembership : moduleAdministrators) {
            personIdsForSignature.add(roleMembership.getMemberId());
        }
        List<PersonSignature> personSignatures = getPersonSignatureDao()
                .getPersonSignatureForPersonIds(personIdsForSignature);
        PersonSignature adminSignature = null;
        for (PersonSignature personSignature : personSignatures) {
            if (personSignature.isDefaultAdminSignature()) {
                adminSignature = personSignature;
                break;
            }
        }
        return adminSignature;
    }

    /**
     * This method is to get logged in person signature
     * @param personId
     * @return
     */
    protected PersonSignature getLoggedinPersonSignature(String personId) {
        Map<String, Object> fieldValues = new HashMap<String, Object>();
        fieldValues.put(Constants.PERSON_SIGNATURE_PERSON_ID, personId);
        fieldValues.put(Constants.PERSON_SIGNATURE_ACTIVE, Boolean.TRUE);
        PersonSignature personSignature = (PersonSignature) getBusinessObjectService()
                .findByPrimaryKey(PersonSignature.class, fieldValues);
        return personSignature;
    }

    protected List<RoleMembership> getAdministrators() {
        List<String> roleIds = new ArrayList<String>();
        String roleId = getRoleService().getRoleIdByNamespaceCodeAndName(RoleConstants.DEPARTMENT_ROLE_TYPE,
                getAdministratorType());
        roleIds.add(roleId);
        Map<String, String> attrSet = new HashMap<String, String>();
        attrSet.put(KcKimAttributes.UNIT_NUMBER, getLeadUnitNumber());
        return getRoleService().getRoleMembers(roleIds, attrSet);
    }

    protected String getSignatureTypeParameter() {
        return getParameterService().getParameterValueAsString(getModuleNameSpace(),
                ParameterConstants.DOCUMENT_COMPONENT, CORRESPONDENCE_SIGNATURE_TYPE);
    }

    protected Collection<String> getSignatureTagParameter() {
        return getParameterService().getParameterValuesAsString(getModuleNameSpace(),
                ParameterConstants.DOCUMENT_COMPONENT, CORRESPONDENCE_SIGNATURE_TAG);
    }

    public ParameterService getParameterService() {
        return parameterService;
    }

    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }

    public RoleService getRoleService() {
        return roleService;
    }

    public void setRoleService(RoleService roleService) {
        this.roleService = roleService;
    }

    public PersonSignatureDao getPersonSignatureDao() {
        return personSignatureDao;
    }

    public void setPersonSignatureDao(PersonSignatureDao personSignatureDao) {
        this.personSignatureDao = personSignatureDao;
    }

    public BusinessObjectService getBusinessObjectService() {
        return businessObjectService;
    }

    public void setBusinessObjectService(BusinessObjectService businessObjectService) {
        this.businessObjectService = businessObjectService;
    }

    protected String getLeadUnitNumber() {
        return leadUnitNumber;
    }

    protected String getAdministratorType() {
        return administratorType;
    }

    public String getModuleNameSpace() {
        return moduleNameSpace;
    }

}