
org.jpedal.examples.baseviewer.BaseViewerFX Maven / Gradle / Ivy
/*
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info: http://www.idrsolutions.com
* Help section for developers at http://www.idrsolutions.com/support/
*
* (C) Copyright 1997-2016 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* BaseViewerFX.java
* ---------------
*/
package org.jpedal.examples.baseviewer;
import java.io.File;
import java.util.List;
import javafx.animation.Transition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.control.ToolBar;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.jpedal.PdfDecoderFX;
import org.jpedal.examples.viewer.gui.javafx.FXViewerTransitions;
import org.jpedal.examples.viewer.gui.javafx.FXViewerTransitions.TransitionDirection;
import org.jpedal.examples.viewer.gui.javafx.FXViewerTransitions.TransitionType;
import org.jpedal.examples.viewer.gui.javafx.dialog.FXInputDialog;
import org.jpedal.exception.PdfException;
import org.jpedal.external.PluginHandler;
import org.jpedal.objects.PdfPageData;
import org.jpedal.parser.DecoderOptions;
/** BaseViewerFX
*
* If you are compiling, you will need to download all the examples source files from :
* How to View PDF File in Java.
*
* Run directly from jar with java -cp jpedal.jar org/jpedal/examples/baseviewer/BaseViewerFX
*
* There are plenty of tutorials on how to configure the Viewer on our website Support Section.
*
* This class provides example code to create a bare-bones PDF Viewer in JavaFX.
*
* Click here for a bare-bones PDF Viewer in Java Swing.
*
* For more help and tutorials, visit our websites Support Section.
*
*/
public class BaseViewerFX extends Application {
private final org.jpedal.PdfDecoderFX pdf = new org.jpedal.PdfDecoderFX();
PluginHandler customPluginHandle;
/**
* Enum to control how we fit the content to the page.
*
* AUTO will automatically fit the content to the stage depending on its orientation
* WIDTH will fit the content to the stage width depending on its orientation
* HEIGHT will fit the content to the stage height depending on its orientation
*/
public enum FitToPage{
AUTO, WIDTH, HEIGHT, NONE
}
String PDFfile;
//Variable to hold the current file/directory
File file;
//These two variables are todo with PDF encryption & passwords
private String password; //Holds the password from the JVM or from User input
private boolean closePasswordPrompt; //boolean controls whether or not we should close the prompt box
// Layout panes
private VBox top;
private HBox bottom;
private ScrollPane center;
//Group is a container which holds the decoded PDF content
private Group group;
// for the location of the pdf file
private Text fileLoc;
private float scale = 1.0f;
private final float[] scalings = {0.01f, 0.1f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 2.0f, 4.0f, 7.5f, 10.0f};
private int currentScaling=5;
private static final float insetX = 25;
private static final float insetY = 25;
private int currentPage = 1;
Stage stage;
Scene scene;
//Controls size of the stage, in theory setting this to a higher value will
//increase image quality as there's more pixels due to higher image resolutions
static final int FXscaling=1;
FitToPage zoomMode = FitToPage.AUTO;
private TransitionType transitionType = TransitionType.None;
public static void main(final String[] args){
DecoderOptions.javaVersion = Float.parseFloat(System.getProperty("java.specification.version"));
System.out.println("JAVA VERSION : "+ DecoderOptions.javaVersion);
if (DecoderOptions.javaVersion < 1.8f) {
throw new RuntimeException("You need to Run Java 1.8+");
}
launch(args);
}
/**
* launches BaseViewerFX viewer using supplied stage for displaying PDF files
*
* @param stage is of type final Stage
*/
@Override
public void start(final Stage stage){
stage.setTitle("Base Viewer FX - " + PdfDecoderFX.version);
// Set transition if set in properites:
String trans = System.getProperty("org.jpedal.fxtransition");
if(trans != null){
// Ensure correct lettering
trans = trans.substring(0, 1).toUpperCase() + trans.substring(1).toLowerCase();
transitionType = TransitionType.valueOf(trans);
}
// Set page if set in JVM flag
final String pageNum = System.getProperty("org.jpedal.page");
if(pageNum != null){
currentPage = Integer.parseInt(pageNum);
}
this.stage=stage;
scene=setupViewer(800,600);
// Get command line arguments
final List args = this.getParameters().getUnnamed();
/**
* setup initial display
* Setting this before loadPDF() gives access to the toolbar buttons
* when called in loadPDF() via id.
*/
stage.setScene(scene);
stage.show();
// this.getParameters().getUnamed() will never be null according to FX Javadoc, so no null check needed
// http://docs.oracle.com/javafx/2/api/javafx/application/Application.Parameters.html#getUnnamed%28%29
if(args.size() == 1){
final String input = args.get(0);
file = new File(input);
Platform.runLater(new Runnable() {
@Override
public void run() {
loadPDF(file);
// Unset the page
if(System.getProperty("org.jpedal.page") != null) {
System.setProperty("org.jpedal.page", "");
}
}
});
}
addListeners();
}
/**
* creates all the components and adds change listeners for auto-centering
* for JavaFX PDF viewer
*
* @param w The width to use for the viewer
* @param h The height to use for the viewer
* @return scene
*/
public Scene setupViewer(final int w, final int h){
/*
* Setting up layout panes and assigning them to the appropiate locations
*/
final BorderPane root = new BorderPane();
top = new VBox();
root.setTop(top);
top.getChildren().add(setupToolBar());
bottom = new HBox();
bottom.setPadding(new Insets(0,10,0,10));
root.setBottom(bottom);
center = new ScrollPane();
center.setPannable(true);
center.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
center.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
//needs to be added via group so resizes (see http://pixelduke.wordpress.com/2012/09/16/zooming-inside-a-scrollpane/)
group=new Group();
group.getChildren().add(pdf);
center.setContent(group);
root.setCenter(center);
center.viewportBoundsProperty().addListener(new ChangeListener() {
@Override
public void changed(final ObservableValue extends Bounds> ov, final Bounds ob, final Bounds nb) {
adjustPagePosition(nb);
}
});
/**Sets the text to be displayed at the bottom of the FX Viewer**/
fileLoc = new Text("No PDF Selected");
fileLoc.setId("file_location");
bottom.getChildren().add(fileLoc);
scene=new Scene(root, w*FXscaling, h*FXscaling);
return scene;
}
public void addListeners(){
/**
* auto adjust so dynamically resized as viewer width alters
*/
scene.widthProperty().addListener(new ChangeListener() {
@Override
public void changed(final ObservableValue extends Number> observableValue, final Number oldSceneWidth, final Number newSceneWidth) {
fitToX(zoomMode);
}
});
scene.heightProperty().addListener(new ChangeListener() {
@Override
public void changed(final ObservableValue extends Number> observableValue, final Number oldSceneHeight, final Number newSceneHeight) {
fitToX(zoomMode);
}
});
/**
* Controls for dragging a PDF into the scene
* Using the dragboard, which extends the clipboard class,
* detect a file being dragged onto the scene and if the user drops the file
* we load it.
*/
scene.setOnDragOver(new EventHandler() {
@Override
public void handle(final DragEvent event) {
final Dragboard db = event.getDragboard();
if (db.hasFiles()) {
event.acceptTransferModes(TransferMode.COPY);
} else {
event.consume();
}
}
});
scene.setOnDragDropped(new EventHandler() {
@Override
public void handle(final DragEvent event) {
final Dragboard db = event.getDragboard();
boolean success = false;
if(db.hasFiles()){
success = true;
// Only get the first file from the list
file = db.getFiles().get(0);
Platform.runLater(new Runnable() {
@Override
public void run() {
loadPDF(file);
}
});
}
event.setDropCompleted(success);
event.consume();
}
});
}
/**
* Sets up a MenuBar to be used at the top of the window.
*
* It contains one Menu - navMenu - which allows the user to open and navigate pdf files
*
* @return
*/
private ToolBar setupToolBar() {
final ToolBar toolbar = new ToolBar();
final Button open = new Button("Open");
final Button back = new Button("Back");
final ComboBox pages = new ComboBox();
final Label pageCount = new Label();
final Button forward = new Button("Forward");
final Button zoomIn = new Button("Zoom in");
final Button zoomOut = new Button("Zoom out");
final Button fitWidth = new Button("Fit to Width");
final Button fitHeight = new Button("Fit to Height");
final Button fitPage = new Button("Fit to Page");
ComboBox transitionList = new ComboBox();
open.setId("open");
back.setId("back");
pageCount.setId("pgCount");
pages.setId("pages");
forward.setId("forward");
zoomIn.setId("zoomIn");
zoomOut.setId("zoomOut");
fitWidth.setId("fitWidth");
fitHeight.setId("fitHeight");
fitPage.setId("fitPage");
/**
* Open the PDF File
*/
open.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
final FileChooser chooser = new FileChooser();
chooser.setTitle("Open PDF file");
//Open directory from existing directory
if(file != null){
final File existDirectory = file.getParentFile();
if(existDirectory.exists()) {
chooser.setInitialDirectory(existDirectory);
}
}
//Set extension filter
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("PDF files (*.pdf)", "*.pdf");
chooser.getExtensionFilters().add(extFilter);
file = chooser.showOpenDialog(null);
if(file != null){
Platform.runLater(new Runnable() {
@Override
public void run() {
loadPDF(file);
}
});
}
}
});
pages.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener() {
@Override public void changed(final ObservableValue extends Number> ov, final Number oldVal, final Number newVal) {
if(newVal.intValue() != -1 && newVal.intValue()+1 != currentPage){
final int newPage = newVal.intValue() + 1;
goToPage(newPage);
}
}});
// Navigate backward
back.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
if (currentPage > 1) {
goToPage(currentPage - 1);
}
}
});
// Navigate forward
forward.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
if (currentPage < pdf.getPageCount()) {
goToPage(currentPage + 1);
}
}
});
// Zoom in
zoomIn.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
zoomMode = FitToPage.NONE;
if (currentScaling < scalings.length - 1) {
currentScaling = findClosestIndex(scale, scalings);
if (scale >= scalings[findClosestIndex(scale, scalings)]) {
currentScaling++;
}
scale = scalings[currentScaling];
}
pdf.setPageParameters(scale, currentPage);
adjustPagePosition(center.getViewportBounds());
}
});
// Zoom out
zoomOut.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
zoomMode = FitToPage.NONE;
if (currentScaling > 0) {
currentScaling = findClosestIndex(scale, scalings);
if (scale <= scalings[findClosestIndex(scale, scalings)]) {
currentScaling--;
}
scale = scalings[currentScaling];
}
pdf.setPageParameters(scale, currentPage);
adjustPagePosition(center.getViewportBounds());
}
});
// Fit to width
fitWidth.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
zoomMode = FitToPage.WIDTH;
fitToX(FitToPage.WIDTH);
}
});
// Fit to height
fitHeight.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
zoomMode = FitToPage.HEIGHT;
fitToX(FitToPage.HEIGHT);
}
});
// Fit to Page
fitPage.setOnAction(new EventHandler() {
@Override
public void handle(final ActionEvent t) {
zoomMode = FitToPage.AUTO;
fitToX(FitToPage.AUTO);
}
});
final Region spacerLeft = new Region();
final Region spacerRight = new Region();
HBox.setHgrow(spacerLeft, Priority.ALWAYS);
HBox.setHgrow(spacerRight, Priority.ALWAYS);
// Set up the ComboBox for transitions
final ObservableList options = FXCollections.observableArrayList();
for(final TransitionType transition : TransitionType.values()){
options.add(transition.name());
}
if(!options.isEmpty()){
transitionList = new ComboBox(options);
// Put before setValue so that setValue triggers the event
transitionList.valueProperty().addListener(new ChangeListener(){
@Override public void changed(final ObservableValue extends String> ov, final String oldVal, final String newVal) {
transitionType = TransitionType.valueOf(newVal);
}});
transitionList.setValue(options.get(transitionType.ordinal()));
}
toolbar.getItems().addAll(open, spacerLeft, back, pages, pageCount, forward, zoomIn, zoomOut, spacerRight, transitionList);
return toolbar;
}
/**
* take a File handle to PDF file on local filesystem and displays in PDF viewer
*
* @param input The PDF file to load in the viewer
*/
public void loadPDF(final File input){
if(input == null) {
return;
}
scale = 1; //reset to default for new page
PDFfile=input.getAbsolutePath();
fileLoc.setText(PDFfile);
openFile(input,null,false);
}
/**
* take a File handle to PDF file on local filesystem and displays in PDF viewer
*
* @param input The PDF file to load in the viewer
*/
public void loadPDF(final String input){
if(input == null) {
return;
}
scale = 1; //reset to default for new page
PDFfile=input;
fileLoc.setText(PDFfile);
if(input.startsWith("http")){
openFile(null, input,true);
}else{
openFile(new File(input),null,false);
}
}
private void openFile(final File input,String url, boolean isURL) {
try {
//Open the pdf file so we can check for encryption
if(isURL){
pdf.openPdfFileFromURL(url,false);
}else{
pdf.openPdfFile(input.getAbsolutePath());
}
if(customPluginHandle!=null){
if(isURL){
customPluginHandle.setFileName(url);
}else{
customPluginHandle.setFileName(input.getAbsolutePath());
}
}
if(System.getProperty("org.jpedal.page") != null && !System.getProperty("org.jpedal.page").isEmpty()){
currentPage = currentPage < 1 ? 1 : currentPage;
currentPage = currentPage > pdf.getPageCount() ? pdf.getPageCount() : currentPage;
}else{
currentPage = 1;
}
/**
* This code block deals with user input and JVM passwords in Encrypted PDF documents.
*/
if(pdf.isEncrypted()){
int passwordCount = 0; //Monitors how many attempts there have been to the password
closePasswordPrompt = false; //Do not close the prompt box
//While the PDF content is not viewable, repeat until the correct password is found
while(!pdf.isFileViewable() && !closePasswordPrompt) {
/**
* See if there's a JVM flag for the password & Use it if there is
* Otherwise prompt the user to enter a password
*/
if(System.getProperty("org.jpedal.password")!=null){
password = System.getProperty("org.jpedal.password");
}else if(!closePasswordPrompt){
showPasswordPrompt(passwordCount);
}
//If we have a password, try and open the PdfFile again with the password
if (password != null) {
if(isURL){
pdf.openPdfFileFromURL(url,false,password);
}else{
pdf.openPdfFile(input.getAbsolutePath());
}
//pdf.setEncryptionPassword(password);
}
passwordCount += 1; //Increment he password attempt
}
}
// Set up top bar values
((Label)top.lookup("#pgCount")).setText("/" + pdf.getPageCount());
final ComboBox pages = ((ComboBox)top.lookup("#pages"));
pages.getItems().clear();
for(int i = 1; i <= pdf.getPageCount(); i++){
pages.getItems().add(String.valueOf(i));
}
// Goes to the first page and starts the decoding process
goToPage(currentPage);
} catch (final PdfException ex) {
ex.printStackTrace();
}
}
/**
* This method will show a popup box and request for a password.
*
* If the user does not enter the correct password it will ask them to try again.
* If the user presses the Cross button, the password prompt will close.
*
* @param passwordCount is an int which represents the current input attempt
*/
private void showPasswordPrompt(final int passwordCount){
//Setup password prompt content
final Text titleText = new Text("Password Request");
final TextField inputPasswordField = new TextField("Please Enter Password");
//If the user has attempted to enter the password more than once, change the text
if(passwordCount >= 1){
titleText.setText("Incorrect Password");
inputPasswordField.setText("Please Try Again");
}
final FXInputDialog passwordInput = new FXInputDialog(stage, titleText.getText()){
@Override
protected void positiveClose(){
super.positiveClose();
closePasswordPrompt = true;
}
};
password = passwordInput.showInputDialog();
}
private void fitToX(final FitToPage fitToPage) {
if(fitToPage == FitToPage.NONE) {
return;
}
final float pageW=pdf.getPdfPageData().getCropBoxWidth2D(currentPage);
final float pageH=pdf.getPdfPageData().getCropBoxHeight2D(currentPage);
final int rotation = pdf.getPdfPageData().getRotation(currentPage);
//Handle how we auto fit the content to the page
if(fitToPage == FitToPage.AUTO && (pageW < pageH)){
if(pdf.getPDFWidth() 1){
top.lookup("#back").setDisable(false);
}else{
top.lookup("#back").setDisable(true);
}
if(currentPage < pdf.getPageCount()){
top.lookup("#forward").setDisable(false);
}else{
top.lookup("#forward").setDisable(true);
}
((ComboBox)top.lookup("#pages")).getSelectionModel().select(currentPage - 1);
}
private void goToPage(final int newPage){
final TransitionDirection direction ;
// For sliding Transitions
if(transitionType != TransitionType.Fade || transitionType != TransitionType.None){
direction = newPage > currentPage ? TransitionDirection.LEFT: TransitionDirection.RIGHT;
}else{
direction = TransitionDirection.NONE;
}
switch (transitionType){
case Fade:
startTransition( newPage, direction);
break;
case Scale:
startTransition( newPage, direction);
break;
case Rotate:
startTransition( newPage, direction);
break;
case CardStack:
startTransition( newPage, direction);
break;
default: //no transition
currentPage = newPage;
decodePage();
break;
}
}
private void startTransition(final int newPage,final TransitionDirection direction){
final Transition exitTransition = FXViewerTransitions.exitTransition(pdf, transitionType, direction);
if(exitTransition != null){
exitTransition.setOnFinished(new EventHandler() {
@Override public void handle(final ActionEvent t) {
currentPage = newPage;
Platform.runLater(new Runnable() {
@Override
public void run() {
decodePage();
}
});
TransitionDirection entryDirection = direction;
if(direction != TransitionDirection.NONE){
entryDirection = direction == TransitionDirection.LEFT ? TransitionDirection.RIGHT : TransitionDirection.LEFT;
}
final Transition entryTransition = FXViewerTransitions.entryTransition(pdf, transitionType, entryDirection);
entryTransition.play();
}
});
exitTransition.play();
}
}
/**
* @return the case sensitive full path and name of the PDF file
*/
public String getPDFfilename(){
return PDFfile;
}
private void adjustPagePosition(final Bounds nb){
// (new scrollbar width / 2) - (page width / 2)
double adjustment = ((nb.getWidth() / 2) - (group.getBoundsInLocal().getWidth() /2));
// Keep the group within the viewport of the scrollpane
if(adjustment < 0) {
adjustment = 0;
}
group.setTranslateX(adjustment);
}
// Set a space between the top toolbar and the page
private void setBorder() {
// Why it's easier to use a dropshadow for this is beyond me, but here it is...
final int rotation = pdf.getPdfPageData().getRotation(currentPage);
final double x = (rotation == 90 || rotation == 270) ? 40 : 0;
final double y = (rotation == 90 || rotation == 270) ? 0 : 40;
final DropShadow pdfBorder = new DropShadow(0, x,y, Color.TRANSPARENT);
pdf.setEffect(pdfBorder);
}
public void addExternalHandler(PluginHandler customPluginHandle){
this.customPluginHandle=customPluginHandle;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy