This blog I will show you how can make a free pdf previewer by generating images of pdf files and stream this png with a servlet to the objectImage of the jsf page.
If you have a web application where people can download pdf files, then this can be handy to provide a sort of previewer where the first page is shown, so they don't have to download or open every pdf file in acrobat reader to find the right document.
I use for the pdf to image generation the library of
jpedal and some great code of Richard Braman for the scaling and generating of the png files.
Here is how it looks. First you have to select a pdf document.
If we press the load button then the first page is shown with the total pagecount of the document.
Here is the jsf page.
<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces"
xmlns:afh="http://xmlns.oracle.com/adf/faces/html"
xmlns:cust="http://xmlns.oracle.com/adf/faces/customizable">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1252"/>
<title>view</title>
</head>
<body>
<h:form id="form1">
<af:panelGroup layout="vertical">
<af:panelGroup layout="horizontal">
<af:selectOneChoice id="choice" label="Document"
valueChangeListener="#{ViewerBean.changeChoice}">
<af:selectItem label="doc1" value="javascript"/>
<af:selectItem label="doc2" value="threads"/>
</af:selectOneChoice>
<af:commandButton text="Load" id="laden"/>
</af:panelGroup>
<af:panelGroup rendered="#{ViewerBean.showImage}" >
<af:commandButton text="Previous" id="vorige" actionListener="#{ViewerBean.vorigePagina}">
</af:commandButton>
<af:outputText value="#{ViewerBean.pagesOverview}"/>
<af:commandButton text="Next" id="volgende" actionListener="#{ViewerBean.volgendePagina}">
</af:commandButton>
</af:panelGroup>
<af:objectImage id="objectImage1"
partialTriggers="volgende vorige laden"
binding="#{ViewerBean.image}" rendered="#{ViewerBean.showImage}" />
</af:panelGroup>
</h:form>
</body>
</html>
</f:view>
</jsp:root>
I have to use partialtriggers on the objectImage else it won't refresh from the servlet if I press the load or previous / next buttons.
In the faces-config.xml I have defined two beans, the first is for the jsf page and the second is for the servlet. This hashmap I use to pass the parameters to the servlet
<managed-bean>
<managed-bean-name>ViewerBean</managed-bean-name>
<managed-bean-class>nl.ordina.pdf.backing.Viewer</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>ImageParametersBean</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
The backing bean of the jsf page. Basically I fill the servlet hashmap with the right values.
package nl.ordina.pdf.backing;
import java.util.HashMap;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import nl.ordina.pdf.ImageGenerator;
import oracle.adf.view.faces.component.core.output.CoreObjectImage;
public class Viewer {
private String tempPath = "d:/temp/";
private String imageUrl = "http://XPCND7010XMP:8988/pdf_previewer-ViewController-context-root/preview";
private String pdfArchivePath = "D:/documenten/MyeBooks/";
private int page = 1;
private int totalPages = 1;
private String pagesOverview;
private ImageGenerator img = new ImageGenerator();
private CoreObjectImage image;
private boolean showImage = false;
private HashMap parameters;
public Viewer() {
parameters = getHashMapServlet();
parameters.put("temppath",tempPath);
parameters.put("pdfarchivepath",pdfArchivePath);
}
public void volgendePagina(ActionEvent event){
if ( page <> 1 ){
page = page -1;
setServletPage(page);
}
}
public void changeChoice(ValueChangeEvent valueChangeEvent) {
page = 1;
setServletPage(page);
setServletDocument(valueChangeEvent.getNewValue().toString());
image.setSource(imageUrl);
showImage = true;
totalPages = img.pdfTotalPages(pdfArchivePath,valueChangeEvent.getNewValue().toString());
}
private HashMap getHashMapServlet(){
FacesContext fctx = FacesContext.getCurrentInstance();
return (HashMap)fctx.getApplication().createValueBinding("#{ImageParametersBean}").getValue(fctx);
}
private void setServletDocument(String doc){
parameters.put("document",doc);
}
private void setServletPage(int page){
parameters.put("page",page);
}
public void setImage(CoreObjectImage image) {
this.image = image;
}
public CoreObjectImage getImage() {
return image;
}
public void setShowImage(boolean showImage) {
this.showImage = showImage;
}
public boolean isShowImage() {
return showImage;
}
public String getPagesOverview() {
pagesOverview = page + " / " + totalPages;
return pagesOverview;
}
}
The servlet code which reads the hashmap , generates the png and streams it back to the jsf page
package nl.ordina.pdf.servlet;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import nl.ordina.pdf.ImageGenerator;
public class PreviewServlet extends HttpServlet {
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HashMap parameters = (HashMap)request.getSession().getAttribute("ImageParametersBean");
Integer page = 1;
String documentName = null, temp = null, archive = null;
if (parameters != null) {
documentName = parameters.get("document").toString();
page = new Integer(parameters.get("page").toString());
temp = parameters.get("temppath").toString();
archive = parameters.get("pdfarchivepath").toString();
}
ImageGenerator img = new ImageGenerator();
byte[] data = img.getPic(documentName,page.intValue(),temp,archive );
response.setContentType("image/png");
response.setContentLength( data.length );
response.getOutputStream().write( data );
response.getOutputStream().flush();
response.setHeader("Cache-Control", "no-cache");
}
}
Here is the
example project with the jpedal code for the png generations.
To get this working change the select one choice of the jsf page with not secured pdf files and without the .pdf extension. Change the variables in the viewer bean with your own values
private String tempPath = "d:/temp/";
private String imageUrl = "http://XPCND7010XMP:8988/pdf_previewer-ViewController-context-root/preview";
private String pdfArchivePath = "D:/projecten/justid/cdd_archief/PEN_ARCH/01/01/45/";
Update
JPedal released a new jar where they changed something and you can get the following exception 08/05/26 17:07:21 java.awt.HeadlessException
08/05/26 17:07:21 at sun.java2d.HeadlessGraphicsEnvironment.getDefaultScreenDevice(HeadlessGraphicsEnvironment.java:65)
Here is the code of the new imagegenerator
package nl.ordina.pdf;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.jpedal.*;
import org.jpedal.exception.PdfException;
import java.io.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
public class ImageGenerator {
public ImageGenerator() {
}
public byte[] getPic( String document,Integer picture
, String temp, String archive, String generateAlways) {
byte[] data = null;
String path = temp+document+"_"+picture+".png";
try {
boolean exists = (new File(path)).exists();
if (exists && generateAlways.equals("no") ) {
data = read(path);
} else {
path= PDFToPic(archive,document,picture,temp);
data = read(path);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public int pdfTotalPages(String filepath,String doc)
{
try
{
PdfDecoder decoder = new PdfDecoder();
decoder.openPdfFile(filepath+doc+".pdf");
if (decoder.isFileViewable()) {
int PageCount = decoder.getPageCount() ;
return PageCount;
}
} catch(Exception err) {
err.printStackTrace();
}
return 0;
}
public byte[] read(String file) throws IOException {
InputStream in = new FileInputStream(file);
byte[] data = new byte[in.available()];
in.read(data);
return data;
}
public String PDFToPic(String filepath,String doc, int picture,String temppath)
{
try
{
PdfDecoder decoder = new PdfDecoder();
decoder.openPdfFile(filepath+doc+".pdf");
if (decoder.isFileViewable()) {
int PageCount = decoder.getPageCount() ;
double ImageWidth = 0;
double ImageHeight = 0;
if ( picture <= PageCount) {
//generate png files
generatePNGfromPDF(decoder, picture, ImageWidth, ImageHeight, doc,temppath);
return temppath+doc+"_"+picture+".png";
} else {
generatePNGfromPDF(decoder, PageCount, ImageWidth, ImageHeight, doc,temppath);
return temppath+doc+"_"+PageCount+".png";
}
}
} catch(Exception err) {
err.printStackTrace();
}
return null;
}
private void generatePNGfromPDF( PdfDecoder decoder
, int PageNumber
, double width
, double height
, String doc
, String temppath){
//int size = 100;
decoder.useHiResScreenDisplay(true);
decoder.setExtractionMode(PdfDecoder.RAWIMAGES+PdfDecoder.FINALIMAGES);
int scaling = 100;
try {
BufferedImage PDF = decoder.getPageAsImage(PageNumber);
if(scaling!=100){
int newWidth=PDF.getWidth()*scaling/100;
//only 1 new co-ord needed so use -1 for other as aspect ration does not change
Image scaledImage= PDF.getScaledInstance(newWidth,-1,BufferedImage.SCALE_SMOOTH);
PDF = new BufferedImage(scaledImage.getWidth(null),scaledImage.getHeight(null) , BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = PDF.createGraphics();
g2.drawImage(scaledImage, 0, 0,null);
}
File fileToSave = new File( temppath+doc+"_"+PageNumber+".png");
boolean failed=decoder.getObjectStore().saveStoredImage(temppath+doc+"_"+PageNumber+".png",PDF,true,false,"png");
} catch (PdfException e) {
// TODO
e.printStackTrace();
}
}
}