richfaces 4 mediaOutput throwing PropertyNotFoundException
pramanish Jun 5, 2014 9:27 AMHi,
I am using richfaces 4.3.6 Final along with JSF 2 and wildfly-8.1.0.CR1. In my application I am uploading image and showing that uploaded image into <rich:mediaOutput> but I am getting below exception while rendering <rich:mediaOutput> .Below is my xhtml and java code, please take a look at this and let me know If I am doing anything wrong.
Any workaround or solution to fix it.
Thanks in advance.
upload.xhtml
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:s="http://jboss.org/schema/seam/taglib"
xmlns:cu="http://ext.test.com/cu/functions"
xmlns:el="http://ext.test.com/el/functions">
<h:form id="docupload" style="margin-left:5px; width: 100%;">
<h:messages layout="horizontal" globalOnly="true" showDetail="false"
showSummary="true" />
<c:choose>
<c:when test="#{featureConfig.OFLOWS_MOBILIZED_ENABLED().booleanValue() eq 'true' and themeResolver.canvasEnabled}">
<br/>
<span id="submitButton"> <s:link styleClass="button" view="/oconsole/appuploadhelper.seam"
><span>Upload
Documents</span>
<s:conversationPropagation type="join"></s:conversationPropagation>
</s:link>
</span>
<br/>
<br/>
</c:when>
<c:otherwise>
<rich:fileUpload fileUploadListener="#{fileUploadBean.listener}"
maxFilesQuantity="#{fileUploadBean.uploadsAvailable}" id="upload"
immediateUpload="false"
acceptedTypes="jpg,gif,png,pdf,bmp,tif,tiff,jpeg"
allowFlash="#{featureConfig.ENABLE_FLASH_UPLOADS()}"
style="width:100%;"
disabled="#{appRequest.checkApplicationsStatus()}">
<a4j:ajax event="uploadcomplete" render="info" />
</rich:fileUpload>
</c:otherwise>
</c:choose>
<p>Once you've uploaded your documents, classify each one using
the categories below.</p>
<rich:panel bodyClass="info" id="info">
<f:facet name="header">
<h:outputText value="Classify uploaded documents" />
</f:facet>
<h:outputText value="No files currently uploaded"
rendered="#{fileUploadBean.size==0}" />
<s:div id="docuploadTable" style="border:none;">
<c:forEach items="#{fileUploadBean.files}" var="file"
varStatus="row">
<s:div id="docRow#{row.index}" styleClass="documentRow">
<s:div id="docImageCol#{row.index}"
styleClass="docUploadImageColumn">
<a4j:mediaOutput id="docImage#{row.index}" element="img"
mimeType="#{file.mime}" createContent="#{fileUploadBean.paint}"
value="#{row.index}" style="max-width:200px;" cacheable="false">
<f:param value="#{fileUploadBean.timeStamp}" name="time" />
<s:conversationId value="#{conversation.id}" />
</a4j:mediaOutput>
</s:div>
<s:div id="docCategoryCol#{row.index}"
styleClass="docUploadCategoryColumn">
<h:selectOneMenu id="docCategory#{row.index}" name="docCategory" value="">
<el:setValueBinding var="selectionmap"
valueBinding="#{fileUploadBean.documentCategory}" />
<s:selectItems var="selection"
value="#{selectionmap.entrySet()}" label="#{selection.value}"
itemValue="#{selection.key}" hideNoSelectionLabel="true" />
</h:selectOneMenu>
</s:div>
</s:div>
<br class="clearfloat" />
<br class="clearfloat" />
</c:forEach>
</s:div>
</rich:panel>
<br />
<span id="uploadSaveBtn"> <a4j:commandLink id="saveLink"
onclick="replaceWithSpinner(this);"
action="#{fileUploadBean.saveSupportingDocuments()}"
styleClass="button" render="docupload">
<s:conversationPropagation type="join"></s:conversationPropagation>
<span>Save</span>
</a4j:commandLink>
</span>
</h:form>
</ui:composition>
DocUploadBean.java
package com.test.seam.action.oweb;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.el.ELContext;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlSelectOneMenu;
import javax.faces.context.FacesContext;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Begin;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.End;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Synchronized;
import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Conversation;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
import org.jboss.seam.ui.component.html.HtmlDiv;
import org.richfaces.event.FileUploadEvent;
import org.richfaces.model.UploadedFile;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifSubIFDDirectory;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfCopyFields;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.RandomAccessFileOrArray;
import com.lowagie.text.pdf.codec.TiffImage;
import com.test.seam.entity.ApplicationRequest;
import com.test.seam.entity.Customer;
import com.test.seam.entity.Document;
import com.test.seam.entity.DocumentImage;
import com.test.seam.entity.InputField;
import com.test.seam.entity.RawSupportingDocument;
import com.test.seam.entity.SupportingDocument;
import com.test.seam.entity.UserType;
import com.test.seam.infrastructure.ELEvaluator;
import com.test.seam.infrastructure.UserContext;
import com.test.seam.session.oconsole.OConsoleDocUploadManagerBean;
import com.test.seam.session.oconsole.ProductsInfo;
import com.test.seam.session.oweb.DocUploadManager;
import com.test.seam.session.oweb.DocUploadManagerBean;
import com.test.seam.util.PDFToImageConvertor;
import com.test.seam.web.util.PageScopeUtil;
@Name(DocUploadBean.COMPONENT_NAME)
@Scope(ScopeType.CONVERSATION)
// TODO: change this to page scope and fix the interaction
@Synchronized(timeout = 100000)
@AutoCreate
public class DocUploadBean implements Serializable {
private static final int MAX_FILE_SIZE_IN_DATABASE = 5 * 1014 * 1024;
public static final String COMPONENT_NAME = "fileUploadBean";
/**
*
*/
private static final long serialVersionUID = 5339410002696528789L;
// http://itextdocs.lowagie.com/tutorial/general/index.php - See Step 1, and
// description of margin width
private static final float ONE_INCH = 72.0f;
private String customerLevelUpload;
public String getCustomerLevelUpload() {
return customerLevelUpload;
}
public void setCustomerLevelUpload(String customerLevelUpload) {
this.customerLevelUpload = customerLevelUpload;
}
private static final float MAX_CONTENT_WIDTH = PageSize.LETTER.getWidth() - ONE_INCH; // 1 inch margin
private static final float MAX_CONTENT_HEIGHT = PageSize.LETTER.getHeight() - ONE_INCH; // 1 inch margin
private boolean decisionTableEnabled;
// @In (create=true) DocUploadManager docUploadManager;
private static Log log = Logging.getLog(DocUploadBean.class);
@In
UserContext userContext;
@In(required=false, create=false)
ApplicationRequest appRequest;
@Create
@Begin(join=true)
public void create(){
log.debug("Creating DocUploadBean in conversation: " +Conversation.instance().getId() );
}
@Destroy
public void remove(){
log.debug("Destroying DocUploadBean in conversation: " +Conversation.instance().getId() );
}
public static String className = DocUploadManagerBean.class.getName();
// files holds atomic support docs. each page in a multi page pdf is separated
// into individual pdf doc that can be used as different supporting documents.
// e.g. a multipage pdf has 1 page of passport img, 1 page of insurance id
// This will get separated into 2 pdf docs and stored in files
private ArrayList<DocumentAndCategory> files = new ArrayList<DocumentAndCategory>();
// separate compound supporting documents into individual page of separate pdf docs
// private List<DocumentAndCategory> atomicSupportingDocs;
private int uploadsAvailable = 10;
private boolean autoUpload = false;
private boolean useFlash = false;
//private SupportingDocumentCategory defaultCategory = SupportingDocumentCategory.BANK_STATEMENT;
//private List<SupportingDocumentCategory> documentCategory;
public int getSize() {
if (getFiles().size() > 0) {
return getFiles().size();
} else {
return 0;
}
}
public DocUploadBean() {
}
public void paint(OutputStream stream, Object object) throws IOException {
log.debug("DocUploadBean.paint()");
log.debug("size: " + files.size());
log.debug("index: " + object);
stream.write(getFiles().get((Integer) object).getData());
}
public void listener(FileUploadEvent event) throws Exception {
log.debug(className +".listener() is called to convert and store uploaded files.");
PageScopeUtil pageScopeUtil = PageScopeUtil.instance();
pageScopeUtil.setErrMsg(null);//clear if previous request had errors, note cannot use event scope for the uploader as the upload is its own request
UploadedFile item = event.getUploadedFile();
//By default this flag variable will be false. When user is on validate decision table
//tab in console page that time, this flag variable will be true means it will invoke code
//related to validating decision table which is in else section of this condition.
if(!decisionTableEnabled){
RawSupportingDocument rawDocument = createRawDocument(item);
saveRawDocument(rawDocument);
if (!isEmpty(rawDocument.getDocumentImage().getContent())) {
log.info("Processing uploaded file '#0'", rawDocument.getDocumentImage().getFileName());
try{
if (StringUtils.endsWithIgnoreCase(item.getName(), ".pdf")) {
log.debug("process uploaded pdf file");
preparePdf(rawDocument.getDocumentImage().getFileName(), rawDocument.getDocumentImage().getContent());
} else if (StringUtils.endsWithIgnoreCase(item.getName(), ".tif")
|| StringUtils.endsWithIgnoreCase(item.getName(), ".tiff")) {
log.debug("process uploaded tiff file");
prepareTiff(item.getName(), rawDocument.getDocumentImage().getContent());
} else {
log.debug("process uploaded file (jpg,gif,png,pdf,bmp)");
prepareImage(item.getName(), rawDocument.getDocumentImage().getContent());
}
} catch(PDFSecurityException pse){
String errMsg = "Your PDF document has security settings that prevented us from processing it.";
pageScopeUtil.setErrMsg(errMsg);
log.error(errMsg, pse);
return;
}catch(Throwable t){
String errMsg = "There is some problem with the uploaded file, we can't process it.";
pageScopeUtil.setErrMsg(errMsg);
log.error(t, t.getMessage());
return;
}
}
uploadsAvailable--;
} else {
validateDecisionTable(item);
}
}
private void prepareImage(String fileName, byte[] data) throws Exception {
files.add(extractPageFromImage(data, fileName, null));
}
private DocumentAndCategory extractPageFromImage(final byte[] data, final String fileName, String documentCategory) throws Exception {
// pdf wrapper around the image
DocumentAndCategory pdfDoc = new DocumentAndCategory();
pdfDoc.setName(fileName);
pdfDoc.setDocumentCategory(documentCategory);
byte[] pdfBytes = convertOtherToPDFDoc(data);
pdfDoc.setPdfByte(pdfBytes);
// the image
pdfDoc.setData(data);
pdfDoc.setLength(data.length);
return pdfDoc;
}
private DocumentAndCategory extractPageFromImage(byte[] data, String fileName) throws Exception {
return extractPageFromImage(data, fileName, null);
}
private void prepareTiff(String fileName, byte[] data) throws Exception {
final List<DocumentAndCategory> docs = extractPagesFromTiff(data, fileName, null);
for (DocumentAndCategory doc: docs) {
files.add(doc);
}
}
private List<DocumentAndCategory> extractPagesFromTiff(final byte[] data,
String fileName, String documentCategory) throws Exception {
// convert each page in the tiff into a separate pdf
// note each tiff image can have multiple pages
List<byte[]> pdfByteList = convertTiffToPDFDocs(data);
final List<DocumentAndCategory> docs = new ArrayList<DocUploadBean.DocumentAndCategory>();
// store each pdf in this.files list, also include the thumb nails
for (int c = 0; c < pdfByteList.size(); c++) {
DocumentAndCategory pdfDoc = new DocumentAndCategory();
// store the pdf for each page
pdfDoc.setName(fileName);
pdfDoc.setDocumentCategory(documentCategory);
byte[] pdfBytes = pdfByteList.get(c);
pdfDoc.setPdfByte(pdfBytes);
// store the thumb nail image
List<byte[]> imageBytesList = convertPDFToImagePages(pdfBytes, true);
byte[] imageBytes = imageBytesList.get(0);
pdfDoc.setData(imageBytes);
pdfDoc.setLength(imageBytes.length);
docs.add(pdfDoc);
}
return docs;
}
private Collection<DocumentAndCategory> extractPagesFromTiff(byte[] data, String fileName) throws Exception {
return extractPagesFromTiff(data, fileName, null);
}
private void preparePdf(String fileName, byte[] data) throws Exception {
final List<DocumentAndCategory> docs = extractPagesFromPdf(data, fileName, null);
for (DocumentAndCategory doc: docs) {
files.add(doc);
}
}
private List<DocumentAndCategory> extractPagesFromPdf(final byte[] data, final String fileName, String documentCategory) throws Exception {
// separate the pdf into separate pages, one page per doc
List<byte[]> pdfByteList = separatePDFPages(data);
// get the individual page image
List<byte[]> imageBytesList = convertPDFToImagePages(data, true);
final List<DocumentAndCategory> docs = new ArrayList<DocUploadBean.DocumentAndCategory>();
// save each jpg image in the files object. files object is later used to categorize each doc.
for (int c = 0; c < imageBytesList.size(); c++) {
DocumentAndCategory pdfDoc = new DocumentAndCategory();
// store the pdf for each page
pdfDoc.setName(fileName);
pdfDoc.setDocumentCategory(documentCategory);
byte[] pdfBytes = pdfByteList.get(c);
pdfDoc.setPdfByte(pdfBytes);
// store the thumb nail image
byte[] imageBytes = imageBytesList.get(c);
pdfDoc.setData(imageBytes);
pdfDoc.setLength(imageBytes.length);
docs.add(pdfDoc);
}
return docs;
}
private List<DocumentAndCategory> extractPagesFromPdf(byte[] data, String fileName) throws Exception {
return extractPagesFromPdf(data, fileName, null);
}
private void saveRawDocument(RawSupportingDocument rsd) {
log.info("Saving File Name: #0, Content Type: #1", rsd.getDocumentImage().getFileName(), rsd.getDocumentImage().getMimeType());
String componentName = "";
if (userContext.getNamespace().getUserType() == UserType.OFFICER)
componentName = OConsoleDocUploadManagerBean.COMPONENT_NAME;
else
componentName = DocUploadManagerBean.COMPONENT_NAME;
DocUploadManager docUploadManager = (DocUploadManager) Component
.getInstance(componentName);
docUploadManager.saveRaw(rsd);
}
private RawSupportingDocument createRawDocument(UploadedFile item) throws IOException {
DocumentImage d = new DocumentImage();
// TODO: item.isTempFile() -- alternate in RF4?
// if (item.isTempFile()) {
// if (item.getFile().length() < MAX_FILE_SIZE_IN_DATABASE) {
// log.info("Loading file '#0' (#1K) into DB", item.getName(), item.getFile().length() / 1024);
// FileInputStream fileInput = null;
// try {
// fileInput = new FileInputStream(item.getFile());
// d.setContent(IOUtils.toByteArray(fileInput));
// } finally {
// IOUtils.closeQuietly(fileInput);
// }
// try {
// item.getFile().delete();
// } catch (Exception e) {
// log.warn("Can't remove uploaded file '#0'", e, item.getFile().getAbsolutePath());
// }
// } else {
// log.info("Uploaded file '#0' is to big (#1K), leaving it in the file system", item.getName(), item.getFile().length() / 1024);
// d.setFilePath(item.getFile().getAbsolutePath());
// }
// } else {
log.info("Loading file '#0' (#1K) into DB", item.getName(), item.getData().length / 1024);
d.setContent(item.getData());
// }
d.setFileName(item.getName());
d.setDescription(item.getName());
d.setMimeType(item.getContentType());
d.setLabel(item.getName());
RawSupportingDocument rsd = new RawSupportingDocument();
rsd.setDocumentImage(d);
return rsd;
}
/**
* Use by console to save uploaded supporting docs
*/
public void saveSupportingDocuments() {
try {
saveSupportingDocuments(OConsoleDocUploadManagerBean.COMPONENT_NAME);
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Supporting Documents are uploaded", null));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* responsible for saving uploaded document from docupload.xhtml in app
* It will append the new supporting doc to the existing set in the database
*
* @param componentName
*/
public void saveSupportingDocuments(String componentName) {
saveSupportingDocuments(componentName, true);
clearUploadData();
}
/**
* This method is responsible for saving the changes made on already
* uploaded supporting documents on Re-categorization of supporting
* documents page.
*
* It will not append. Instead it will erase what is in db and than save
*
*/
public void saveRecategorizedSupportingDocuments() {
saveSupportingDocuments(DocUploadManagerBean.COMPONENT_NAME, false);
}
/**
* helper function to save supporting documents.
*
* @param componentName
* @param isAppend
*/
@End
public void saveSupportingDocuments(String componentName, boolean isAppend) {
try {
UIComponent dependenciesComp = FacesContext.getCurrentInstance().getViewRoot().findComponent("docupload:docuploadTable");
HtmlDiv html = (HtmlDiv) dependenciesComp;
// setup categorizedDocMap which maps category to each file in this.files (contains jpg image)
Map<String, List<DocumentAndCategory>> categorizedDocMap = new HashMap<String, List<DocumentAndCategory>>();
for (int i = 0; i < html.getChildren().size(); i++) {
DocumentAndCategory file = files.get(i);
// process those not mark for delete
if (file.getMarkForDelete()==false) {
HtmlDiv innerDiv = (HtmlDiv) html.getChildren().get(i);
HtmlSelectOneMenu selectMenu = (HtmlSelectOneMenu) innerDiv.getChildren().get(1).getChildren().get(0);
if(selectMenu != null && selectMenu.getValue() != null){
String category = selectMenu.getValue().toString();
if (categorizedDocMap.get(category) != null) {
List<DocumentAndCategory> documentList = categorizedDocMap.get(category);
documentList.add(file);
} else {
List<DocumentAndCategory> documentList = new ArrayList<DocumentAndCategory>();
documentList.add(file);
categorizedDocMap.put(category, documentList);
}
}else{
log.warn("Uploaded supporting document was not categorized.");
}
}
}
// convert categorizedDocMap to pdf documents.
List<SupportingDocument> supportingDocList = mergePdfDocByCategory(categorizedDocMap);
// save the pdf docs
DocUploadManager docUploadManager = (DocUploadManager) Component.getInstance(componentName);
docUploadManager.save(supportingDocList, isAppend);
// clean up; scan backward and remove those in the files that have been deleted
for (int count = files.size(); count>0; count--) {
if (files.get(count-1).getMarkForDelete()==true) {
files.remove(count-1);
}
}
} catch (Exception e) {
log.error(e);
}
}
@Factory(value = "documentCategory")
public Map<String, String> getDocumentCategory() {
String componentName = "";
if (userContext.getNamespace().getUserType() == UserType.OFFICER) componentName = OConsoleDocUploadManagerBean.COMPONENT_NAME;
else componentName = DocUploadManagerBean.COMPONENT_NAME;
DocUploadManager docUploadManager = (DocUploadManager)Component.getInstance(componentName);
return docUploadManager.getSupportingDocumentCtgry();
}
/*@Factory(value = "documentCategory")
public List<SupportingDocumentCategory> getDocumentCategory() {
SupportingDocumentCategory[] sdcArray = SupportingDocumentCategory
.values();
List<SupportingDocumentCategory> sdcList = new ArrayList<SupportingDocumentCategory>();
for (int i = 0; i < sdcArray.length; i++) {
sdcList.add(sdcArray[i]);
}
return sdcList;
}
public SupportingDocumentCategory getDefaultCategory() {
return defaultCategory;
}
public void setDefaultCategory(SupportingDocumentCategory defaultCategory) {
this.defaultCategory = defaultCategory;
}
public void setDocumentCategory(
List<SupportingDocumentCategory> documentCategory) {
this.documentCategory = documentCategory;
}*/
public String clearUploadData() {
files.clear();
setUploadsAvailable(10);
return null;
}
/**
* convert a multipage tiff into multiple pdf docs
*
* @param imageBytes
* @return
*/
public List<byte[]> convertTiffToPDFDocs(byte[] imageBytes) {
// each item in the list is a byte array of a pdf document. the pdf document holds the one image in the multi-page tiff
List<byte[]> pdfByteList = new ArrayList<byte[]>();
RandomAccessFileOrArray ra = new RandomAccessFileOrArray(imageBytes);
int pages = TiffImage.getNumberOfPages(ra);
for(int i = 1; i <= pages; i++){
// construct the pdf doc
com.lowagie.text.Document document = new com.lowagie.text.Document();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
PdfWriter w = PdfWriter.getInstance(document, byteArrayOutputStream);
} catch (DocumentException e) {
log.error("failed to create PdfWriter: #0", e);
e.printStackTrace();
}
document.open();
// resize the tiff if necessary
Image tiff = TiffImage.getTiffImage(ra, i);
if (tiff == null)
break;
// scale and rotate if the img is too big
if (tiff.getHeight() > MAX_CONTENT_HEIGHT || tiff.getWidth() > MAX_CONTENT_WIDTH) {
// if image length is shorter than width by more than an inch
if ((tiff.getHeight() + ONE_INCH) < tiff.getWidth()) {
tiff.setRotationDegrees(-90);
}
tiff.scaleToFit(MAX_CONTENT_WIDTH, MAX_CONTENT_HEIGHT);
}
adjustRotation(imageBytes, tiff);
// add tiff to doc
try {
document.add(tiff);
} catch (DocumentException e) {
log.error("failed to add tiff to pdf doc: #0", e);
e.printStackTrace();
}
document.close();
pdfByteList.add(byteArrayOutputStream.toByteArray());
}
return pdfByteList;
}
/**
* convert jpg,gif,png,pdf,bmp to pdf
* @param imageBytes
* @return
* @throws Exception
*/
public byte[] convertOtherToPDFDoc(byte[] imageBytes) throws Exception {
com.lowagie.text.Document document = new com.lowagie.text.Document();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PdfWriter w = PdfWriter.getInstance(document, byteArrayOutputStream);
// w.setFullCompression();
// w.setCompressionLevel(9);
document.open();
Image jpg = Image.getInstance(imageBytes);
if (jpg == null)
return null;
if (jpg.getHeight() > MAX_CONTENT_HEIGHT || jpg.getWidth() > MAX_CONTENT_WIDTH)
jpg.scaleToFit(MAX_CONTENT_WIDTH, MAX_CONTENT_HEIGHT);
adjustRotation(imageBytes, jpg);
document.add(jpg);
document.close();
return byteArrayOutputStream.toByteArray();
}
/**
* Fixes the rotation of the image if the initial orientation was not normal.
* Requires the image has EXIF meta data and is of supported formatts, JPEG, TIFF, or GIF
*
* TODO - use apache Tika instead?
*
* Drew Metadata-extractor image library is used
* http://code.google.com/p/metadata-extractor/wiki/GettingStarted
* reference to orientation codes:
* http://jpegclub.org/exif_orientation.html
*
* @param imageBytes - used by parse out the EXIF image meta data
* @param iTextImg - used to rotate the image before embedding into a PDF
*/
private void adjustRotation(byte[] imageBytes, Image iTextImg){
if(imageBytes == null)
return;
if(iTextImg == null)
return;
BufferedInputStream bis= null;
try {
bis = new BufferedInputStream(new ByteArrayInputStream(imageBytes));
boolean waitForBytes = false;
int exifOrientation = 1;//default to normal, which needs no rotation
Metadata metadata = ImageMetadataReader.readMetadata(bis, waitForBytes);
if(log.isTraceEnabled()){
//list image's taken date
ExifSubIFDDirectory directory = metadata.getDirectory(ExifSubIFDDirectory.class);
if(directory != null && directory.containsTag(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL)){
Date date = directory.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL);
log.trace("Image taken on: " + date);
}
}
//find tag orientation
final ExifIFD0Directory exifIFD0Directory = metadata.getDirectory(ExifIFD0Directory.class);
if(exifIFD0Directory != null && exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)){
exifOrientation = exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
log.debug("Image EXIF orientation: " + exifOrientation);
}else{
log.debug("Image did not have EXIF orientation meta tag specified, not adjusting rotation");
return;
}
//Note rotation will turn it to the left, counter clockwise
switch(exifOrientation){
case 1:
log.debug("EXIF orientation of 1, no adjustment rotation required.");
break;//normal
case 2:
log.debug("EXIF orientation of 2, no adjustment rotation required.");
break;//flip/mirror, consider unflipping
case 3:
iTextImg.setRotationDegrees(180);//upside down
log.info("Adjusted uploaded image rotation by 180 degrees to compensate for EXIF orientation of 3, upside down.");
break;
case 4:
iTextImg.setRotationDegrees(180);//upside down
log.info("Adjusted uploaded image rotation by 180 degrees to compensate for EXIF orientation of 4, upside down.");
break;
case 5:
iTextImg.setRotationDegrees(-90);//right side is up
log.info("Adjusted uploaded image rotation by -90 degrees to compensate for EXIF orientation of 5, right side is up.");
break;
case 6:
iTextImg.setRotationDegrees(-90);//left side is down
log.info("Adjusted uploaded image rotation by -90 degrees to compensate for EXIF orientation of 6, left side is down.");
break;
case 7:
iTextImg.setRotationDegrees(90);//right side is down
log.info("Adjusted uploaded image rotation by 90 degrees to compensate for EXIF orientation of 7, right side is down.");
break;
case 8:
iTextImg.setRotationDegrees(90);//left side is up
log.info("Adjusted uploaded image rotation by 90 degrees to compensate for EXIF orientation of 6, left side is up.");
break;
}
}catch(Exception e) {
String errMsg = "Problem attempting to identify and rotate image based on EXIF metadata, error=" + e.getMessage();
log.error(errMsg, e);
}finally{
if(bis != null){
try {
bis.close();
} catch (Exception ie) {
log.error("Error closing buffered input stream for docupload bean meta data read, error=" + ie.getMessage(), ie);
}
}
}
}
/**
* merge a list of documents into a single pdf document
*
* @param pages
* @return
* @throws DocumentException
* @throws IOException
*/
public byte[] mergeDocList(List<DocumentAndCategory> pages) throws DocumentException, IOException {
if (pages != null && pages.size() > 0) {
List<PdfReader> readers = new ArrayList<PdfReader>();
Iterator<DocumentAndCategory> iteratorPDFs = pages.iterator();
// Create readers for each pdfs in the docList.
while (iteratorPDFs.hasNext()) {
DocumentAndCategory doc = iteratorPDFs.next();
PdfReader pdfReader = new PdfReader(doc.pdfData);
readers.add(pdfReader);
}
// create a pdf copy to hold the merged doc
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfCopyFields copy = new PdfCopyFields(baos);
copy.open();
// iterate every pdf reader and append to the pdf copy
Iterator<PdfReader> iter = readers.iterator();
while (iter.hasNext()) {
PdfReader pdfReader = iter.next();
copy.addDocument(pdfReader);
}
copy.close();
return baos.toByteArray();
}
return null;
}
/**
* each catagory can have multiple docs. this function
* converts all pdfs for each category into one category pdf.
*
* @param categorizedDocMap
* @return
* @throws Exception
*/
public List<SupportingDocument> mergePdfDocByCategory(Map<String, List<DocumentAndCategory>> categorizedDocMap) throws Exception {
List<SupportingDocument> pdfDocumentsList = new ArrayList<SupportingDocument>();
// iterate every category
Iterator<String> categoryIter = categorizedDocMap.keySet().iterator();
while (categoryIter.hasNext()) {
String category = categoryIter.next();
List<DocumentAndCategory> documentList = categorizedDocMap.get(category);
SupportingDocument sDocument = createSupportingDoc(documentList, category);
pdfDocumentsList.add(sDocument);
}
return pdfDocumentsList;
}
private SupportingDocument createSupportingDoc(List<DocumentAndCategory> pages, String category) throws DocumentException, IOException {
// merge all docs into 1 pdf
byte pdfBytes[] = mergeDocList(pages);
SupportingDocument sDocument = new SupportingDocument();
DocumentImage docImage = new DocumentImage();
docImage.setContent(pdfBytes);
docImage.setFileName(category + ".pdf");
docImage.setDescription(category + " Documents");
docImage.setMimeType("application/pdf");
sDocument.setDocumentImage(docImage);
//sDocument.setDocumentCategory(SupportingDocumentCategory.valueOf(category));
//sDocument.setDocumentCategory(appLookupManager.getApplicationLookupByName(EnumCtgry.DOCUMENT_CATEGORY, category));
sDocument.setDocumentCategory(category);
return sDocument;
}
/**
* Separate a multipage pdf into individual pages with 1 pdf document each
*
* @param pdfBytes
* @return
*/
public List<byte[]> separatePDFPages(byte[] pdfBytes) throws Exception {
List<byte[]> results = new ArrayList<byte[]>();
try {
PdfReader srcPdf = new PdfReader(pdfBytes);
boolean encryptedPDF = srcPdf.isEncrypted();
if(encryptedPDF){
log.debug("PDF is encrypted");
//byte[] userPwd = srcPdf.computeUserPassword();
//srcPdf = new PdfReader(pdfBytes, userPwd);
//srcPdf = new PdfReader(pdfBytes, "test".getBytes());
//com.lowagie.tools.plugins.Decrypt;
}
int totalPages = srcPdf.getNumberOfPages();
// itext pdf start with page 1
for (int c=1; c<=totalPages; c++) {
// each page gets its own document
com.lowagie.text.Document document = new com.lowagie.text.Document();
// Create a writer for the outputstream for each page doc
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
document.open();
PdfContentByte cb = writer.getDirectContent(); // Holds the PDF data
PdfImportedPage srcPage = null;
document.newPage();
//writer.setEncryption(1, 2, 2, PdfWriter.STANDARD_ENCRYPTION_128);
try{
srcPage = writer.getImportedPage(srcPdf, c);
}catch(Exception e){
if(e.getMessage().toLowerCase().contains("password") ||
e.getMessage().toLowerCase().contains("security")){
throw new PDFSecurityException(e);
}else{
throw e;
}
}
cb.addTemplate(srcPage, 0, 0);
outputStream.flush();
document.close();
outputStream.close();
results.add(outputStream.toByteArray());
}
} catch(PDFSecurityException pse){
throw pse;
}catch (Exception e) {
log.error("separatePDFPages() failed to separate pdf document into individual pages. " + e.getMessage());
e.printStackTrace();
}
return results;
}
/**
* convert each page in the pdf to jpg images
*
* @param pdfBytes
* @param allPages
* @return
* @throws Exception
*/
public List<byte[]> convertPDFToImagePages(byte[] pdfBytes, boolean allPages)
throws Exception {
List<byte[]> bytesList = new ArrayList<byte[]>();
PDFToImageConvertor pdfToImage = (PDFToImageConvertor) Component.getInstance(PDFToImageConvertor.class);
List<BufferedImage> bufferedImageList = pdfToImage.convertPDFToImage(pdfBytes, true, false);
for (int i = 0; i < bufferedImageList.size(); i++) {
BufferedImage bufferedImage = bufferedImageList.get(i);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", baos);
byte[] imageBytes = baos.toByteArray();
bytesList.add(imageBytes);
}
return bytesList;
}
/**
* Call by docupload.page.xml (docupload.xhtml) to load supporting_document from database.
* @throws Exception
*/
// @Create
public void loadFaxedDocuments() throws Exception {
log.debug(" getFaxedDocuments() of " + className + " is called to retrieve faxed documents.");
// load the pdf doc, one per category, from database
DocUploadManager docUploadManager = (DocUploadManager) Component.getInstance(DocUploadManagerBean.class);
loadSupportingDocuments(docUploadManager.getFaxedDocuments());
}
/**
* This method will be called from UI while entering into the validation decision table Tab.( productslist.xhtml)
* It will enable decisionTableEnabled instance variable so that listner method which is common for all other file uploads
* will execute code specific to validate decision table.
*/
public void enableDecisionTableConfig(){
log.info("Entered into enableDecisionTableConfig");
decisionTableEnabled = true;
}
/**
* This method is responsible for validating the uploaded xls file. It will be called from listener method
* if decisionTableEnabled instance variable is true. Internally this method invokes ProductInfo.validateDecisionTable()
* which has the actual logic of validating decision table and will return the msg accordingly.
* @param item item contains actual uploaded file data and metadata.
*/
public void validateDecisionTable(UploadedFile item){
log.info("Entered into validateDecisionTable, Validating decision table : "+item.getName());
ProductsInfo productInfo = (ProductsInfo) Component.getInstance("productsInfo");
PageScopeUtil pageScopeUtil = PageScopeUtil.instance();
String errMsg = productInfo.validateDecisionTable(item.getData());
pageScopeUtil.setErrMsg(errMsg);
}
/**
* This method is responsible for loading the existing supporting
* documents from db on recategorizedocs.xhtml
*
* @param componentName
* @throws Exception
*/
public void loadSupportingDocumets(String componentName) throws Exception {
log.info("Load Supporting Documents called for component:" + componentName);
DocUploadManager docUploadManager = (DocUploadManager) Component.getInstance(componentName);
// get supporting docs from database
loadSupportingDocuments(docUploadManager.getUploadedSupportingDocuments());
}
/**
*
* This helper function does the following
* 1. break each page in each supporting doc into separate pdf doc
* 2. create separate images (one per page) for each pdf doc for use as thumb nail
*
* All these in formation is stored in this.files list
* @param supportingDocList
* @throws Exception
*/
void loadSupportingDocuments(List<SupportingDocument> supportingDocList) throws Exception {
files.clear();
// iterate through each supporting doc and create an image for every page in the doc
if (supportingDocList != null) {
for (SupportingDocument sd : supportingDocList) {
// separate the pdf into individual pages. each page is a pdf doc
List<byte[]> pdfByteList = separatePDFPages(sd.getDocumentImage().getContent());
// get image for each page in the pdf
List<byte[]> imageBytesList = convertPDFToImagePages(sd.getDocumentImage().getContent(), true);
// add every page to this.files list
for (int i = 0; i < imageBytesList.size(); i++) {
byte[] pdfBytes = pdfByteList.get(i);
byte[] imageBytes = imageBytesList.get(i);
DocumentAndCategory pdfDoc = new DocumentAndCategory();
//pdfDoc.setDocumentCategory(sd.getDocumentCategory());
if (sd.getDocumentCategory() != null) pdfDoc.setDocumentCategory(sd.getDocumentCategory());
pdfDoc.setName(sd.getDocumentImage().getFileName());
pdfDoc.setPdfByte(pdfBytes);
pdfDoc.setLength(imageBytes.length);
pdfDoc.setData(imageBytes);
files.add(pdfDoc);
}
}
}
}
/**
* This method is responsible for removing a supporting
* documents from this.files. Change is not saved until user click update button
*
* @param docId
* @throws Exception
*/
public void removeSupportingDocument(int index) throws Exception {
files.remove(index);
}
public long getTimeStamp() {
return System.currentTimeMillis();
}
public ArrayList<DocumentAndCategory> getFiles() {
return files;
}
public void setFiles(ArrayList<DocumentAndCategory> files) {
this.files = files;
}
/**
* use by recategorizeddocs.xhtml
*
* @return
*/
public List<DocumentAndCategory> getSupportingDocs() {
return files;
}
public int getUploadsAvailable() {
return uploadsAvailable;
}
public void setUploadsAvailable(int uploadsAvailable) {
this.uploadsAvailable = uploadsAvailable;
}
public boolean getAutoUpload() {
return autoUpload;
}
public void setAutoUpload(boolean autoUpload) {
this.autoUpload = autoUpload;
}
public boolean getUseFlash() {
return useFlash;
}
public void setUseFlash(boolean useFlash) {
this.useFlash = useFlash;
}
public static class DocumentAndCategory extends Document {
private byte[] pdfData = null;
private boolean markForDelete = false;
/* private SupportingDocumentCategory documentCategory;
public SupportingDocumentCategory getDocumentCategory() {
return documentCategory;
}
public void setDocumentCategory(SupportingDocumentCategory documentCategory) {
this.documentCategory = documentCategory;
}*/
private String documentCategory;
public DocumentAndCategory(SupportingDocument document) {
setPdfByte(document.getDocumentImage().getContent());
setData(document.getDocumentImage().getContent());
setDocumentCategory(document.getDocumentCategory());
}
public DocumentAndCategory() {
}
public String getDocumentCategory(){
return documentCategory;
}
public void setDocumentCategory(String documentCategoryParam) {
this.documentCategory = documentCategoryParam;
}
public void setPdfByte(byte[] pdf) {
this.pdfData = pdf;
}
public void setMarkForDelete(boolean b) {
markForDelete = b;
}
public boolean getMarkForDelete() {
return markForDelete;
}
}
public Boolean checkDocumentsSignedByCoapplicant() {
DocUploadManager docUploadManager = (DocUploadManager) Component.getInstance(DocUploadManagerBean.COMPONENT_NAME);
return docUploadManager.checkDocumentsSignedByCoapplicant();
}
@RequestParameter private String appReqId;
public String getAppReqId() {
return appReqId;
}
public void setAppReqId(String appReqId) {
this.appReqId = appReqId;
}
public void propagateAppRequest(){
if(appReqId != null){
//This upload operation is being associated with an application/appRequest
//put the id into session so other things can use it if needed
//ie for page navigation after upload (check on co-applicant or not)
Contexts.getSessionContext().set("appReqId", new Long(appReqId));
}
//to support primary to upload his own document from home this action is required
//because of joint workflow streamline changes
if(customerLevelUpload != null && customerLevelUpload.equals("1")){
Long appReqId = (Long) Contexts.getSessionContext().get("appReqId");
if(appReqId != null){
Contexts.getSessionContext().set("isPrimaryContinuingAsJoint"+appReqId, false);
}
}
}
@SuppressWarnings("serial")
public static class PDFSecurityException extends Exception{
public PDFSecurityException(){
}
public PDFSecurityException(Exception e){
super(e);
}
public PDFSecurityException(Throwable t){
super(t);
}
public PDFSecurityException(String msg, Exception e){
super(msg, e);
}
}
public void driversLicenseUploadListener(FileUploadEvent event) throws Exception {
if (event.getComponent().getValueExpression("inputField") == null) {
throw new IllegalArgumentException("Can't find attribute: inputField");
}
final InputField inputField = (InputField)event.getComponent()
.getValueExpression("inputField")
.getValue(FacesContext.getCurrentInstance().getELContext());
final Customer customer = (Customer)ELEvaluator.eval(inputField.getField().getEntity().getEffectivePath());
customer.clearDriverLicenseRawDocuments();
documentUploadListener(event);
}
public void documentUploadListener(FileUploadEvent event) throws Exception {
PageScopeUtil pageScopeUtil = PageScopeUtil.instance();
pageScopeUtil.setErrMsg(null);//clear if previous request had errors, note cannot use event scope for the uploader as the upload is its own request
if (event.getComponent().getValueExpression("inputField") == null) {
throw new IllegalArgumentException("Can't find attribute: inputField");
}
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
final InputField inputField = (InputField)event.getComponent()
.getValueExpression("inputField")
.getValue(elContext);
final Customer customer = (Customer)ELEvaluator.eval(inputField.getField().getEntity().getEffectivePath());
final UploadedFile UploadedFile = event.getUploadedFile();
final String documentCategory = inputField.getDocumentCategory();
if (isEmpty(documentCategory)) {
throw new IllegalArgumentException("documentCategory is null, check field configuration: " +
inputField.getFieldGroup().getPage().getInputPageType() + ":" +
inputField.getFieldGroup().getName() + ":" +
inputField.getField().getName());
}
final RawSupportingDocument rawDocument = createRawDocument(UploadedFile);
rawDocument.setClient(userContext.getClient());
rawDocument.setCustomer(customer);
rawDocument.setCategory(documentCategory);
customer.addRawSupportingDocument(rawDocument);
// re-create document from raw documents (including new raw)
if (!isEmpty(rawDocument.getDocumentImage().getContent())) {
log.info("Processing file '#0' (#1K)",
rawDocument.getDocumentImage().getFileName(), rawDocument.getDocumentImage().getContent().length / 1024);
customer.removeDocumentsByCategory(documentCategory);
final List<RawSupportingDocument> rawDocuments = customer.getRawDocumentsByCategory(documentCategory);
final List<DocumentAndCategory> pages = extractPages(rawDocuments);
final SupportingDocument document = createSupportingDoc(pages, documentCategory);
customer.addSupportingDocument(document);
document.setCustomer(customer);
document.setClient(userContext.getClient());
}
}
private List<DocumentAndCategory> extractPages(List<RawSupportingDocument> rawDocuments) throws Exception {
List<DocumentAndCategory> docPages = new ArrayList<DocUploadBean.DocumentAndCategory>();
for (final RawSupportingDocument raw: rawDocuments) {
final DocumentImage docImage = raw.getDocumentImage();
if (docImage.isPdf()) {
docPages.addAll(extractPagesFromPdf(docImage.getContent(), docImage.getFileName()));
} else if (docImage.isTiff()) {
docPages.addAll(extractPagesFromTiff(docImage.getContent(), docImage.getFileName()));
} else {
docPages.add(extractPageFromImage(docImage.getContent(), docImage.getFileName()));
}
}
return docPages;
}
}
Thanks,
Manish