/*
* Copyright 2006-2012 ICEsoft Technologies Inc.
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* 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.icepdf.examples.jsf.viewer.view;
//import com.icesoft.faces.component.inputfile.FileInfo;
//import com.icesoft.faces.component.inputfile.InputFile;
import org.icefaces.ace.component.fileentry.FileEntry;
import org.icefaces.ace.component.fileentry.FileEntryEvent;
import org.icefaces.ace.component.fileentry.FileEntryResults;
import org.icepdf.core.pobjects.fonts.FontFactory;
import org.icepdf.examples.jsf.viewer.util.FacesUtils;
import javax.annotation.PreDestroy;
import javax.faces.event.ActionEvent;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.ValueChangeEvent;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.io.File;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* DocumentManager is a session scpoed bean responsible for managing the
* state of PDF documents. When a new document is loaded it is added to the
* document history list. The document state is stored for all documents
* opened during the user session but only one document is actually kept
* open in memory.
*
* @since 3.0
*/
public class DocumentManager implements Serializable {
public static final String BEAN_NAME = "documentManager";
private static final Logger logger =
Logger.getLogger(DocumentManager.class.toString());
// state of current document, outline, annotations, page cursor, zoom, path
// and image export type.
private DocumentState currentDocumentState;
// list of demo files if present
private static final String DEMO_DIRECTORY = "/WEB-INF/demos/";
private static ArrayList<DocumentState> demoFilePaths;
private int fileUploadProgress;
private boolean uploadDialogVisibility;
// enable/disable font engine state.
private boolean isFontEngine = true;
private static boolean isDemo;
// list of document history, we only keep one document open at at time
// but we can keep a list of previous document states encase the document
// is opened again.
private ArrayList<DocumentState> documentStateHistory =
new ArrayList<DocumentState>(10);
/**
* Opens the PDF document specified by the request param "documentPath".
*
* @param event jsf action event.
*/
public void openDocument(ActionEvent event) {
try {
// opens a document from based on the path information passed
// in as a request parameter. Called from either the document history
// or demo folder list.
String documentPath = FacesUtils.getRequestParameter("documentPath");
documentPath = URLDecoder.decode(documentPath, "UTF-8");
// try to load the document
loadFilePath(documentPath);
// refresh current page state.
refreshDocumentState();
} catch (Throwable e) {
logger.log(Level.WARNING, "Error opening document.", e);
e.printStackTrace();
}
}
/**
* Loads the specified document
*
* @param demoFileName name of demo file to load
*/
public void setDocumentDemoFile(String demoFileName) {
if (demoFilePaths == null) {
try {
loadDemoFiles();
} catch (Throwable e) {
logger.log(Level.WARNING, "Error loading demo files.", e);
}
}
// check for demo file in path as an id, we reuse the document state
for (DocumentState documentSate : demoFilePaths) {
if (documentSate.getDocumentName().equals(demoFileName)) {
currentDocumentState = new DocumentState(documentSate);
}
}
// see if we can open the document.
try {
currentDocumentState.openDocument();
} catch (Throwable e) {
logger.log(Level.WARNING, "Error loading file default file: ", e);
}
}
/**
* Gets the upload progress bar perscent complete. Should only be
* called be the progress bar component.
*
* @param event inputFile eventObject.
*/
// public void fileUploadProgress(EventObject event) {
// InputFile ifile = (InputFile) event.getSource();
// if (ifile != null) {
// fileUploadProgress = ifile.getFileInfo().getPercent();
// }
// }
/**
* File upload event, we only listen for document that have been saved and
* are of type .pdf.
*
* @param e jsf action event.
*/
public void fileUploadMonitor(FileEntryEvent e) {
FileEntry fe = (FileEntry) e.getComponent();
FileEntryResults results = fe.getResults();
for (FileEntryResults.FileInfo i : results.getFiles()) {
if (i.isSaved()) {
File file = i.getFile();
if (file.getName().toLowerCase().endsWith(".pdf")) {
loadFilePath(file.getPath());
// refresh current page state.
refreshDocumentState();
FacesUtils.addInfoMessage("Successfully upload PDF Document.");
}
} else {
FacesUtils.addInfoMessage("Error during upload of PDF Document.");
logger.log(Level.WARNING,
"Error opening PDF document that was uploaded.");
}
}
}
// public void fileUploadMonitor(ActionEvent event) {
// try {
// InputFile inputFile = (InputFile) event.getSource();
// FileInfo fileInfo = inputFile.getFileInfo();
// if (fileInfo.getStatus() == FileInfo.SAVED) {
// logger.info("File UPload Path " + fileInfo.getFileName());
// if (fileInfo.getFileName().toLowerCase().endsWith(".pdf")) {
// loadFilePath(fileInfo.getPhysicalPath());
// // refresh current page state.
// refreshDocumentState();
// FacesUtils.addInfoMessage("Successfully upload PDF Document.");
// }
// }
// } catch (Throwable e) {
// FacesUtils.addInfoMessage("Error during upload of PDF Document.");
// logger.log(Level.WARNING,
// "Error opening PDF document that was uploaded."
// + e.getMessage(), e);
// }
// }
/**
* Stats the upload process by showing an upload dialog. Users can either
* chose to open a document or close the dialog.
*
* @param event jsf action event.
*/
public void uploadDocument(ActionEvent event) {
// reset file progress.
fileUploadProgress = 0;
// show upload dialog
uploadDialogVisibility = true;
}
/**
* Utility method which loads the PDF document specified by the document
* path.
*
* @param documentPath path to PDF document.
*/
private void loadFilePath(String documentPath) {
// check the state history to see if this session has opened the document
// in question before. If so re-use document state.
DocumentState documentState = null;
for (DocumentState documentHistoryState : documentStateHistory) {
if (documentPath.equals(documentHistoryState.getDocumentPath())) {
documentState = documentHistoryState;
break;
}
}
// setup of current references so that the servlet can show the state
// in question.
if (documentState == null) {
documentState = new DocumentState(documentPath);
documentStateHistory.add(0, documentState);
} else {
// if the document changes then we'll close the previous one.
// but only if it is not shared session.
if (currentDocumentState != null &&
!currentDocumentState.isSharedSession() &&
!documentState.getDocumentName().equals(
currentDocumentState.getDocumentName())) {
currentDocumentState.closeDocument();
}
// update history queue
documentStateHistory.remove(documentState);
documentStateHistory.add(0, documentState);
}
// see if we can open the document.
try {
documentState.openDocument();
// assign the newly open document state.
currentDocumentState = documentState;
} catch (Throwable e) {
logger.log(Level.WARNING, "Error loading file at path: " + documentPath, e);
System.out.println("Error Loading file " + e.getMessage());
FacesUtils.addInfoMessage("Could not open the PDF file." +
documentState.getDocumentName());
// clean up and reset the viewer state.
if (!documentState.isSharedSession()) {
documentState.closeDocument();
}
}
}
/**
* Updates the current document state page cursor to point to the next
* logical page in the document. Nothing happens if there is now documents
* loaded or if the page cursor is at the end of the document.
*
* @param event jsf action event.
*/
public void nextPage(ActionEvent event) {
try {
// if their is is a currentDocument then go to the next page.
if (currentDocumentState != null) {
int totalPages = currentDocumentState.getDocumentLength();
int currentPage = currentDocumentState.getPageCursor();
currentPage++;
if (currentPage > totalPages) {
currentPage = totalPages;
}
currentDocumentState.setPageCursor(currentPage);
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error paging document.", e);
}
}
/**
* Updates the current document state page cursor to point to the previous
* logical page in the document. Nothing happens if there is now documents
* loaded or if the page cursor is at the begining of the document.
*
* @param event jsf action event.
*/
public void previousPage(ActionEvent event) {
try {
// if their is is a currentDocument then go to the next page.
if (currentDocumentState != null) {
int currentPage = currentDocumentState.getPageCursor();
currentPage--;
if (currentPage < 1) {
currentPage = 1;
}
currentDocumentState.setPageCursor(currentPage);
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error paging document.", e);
}
}
/**
* Rotate the current document state by 90 degrees.
*
* @param event jsf action event.
*/
public void rotateDocumentRight(ActionEvent event) {
try {
if (currentDocumentState != null) {
float viewRotation = currentDocumentState.getRotation();
viewRotation -= DocumentState.ROTATION_FACTOR;
if (viewRotation < 0) {
viewRotation += 360;
}
currentDocumentState.setRotation(viewRotation);
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error rotating document.", e);
}
}
/**
* Rotate the current document state by -90 degrees.
*
* @param event jsf action event.
*/
public void rotateDocumentLeft(ActionEvent event) {
try {
if (currentDocumentState != null) {
float viewRotation = currentDocumentState.getRotation();
viewRotation += DocumentState.ROTATION_FACTOR;
viewRotation %= 360;
currentDocumentState.setRotation(viewRotation);
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error rotating document.", e);
}
}
/**
* Gets a list of demo files located in the demo folder.
*
* @return list of PDF document paths in demo folder.
*/
public ArrayList<DocumentState> getDemoFilePaths() {
if (demoFilePaths == null) {
try {
loadDemoFiles();
} catch (Throwable e) {
logger.log(Level.WARNING, "Error loading demo files.", e);
}
}
return demoFilePaths;
}
/**
* Go to the page number specified by the request param "pageNumber".
*
* @param event JSF action event.
*/
public void goToDestination(AjaxBehaviorEvent event) {
try {
List selected = currentDocumentState.getSelected();
if (selected != null && selected.size() > 0){
OutlineItemTreeNode selectedNode = (OutlineItemTreeNode)selected.get(0);
int pageNumber = ((OutlineItemTreeNode.NodeUserObject)selectedNode.getUserObject()).getGoToPage();
currentDocumentState.setPageCursor(pageNumber + 1);
refreshDocumentState();
}
// int pageNumber = Integer.parseInt(
// FacesUtils.getRequestParameter("pageNumber"));
// currentDocumentState.setPageCursor(pageNumber + 1);
// refresh current page state.
} catch (Throwable e) {
logger.log(Level.WARNING, "Error going to specified page number.");
}
}
/**
* Go to the page number specifed by the current document state. If the
* page number is not in the range of the documents pages it is altered to
* the nearest bound.
*
* @param event jsf action event.
*/
public void goToPage(ActionEvent event) {
try {
if (currentDocumentState != null) {
int totalPages = currentDocumentState.getDocumentLength();
int currentPage = currentDocumentState.getPageCursor();
if (currentPage > totalPages) {
currentDocumentState.setPageCursor(totalPages);
}
if (currentPage < 1) {
currentDocumentState.setPageCursor(1);
}
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error paging document.", e);
}
}
public void goToPage2(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
} else {
// refresh current page state.
goToPage(null);
}
}
/**
* Gets the image associated with the current document state.
*
* @return image represented by the pageCursor, rotation and zoom.
*/
public Image getCurrentPageImage() {
if (currentDocumentState != null) {
FontFactory.getInstance().setAwtFontSubstitution(!isFontEngine);
// invalidate the content streams, so we are paint with as close
// to as possible the correct awt font state.
if (isDemo) {
currentDocumentState.invalidate();
}
return currentDocumentState.getPageImage();
}
return null;
}
/**
* Toggle the font engine functionality and refresht he current page view
*
* @param event jsf action event.
*/
public void toggleFontEngine(ActionEvent event) {
try {
if (currentDocumentState != null) {
// toggle flag.
isFontEngine = !isFontEngine;
// refresh current page state.
refreshDocumentState();
}
} catch (Throwable e) {
logger.log(Level.WARNING, "Error enable/disabling document.", e);
}
}
/**
* Read all PDF document that can be found in the Servlet context path +
* DEMO_DIRECTORY .
*
* @throws MalformedURLException error getting file path.
* @throws URISyntaxException error getting file path.
*/
public static void loadDemoFiles() throws MalformedURLException, URISyntaxException {
// Loading of the resource must be done the "JSF way" so that
// it is agnostic about it's environment (portlet vs servlet).
HttpSession session = FacesUtils.getHttpSession(false);
ServletContext context = session.getServletContext();
String demoFilesPath = context.getRealPath(DEMO_DIRECTORY);
// get listing of pdf files that might be in this folder.
File directory = new File(demoFilesPath);
String[] fontPaths;
demoFilePaths = new ArrayList<DocumentState>(5);
if (directory.canRead()) {
fontPaths = directory.list();
for (String pdfFile : fontPaths) {
if (pdfFile != null &&
pdfFile.endsWith(".pdf")) {
demoFilePaths.add(new DocumentState(
directory.getAbsolutePath() + File.separatorChar +
pdfFile, true));
}
}
}
}
private void refreshDocumentState() {
if (currentDocumentState != null) {
// setup page size
currentDocumentState.calculatePageImageSize();
// refresh current image.
currentDocumentState.generateDocumentID();
}
}
public int getFileUploadProgress() {
return fileUploadProgress;
}
public void setFileUploadProgress(int fileUploadProgress) {
this.fileUploadProgress = fileUploadProgress;
}
public void documentZoomLevelChange(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
} else {
// refresh current page state.
refreshDocumentState();
}
}
@PreDestroy
public void dispose() throws Exception {
if (currentDocumentState != null &&
!currentDocumentState.isSharedSession()) {
currentDocumentState.closeDocument();
}
}
public ArrayList<DocumentState> getDocumentStateHistory() {
return documentStateHistory;
}
public DocumentState getCurrentDocumentState() {
return currentDocumentState;
}
public void toggleUploadDialogVisibility(ActionEvent event) {
uploadDialogVisibility = !uploadDialogVisibility;
}
public boolean isUploadDialogVisibility() {
return uploadDialogVisibility;
}
public void setUploadDialogVisibility(boolean uploadDialogVisibility) {
this.uploadDialogVisibility = uploadDialogVisibility;
}
public boolean isFontEngine() {
return isFontEngine;
}
public void setFontEngine(boolean fontEngine) {
isFontEngine = fontEngine;
}
public boolean isDemo() {
return isDemo;
}
public void setDemo(boolean demo) {
isDemo = demo;
}
}