All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.examples.viewer.gui.javafx.JavaFXThumbnailPanel 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


 *
 * ---------------
 * JavaFXThumbnailPanel.java
 * ---------------
 */

package org.jpedal.examples.viewer.gui.javafx;

import java.awt.Font;
import java.awt.image.BufferedImage;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
import org.jpedal.PdfDecoderInt;
import org.jpedal.ThumbnailDecoder;
import org.jpedal.display.GUIThumbnailPanel;
import org.jpedal.examples.viewer.Values;
import org.jpedal.objects.PdfPageData;

/**
 * Used in GUI example code.
 * 
adds thumbnail capabilities to viewer, *
shows pages as thumbnails within this panel, *
So this panel can be added to the viewer * */ public class JavaFXThumbnailPanel extends Tab implements GUIThumbnailPanel { private static final boolean debugThumbnails = false; /**can switch on or off thumbnails*/ private boolean showThumbnailsdefault=true; private boolean generateOtherVisibleThumbnails; private boolean showThumbnails=showThumbnailsdefault; private final PdfDecoderInt decode_pdf; private final ThumbnailDecoder thumbDecoder; /**width and height for thumbnails*/ private static final int thumbH=100,thumbW=70; private Task worker; private boolean drawing; private static final boolean debug = true; private boolean interrupt; private Button[] pageButton; private final VBox content; private final ScrollPane scroll; private int lastPage; private boolean[] isLandscape; private int[] pageHeight; private BufferedImage[] images; private boolean[] buttonDrawn; private final ThumbPainter painter; private final BorderListener border; private final Border selectedBorder; private final Border hoverBorder; private final Border emptyBorder; public JavaFXThumbnailPanel(final PdfDecoderInt decode_pdf) { border = new BorderListener(); painter = new ThumbPainter(); this.decode_pdf = decode_pdf; thumbDecoder = new ThumbnailDecoder(decode_pdf); content = new VBox(); scroll = new ScrollPane(content); content.setAlignment(Pos.CENTER); scroll.viewportBoundsProperty().addListener(new ChangeListener() { @Override public void changed(final ObservableValue ov, final Bounds oldb, final Bounds newb) { // binding the pref width doesn't seem to work in this context so using a listener content.setPrefWidth(newb.getWidth()); if(drawing) { terminateDrawing(); } decode_pdf.waitForDecodingToFinish(); if(decode_pdf.isOpen()) { drawThumbnails(); } } }); selectedBorder = new Border(new BorderStroke(Color.RED, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))); hoverBorder = new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))); emptyBorder = new Border(new BorderStroke(Color.TRANSPARENT, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(2))); this.setContent(scroll); } @Override public boolean isShownOnscreen() { return showThumbnails; } @Override public void terminateDrawing() { generateOtherVisibleThumbnails = false; if(drawing){ interrupt = true; // Removed the loop that was in Swing // This is due to deadlocking with the FX application Thread // So now what happens is we call interupt and allow any thumbnails // being decoded to finish. } } @Override public void setIsDisplayedOnscreen(final boolean b) { showThumbnails = b; } @Override public Object[] getButtons() { return pageButton; } @Override public void addComponentListener() { scroll.vvalueProperty().addListener(painter); } @Override public synchronized void generateOtherVisibleThumbnails(final int currentPage) { //flag to show drawing which terminate can reset generateOtherVisibleThumbnails=true; //stop multiple calls if(currentPage==-1 || currentPage==lastPage || pageButton==null) { return; } final int count = decode_pdf.getPageCount(); for(int i = 0; i < count; i++){ if(!generateOtherVisibleThumbnails) { return; } pageButton[i].setBorder(emptyBorder); } if((currentPage-1 < pageButton.length)&& (count > 1 && currentPage > 0)){ pageButton[currentPage-1].setBorder(selectedBorder); } final double pos = ((scroll.getVmax() / count) * (currentPage-1)); scroll.setVvalue( pos); if(!generateOtherVisibleThumbnails) { return; } //commonValues.setProcessing(false); if(drawing) { terminateDrawing(); } if(!generateOtherVisibleThumbnails) { return; } drawThumbnails(); } @Override public void setupThumbnails(final int pages, final Font textFont, final PdfPageData pageData) { lastPage = -1; content.getChildren().clear(); final Image blankPortrait = createBlankThumbnail(thumbW, thumbH); final Image blankLandscape = createBlankThumbnail(thumbH, thumbW); isLandscape = new boolean[pages]; pageHeight = new int[pages]; pageButton = new Button[pages]; images = new BufferedImage[pages]; buttonDrawn = new boolean[pages]; for(int i = 0; i < pages; i++){ final int page=i+1; String pageLabel = decode_pdf.getIO().convertPageNumberToLabel(page); if(pageLabel==null){ pageLabel = String.valueOf(page); } //create blank image with correct orientation final int ph;//pw final int cropWidth=pageData.getCropBoxWidth(page); final int cropHeight=pageData.getCropBoxHeight(page); final int rotation=pageData.getRotation(page); //Image usedLandscape; final Image usedPortrait; if((rotation==0)|(rotation==180)){ ph=(pageData.getMediaBoxHeight(page)); //pw=(pageData.getMediaBoxWidth(page));//%% //usedLandscape = blankLandscape; usedPortrait = blankPortrait; }else{ ph=(pageData.getMediaBoxWidth(page)); //pw=(pageData.getMediaBoxHeight(page));//%% //usedLandscape = blankPortrait; usedPortrait = blankLandscape; } if(cropWidth > cropHeight){ // VBox contents = new VBox(new ImageView(usedLandscape), new Label(message + ' ' + page)); final Button imageButton = new Button(pageLabel, new ImageView(usedPortrait)); pageButton[i] = imageButton; isLandscape[i] = true; }else{ // VBox contents = new VBox(new ImageView(usedPortrait), new Label()); final Button imageButton = new Button(pageLabel, new ImageView(usedPortrait)); pageButton[i] = imageButton; isLandscape[i] = false; } pageHeight[i] = ph; pageButton[i].setContentDisplay(ContentDisplay.TOP); // Remove button styles pageButton[i].setStyle("-fx-padding:0;-fx-background-color:transparent;"); if(i == 0 && pages > 1){ pageButton[0].setBorder(selectedBorder); }else{ pageButton[i].setBorder(emptyBorder); } pageButton[i].prefWidthProperty().bind(content.prefWidthProperty()); content.getChildren().add(pageButton[i]); pageButton[i].setOnMouseEntered(border); pageButton[i].setOnMouseExited(border); } } @Override public void resetToDefault() { showThumbnails = showThumbnailsdefault; } @Override public void removeAllListeners() { scroll.vvalueProperty().removeListener(painter); for(final Button btn : pageButton){ btn.setOnMouseExited(null); btn.setOnMouseEntered(null); btn.setOnAction(null); } } @Override public void setThumbnailsEnabled(final boolean newValue) { showThumbnailsdefault=newValue; showThumbnails=newValue; } @Override public void dispose() { } @Override public synchronized BufferedImage getImage(int page){ //actually stored starting 0 not 1 so adjust page--; if(images==null || images[page]==null){ if(page>-1){ int h = thumbH; if (isLandscape[page]) { h = thumbW; } final BufferedImage image = thumbDecoder.getPageAsThumbnail(page + 1, h); images[page]=image; return image; }else { return null; } }else { return images[page]; } } @Override public void drawThumbnails() { if(!isSelected()) { return; } //do not generate if still loading Linearized if(decode_pdf.isLoadingLinearizedPDF()) { return; } // Allow for re-entry if(drawing) { this.terminateDrawing(); } worker = new Task() { @Override protected Void call() throws Exception{ if(buttonDrawn != null && pageButton != null){ drawing = true; try{ final double minY = (scroll.getVvalue() * content.getHeight() - (scroll.getHeight() * scroll.getVvalue())); final Rectangle visible = new Rectangle(0, minY, scroll.getWidth(), scroll.getHeight()); final int pages = decode_pdf.getPageCount(); for(int i = 0; i < pages; i++){ decode_pdf.waitForDecodingToFinish(); // i > pageButton.length fixes exception thrown when opening a new PDF if(interrupt || i > pageButton.length){ i=pages; } else if (!buttonDrawn[i] && pageButton[i] != null && visible.intersects(pageButton[i].getBoundsInParent())){ int h = thumbH; if(isLandscape[i]) { h = thumbW; } final BufferedImage page = thumbDecoder.getPageAsThumbnail(i+1, h); if(!interrupt) { createThumbnail(page, i); } } } }catch(final Exception e){ e.printStackTrace(); } } //always reset flag so we can interupt interrupt=false; drawing=false; return null; } }; new Thread(worker, "Thumbnail Thread").start(); } /** * create a blank tile with a cross to use as a thumbnail for unloaded page */ private static Image createBlankThumbnail(final int w, final int h) { final Canvas canvas = new Canvas(w,h); final GraphicsContext gc = canvas.getGraphicsContext2D(); gc.setFill(Color.WHITE); gc.fillRect(0.2, 0.2, w-.5, h-.5); gc.setFill(Color.BLACK); gc.strokeRect(0.2, 0.2, w-.5, h-.5); gc.strokeLine(0, 0, w, h); gc.strokeLine(0, h, w, 0); return canvas.snapshot(null, null); } private void createThumbnail(final BufferedImage page, final int i){ if(page!=null){ final Image image = SwingFXUtils.toFXImage(page, null); if(Platform.isFxApplicationThread()){ ((ImageView)pageButton[i].getGraphic()).setImage(image); buttonDrawn[i] = true; images[i] = page; }else{ Platform.runLater(new Runnable() { @Override public void run() { ((ImageView)pageButton[i].getGraphic()).setImage(image); buttonDrawn[i] = true; images[i] = page; } }); } } } private class ThumbPainter implements ChangeListener{ private boolean requestMade; private final Timeline trapMultipleMovements = new Timeline(new KeyFrame(Duration.millis(250), new EventHandler() { @Override public void handle(final ActionEvent event) { if(!requestMade){ requestMade = true; if(!Values.isProcessing){ if(drawing) { terminateDrawing(); } requestMade = false; drawThumbnails(); } } } })); @Override public void changed(final ObservableValue observable, final Number oldValue, final Number newValue) { if(trapMultipleMovements.getStatus() == Animation.Status.RUNNING) { trapMultipleMovements.stop(); } trapMultipleMovements.setCycleCount(1); trapMultipleMovements.playFromStart(); } } private class BorderListener implements EventHandler{ @Override public void handle(final MouseEvent event) { final Button source = (Button)event.getSource(); final boolean isSelected = source.getBorder().equals(selectedBorder); if(event.getEventType().equals(MouseEvent.MOUSE_ENTERED) && !isSelected){ source.setBorder(hoverBorder); }else if(event.getEventType().equals(MouseEvent.MOUSE_EXITED) && !isSelected){ source.setBorder(emptyBorder); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy