org.jpedal.display.GUIDisplay Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of OpenViewerFX Show documentation
Show all versions of OpenViewerFX Show documentation
Open Source (LGPL) JavaFX PDF Viewer for NetBeans plugin
/*
* ===========================================
* 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-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* GUIDisplay.java
* ---------------
*/
package org.jpedal.display;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import org.jpedal.external.Options;
import org.jpedal.external.RenderChangeListener;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.text.TextLines;
import org.jpedal.utils.ScalingFactory;
/**
* hold code generic to Swing or FX
*/
public class GUIDisplay implements Display {
protected int lastFormPage = -1, lastStart = -1, lastEnd = -1;
protected int pageUsedForTransform;
/**
* tracks indent so changing to continuous does not disturb display
*/
protected int lastIndent = -1;
protected double indent;
/**
* Normally null - user object to listen to paint events
*/
public RenderChangeListener customRenderChangeListener;
//rectangle onscreen
public int rx, ry, rw, rh;
protected DisplayOffsets displayOffsets;
public DecoderOptions options;
/**
* Flag if we should allow cursor to change
*/
public static boolean allowChangeCursor = true;
public final MultiDisplayOptions multiDisplayOptions = new MultiDisplayOptions();
//Animation enabled (currently just turnover in facing)
public static boolean default_turnoverOn = true; //can be altered by user
//Display the first page separately in Facing mode
public static boolean default_separateCover = true; //can be altered by user
public DynamicVectorRenderer currentDisplay;
public boolean isInitialised;
/**
* flag to switch back to unaccelerate screen if no enough memory for scaling
*/
public boolean overRideAcceleration;
/**
* render screen using hardware acceleration
*/
public boolean useAcceleration = true;
public boolean ignoreScalingForAcceleration;
public boolean accelerationAlwaysRedraw;
public PageOffsets currentOffset;
/**
* Holds the x,y,w,h of the current highlighted image, null if none
*/
private int[] highlightedImage;
protected GUIThumbnailPanel thumbnails;
/**
* Keep a record of cumulative offsets for SINGLE_PAGE mode
*/
public int[] pageOffsetH, pageOffsetW;
public boolean[] isRotated;
public int topW, topH;
public double cropX, cropY, cropW, cropH;
/**
* used to draw demo cross
*/
public int crx, cry, crw, crh;
/**
* local copies
*/
public int displayRotation, displayView = SINGLE_PAGE;
public int lastDisplayRotation;
public int insetW, insetH;
public float scaling;
public float lastScaling;
public int pageNumber;
/**
* any scaling factor being used to convert co-ords into correct values
* and to alter image size
*/
public float oldScaling = -1, oldRotation = -1;
public PdfPageData pageData;
// Affine transformation as a double array
protected double[] displayScalingDbl;
/**
* used internally by multiple pages
* scaling -1 to ignore, -2 to force reset
*/
@Override
public int getYCordForPage(final int page, final float scaling) {
if (scaling == -2 || (scaling != -1f && scaling != oldScaling)) {
oldScaling = scaling;
setPageOffsets(page);
}
return getYCordForPage(page);
}
@Override
public boolean getBoolean(final BoolValue option) {
switch (option) {
case SEPARATE_COVER:
return multiDisplayOptions.isSeparateCover();
case TURNOVER_ON:
return multiDisplayOptions.isTurnoverOn();
default:
throw new RuntimeException("GetBoolean called with unknown value " + option);
}
}
@Override
public void setBoolean(final BoolValue option, final boolean value) {
switch (option) {
case SEPARATE_COVER:
multiDisplayOptions.setSeparateCover(value);
return;
case TURNOVER_ON:
multiDisplayOptions.setTurnoverOn(value);
return;
default:
throw new RuntimeException("setBoolean called with unkown value " + value);
}
}
/**
* general method to pass in Objects - only takes RenderChangeListener at present
*
* @param type
* @param newHandler
*/
@Override
public void setObjectValue(final int type, final Object newHandler) {
//set value
switch (type) {
case Options.RenderChangeListener:
customRenderChangeListener = (RenderChangeListener) newHandler;
break;
default:
throw new RuntimeException("setObjectValue does not take value " + type);
}
}
/**
* @param displayView is of type int
* @return returns an int[] with 2 values ([0]width and [1]height)
* which we then use to create a Dimension object in current Swing code
*/
@Override
public int[] getPageSize(final int displayView) {
final int[] pageSize = new int[2]; //element 0 = width, element 1 = height
//height for facing pages
int biggestFacingHeight = 0;
int facingWidth = 0;
if ((displayView == FACING) && (multiDisplayOptions.getPageW() != null)) {
//get 2 facing page numbers
int p1;
final int p2;
if (multiDisplayOptions.isSeparateCover()) {
p1 = pageNumber;
if ((p1 & 1) == 1) {
p1--;
}
p2 = p1 + 1;
} else {
p1 = pageNumber;
if ((p1 & 1) == 0) {
p1--;
}
p2 = p1 + 1;
}
if (p1 == 0) {
biggestFacingHeight = multiDisplayOptions.getPageH(p2);
facingWidth = multiDisplayOptions.getPageW(p2) * 2;
} else {
biggestFacingHeight = multiDisplayOptions.getPageH(p1);
if (p2 < multiDisplayOptions.getPageH().length) {
if (biggestFacingHeight < multiDisplayOptions.getPageH(p2)) {
biggestFacingHeight = multiDisplayOptions.getPageH(p2);
}
facingWidth = multiDisplayOptions.getPageW(p1) + multiDisplayOptions.getPageW(p2);
} else {
facingWidth = multiDisplayOptions.getPageW(p1) * 2;
}
}
}
final int gaps = currentOffset.getGaps();
final int doubleGaps = currentOffset.getDoubleGaps();
switch (displayView) {
case FACING:
pageSize[0] = facingWidth + insetW + insetW;
pageSize[1] = biggestFacingHeight + insetH + insetH;
break;
case CONTINUOUS:
if ((displayRotation == 90) | (displayRotation == 270)) {
pageSize[0] = ((int) (currentOffset.getBiggestHeight() * scaling) + insetW + insetW);
pageSize[1] = pageData.getScaledTotalSingleWidth() + gaps + insetH + insetH;
} else {
pageSize[0] = ((int) (currentOffset.getBiggestWidth() * scaling) + insetW + insetW);
pageSize[1] = pageData.getScaledTotalSingleHeight() + gaps + insetH + insetH;
}
break;
case CONTINUOUS_FACING:
final int pageCount = pageData.getPageCount();
if ((displayRotation == 90) | (displayRotation == 270)) {
if (pageCount == 2) {
pageSize[0] = ((int) (currentOffset.getDoublePageHeight() * scaling) + insetW + insetW);
pageSize[1] = ((int) (currentOffset.getBiggestWidth() * scaling) + gaps + insetH + insetH);
} else {
pageSize[0] = ((int) (currentOffset.getDoublePageHeight() * scaling) + insetW + insetW);
pageSize[1] = pageData.getScaledTotalDoubleWidth() + doubleGaps + insetH + insetH;
}
} else {
if (pageCount == 2) {
pageSize[0] = ((int) (currentOffset.getDoublePageWidth() * scaling) + insetW + insetW);
pageSize[1] = ((int) (currentOffset.getBiggestHeight() * scaling) + gaps + insetH + insetH);
} else {
pageSize[0] = ((int) (currentOffset.getDoublePageWidth() * scaling) + insetW + insetW);
pageSize[1] = pageData.getScaledTotalDoubleHeight() + doubleGaps + insetH + insetH;
}
}
break;
}
return pageSize;
}
@Override
public void setup(final boolean useAcceleration, final PageOffsets currentOffset) {
this.useAcceleration = useAcceleration;
this.currentOffset = currentOffset;
overRideAcceleration = false;
}
@Override
public void setAcceleration(final boolean enable) {
useAcceleration = enable;
}
@Override
public void setAccelerationAlwaysRedraw(final boolean enable) {
accelerationAlwaysRedraw = enable;
}
public void setPageSize(final int pageNumber, final float scaling) {
/*
*handle clip - crop box values
*/
pageData.setScalingValue(scaling); //ensure aligned
topW = pageData.getScaledCropBoxWidth(pageNumber);
topH = pageData.getScaledCropBoxHeight(pageNumber);
final double mediaH = pageData.getScaledMediaBoxHeight(pageNumber);
cropX = pageData.getScaledCropBoxX(pageNumber);
cropY = pageData.getScaledCropBoxY(pageNumber);
cropW = topW;
cropH = topH;
/*
* actual clip values - for flipped page
*/
if (displayView == Display.SINGLE_PAGE) {
crx = (int) (insetW + cropX);
cry = (int) (insetH - cropY);
} else {
crx = insetW;
cry = insetH;
}
//amount needed to move cropped page into correct position
final int offsetY = (int) (mediaH - cropH);
if ((displayRotation == 90 || displayRotation == 270)) {
crw = (int) (cropH);
crh = (int) (cropW);
final int tmp = crx;
crx = cry;
cry = tmp;
crx += offsetY;
} else {
crw = (int) (cropW);
crh = (int) (cropH);
cry += offsetY;
}
}
@Override
public void decodeOtherPages(final int pageNumber, final int pageCount) {
}
@Override
public void disableScreen() {
isInitialised = false;
oldScaling = -1;
}
@Override
public float getOldScaling() {
return oldScaling;
}
@Override
public void refreshDisplay() {
throw new UnsupportedOperationException("refreshDisplay Not supported yet.");
}
@Override
public void flushPageCaches() {
}
@Override
public void init(final float scaling, final int displayRotation, int pageNumber, final DynamicVectorRenderer currentDisplay, final boolean isInit) {
if (pageNumber < 1) {
pageNumber = 1;
}
if (currentDisplay != null) {
this.currentDisplay = currentDisplay;
}
this.scaling = scaling;
this.displayRotation = displayRotation;
this.pageNumber = pageNumber;
this.insetW = options.getInsetW();
this.insetH = options.getInsetH();
//reset over-ride which may have been enabled
pageData.setScalingValue(scaling); //ensure aligned
if (isInit) {
setPageOffsets(this.pageNumber);
isInitialised = true;
}
lastScaling = scaling;
}
@Override
public void setThumbnailPanel(final GUIThumbnailPanel thumbnails) {
this.thumbnails = thumbnails;
}
@Override
public void stopGeneratingPage() {
//request any processes die
multiDisplayOptions.setIsGeneratingOtherPages(false);
multiDisplayOptions.waitToDieThred();
}
@Override
public int getYCordForPage(final int page) {
final int[] yReached = multiDisplayOptions.getyReached();
//int[] pageH=multiDisplayOptions.getPageH();
if (yReached != null) {
//Prevent Continous Facing mode from shifting forward a page
//when scaling from large to small
// if(displayView==Display.CONTINUOUS_FACING){
// return yReached[page] + insetH; // - pageH[page];
// }else{
return yReached[page] + insetH;
// }
} else {
return insetH;
}
}
public int getStartPage() {
return multiDisplayOptions.getStartViewPage();
}
public int getEndPage() {
return multiDisplayOptions.getEndViewPage();
}
@Override
public void setScaling(final float scaling) {
this.scaling = scaling;
if (pageData != null) {
pageData.setScalingValue(scaling);
}
}
@Override
public void setHighlightedImage(final int[] highlightedImage) {
this.highlightedImage = highlightedImage;
}
@Override
public int[] getHighlightedImage() {
return highlightedImage;
}
@Override
public void drawBorder() {
throw new UnsupportedOperationException("drawBorder Not supported yet.");
}
@Override
public int getXCordForPage(final int page) {
final int[] xReached = multiDisplayOptions.getxReached();
if (xReached != null) {
return xReached[page] + insetW;
} else {
return insetW;
}
}
/**
* workout offsets so we can draw pages
*/
@Override
public void setPageOffsets(final int pageNumber) {
final int pageCount = pageData.getPageCount();
multiDisplayOptions.resetValues(pageCount);
pageOffsetW = new int[pageCount + 1];
pageOffsetH = new int[pageCount + 1];
int heightCorrection;
int displayRotation;
isRotated = new boolean[pageCount + 1];
int gap = PageOffsets.pageGap; //set.pageGap*scaling);
if (multiDisplayOptions.isTurnoverOn() &&
pageCount != 2 &&
!pageData.hasMultipleSizes() &&
displayView == Display.FACING) {
gap = 0;
}
//Used to help allign first page is page 2 is cropped / rotated
int LmaxWidth = 0;
int LmaxHeight = 0;
int RmaxWidth = 0;
int RmaxHeight = 0;
final int[] pageW = multiDisplayOptions.getPageW();
final int[] pageH = multiDisplayOptions.getPageH();
/*work out page sizes - need to do it first as we can look ahead*/
for (int i = 1; i < pageCount + 1; i++) {
pageW[i] = pageData.getScaledCropBoxWidth(i);
pageH[i] = pageData.getScaledCropBoxHeight(i);
displayRotation = pageData.getRotation(i) + this.displayRotation;
if (displayRotation >= 360) {
displayRotation -= 360;
}
//swap if this page rotated and flag
if ((displayRotation == 90 || displayRotation == 270)) {
final int tmp = pageW[i];
pageW[i] = pageH[i];
pageH[i] = tmp;
isRotated[i] = true; //flag page as rotated
}
if ((i & 1) == 1) {
if (pageW[i] > RmaxWidth) {
RmaxWidth = pageW[i];
}
if (pageH[i] > RmaxHeight) {
RmaxHeight = pageH[i];
}
} else {
if (multiDisplayOptions.getPageW(i) > LmaxWidth) {
LmaxWidth = multiDisplayOptions.getPageW(i);
}
if (pageH[i] > LmaxHeight) {
LmaxHeight = pageH[i];
}
}
}
final int[] xReached = multiDisplayOptions.getxReached();
final int[] yReached = multiDisplayOptions.getyReached();
//loop through all pages and work out positions
for (int i = 1; i < pageCount + 1; i++) {
heightCorrection = 0;
if (((pageCount == 2) && (displayView == FACING || displayView == CONTINUOUS_FACING)) || (displayView == FACING && !multiDisplayOptions.isSeparateCover())) { //special case
//if only 2 pages display side by side
if ((i & 1) == 1) {
xReached[i] = 0;
yReached[i] = 0;
} else {
xReached[i] = xReached[i - 1] + pageW[i - 1] + gap;
yReached[i] = 0;
if (!(i == 2 && pageData.getRotation(1) == 270)) {
pageOffsetW[2] = (pageW[2] - pageW[1]) + pageOffsetW[1];
pageOffsetH[2] = (pageH[2] - pageH[1]) + pageOffsetH[1];
}
}
} else if (i == 1) { //first page is special case
//First page should be on the left so indent
if (displayView == CONTINUOUS) {
xReached[1] = 0;
yReached[1] = 0;
pageOffsetW[1] = 0;
pageOffsetH[1] = 0;
pageOffsetW[0] = gap; //put the gap values in the empty entry in the offset array. A bit bodgy!
pageOffsetH[0] = gap;
} else if (displayView == CONTINUOUS_FACING) {
pageOffsetW[0] = gap; //put the gap values in the empty entry in the offset array. A bit bodgy!
pageOffsetH[0] = gap;
pageOffsetW[1] = 0;
pageOffsetH[1] = 0;
xReached[1] = LmaxWidth + gap;
yReached[1] = 0;
} else if (displayView == FACING) {
xReached[1] = pageW[1] + gap;
yReached[1] = 0;
}
} else {
//Calculate position for all other pages / cases
if ((displayView == CONTINUOUS_FACING)) {
if (!(i >= 2 &&
(((pageData.getRotation(i) == 270 || pageData.getRotation(i) == 90) &&
(pageData.getRotation(i - 1) != 270 || pageData.getRotation(i - 1) != 90))
|| ((pageData.getRotation(i - 1) == 270 || pageData.getRotation(i - 1) == 90) &&
(pageData.getRotation(i) != 270 || pageData.getRotation(i) != 90))))) {
pageOffsetW[i] = (pageW[i] - pageW[i - 1]) + pageOffsetW[i - 1];
pageOffsetH[i] = (pageH[i] - pageH[i - 1]) + pageOffsetH[i - 1];
}
//Left Pages
if ((i & 1) == 0) {
//Last Page rotated so correct height
if (i < pageCount) {
heightCorrection = (pageH[i + 1] - pageH[i]) / 2;
}
if (heightCorrection < 0) {
heightCorrection = 0; //-heightCorrection;
}
if (i > 3) {
final int temp = (pageH[i - 2] - pageH[i - 1]) / 2;
if (temp > 0) {
heightCorrection += temp;
}
}
yReached[i] = (yReached[i - 1] + pageH[i - 1] + gap) + heightCorrection;
} else { //Right Pages
//Last Page rotated so correct height
heightCorrection = (pageH[i - 1] - pageH[i]) / 2;
yReached[i] = (yReached[i - 1]) + heightCorrection;
}
if ((i & 1) == 0) { //Indent Left pages by diff between maxWidth and pageW (will only indent unrotated)
xReached[i] += (LmaxWidth - pageW[i]);
} else { //Place Right Pages with a gap (This keeps pages centered)
xReached[i] = xReached[i - 1] + pageW[i - 1] + gap;
}
} else if (displayView == CONTINUOUS) {
//Place page below last with gap
yReached[i] = (yReached[i - 1] + pageH[i - 1] + gap);
if (!(i >= 2 &&
(((pageData.getRotation(i) == 270 || pageData.getRotation(i) == 90) &&
(pageData.getRotation(i - 1) != 270 || pageData.getRotation(i - 1) != 90))
|| ((pageData.getRotation(i - 1) == 270 || pageData.getRotation(i - 1) == 90) &&
(pageData.getRotation(i) != 270 || pageData.getRotation(i) != 90))))) {
pageOffsetW[i] = (pageW[i] - pageW[i - 1]) + pageOffsetW[i - 1];
pageOffsetH[i] = (pageH[i] - pageH[i - 1]) + pageOffsetH[i - 1];
}
} else if ((displayView == FACING)) {
if ((i & 1) == 1) { //If right page, place on right with gap
xReached[i] = (xReached[i - 1] + pageW[i - 1] + gap);
if (pageH[i] < pageH[i - 1])//Drop page down to keep pages centred
{
yReached[i] += (((pageH[i - 1] - pageH[i]) / 2));
}
} else { //If left page, indent by diff of max and current page
xReached[i] = 0;
if (i < pageCount && (pageH[i] < pageH[i + 1])) {
//Drop page down to keep pages centered
yReached[i] += ((pageH[i + 1] - pageH[i]) / 2);
}
}
}
}
}
}
@Override
public void dispose() {
currentOffset = null;
multiDisplayOptions.setPageValuesToNull();
this.isRotated = null;
}
@Override
public int[] getCursorBoxOnScreenAsArray() {
throw new UnsupportedOperationException("getCursorBoxOnScreenAsArray Not supported yet.");
}
@Override
public double getIndent() {
return indent;
}
@Override
public void forceRedraw() {
lastFormPage = -1;
lastEnd = -1;
lastStart = -1;
}
/**
* initialise panel and set size to display during updates and update the AffineTransform to new values
*/
@Override
public void setPageRotation(int newRotation) {
//assume unrotated for multiple views and rotate on a page basis
if (displayView != Display.SINGLE_PAGE) {
newRotation = 0;
}
pageUsedForTransform = pageNumber;
if (displayView != Display.SINGLE_PAGE && displayView != Display.FACING) {
displayScalingDbl = ScalingFactory.getScalingForImage(1, 0, scaling, pageData); //(int)(pageData.getCropBoxWidth(pageNumber)*scaling),(int)(pageData.getCropBoxHeight(pageNumber)*scaling),
} else {
displayScalingDbl = ScalingFactory.getScalingForImage(pageNumber, newRotation, scaling, pageData); //(int)(pageData.getCropBoxWidth(pageNumber)*scaling),(int)(pageData.getCropBoxHeight(pageNumber)*scaling),
}
final int insetW = options.getInsetW();
final int insetH = options.getInsetH();
// Affine transformations
switch (newRotation) {
case 90:
displayScalingDbl[4] += ((insetW / scaling) * displayScalingDbl[1]);
displayScalingDbl[5] += ((insetH / scaling) * displayScalingDbl[2]);
break;
case 270:
displayScalingDbl[4] += ((-insetW / scaling) * displayScalingDbl[1]);
displayScalingDbl[5] += ((-insetH / scaling) * displayScalingDbl[2]);
break;
case 180:
displayScalingDbl[4] += ((-insetW / scaling) * displayScalingDbl[0]);
displayScalingDbl[5] += ((insetH / scaling) * displayScalingDbl[3]);
break;
default:
displayScalingDbl[4] += ((insetW / scaling) * displayScalingDbl[0]);
displayScalingDbl[5] += ((-insetH / scaling) * displayScalingDbl[3]);
break;
}
//force redraw if screen being cached
refreshDisplay();
}
@Override
public void resetViewableArea() {
throw new UnsupportedOperationException("resetViewableArea Not supported yet.");
}
@Override
public void paintPage(final Graphics2D g2, final AcroRenderer formRenderer, final TextLines textLines) {
throw new UnsupportedOperationException("paintPage not supported yet.");
}
@Override
public void updateCursorBoxOnScreen(final int[] newOutlineRectangle, final int outlineColor, final int pageNumber, final int x_size, final int y_size) {
throw new UnsupportedOperationException("updateCursorBoxOnScreen Not supported yet.");
}
@Override
public void drawCursor(final Graphics g, final float scaling) {
throw new UnsupportedOperationException("drawCursor Not supported yet.");
}
@Override
public void drawFacing(final Rectangle visibleRect) {
throw new UnsupportedOperationException("drawFacing Not supported yet.");
}
public void setCurrentDisplay(final DynamicVectorRenderer pageView) {
this.currentDisplay = pageView;
}
public int getDisplayRotation() {
return displayRotation;
}
public int getRx() {
return rx;
}
public int getRy() {
return ry;
}
public int getRw() {
return rw;
}
public int getRh() {
return rh;
}
public int getInsetW() {
return insetW;
}
public int getInsetH() {
return insetH;
}
@Override
public Rectangle getDisplayedRectangle() {
throw new UnsupportedOperationException("getDisplayedRectangle Not supported yet.");
}
public Rectangle getDisplayedRectangle(final boolean isShowing, final Rectangle userAnnot) {
//get raw rectangle
rx = userAnnot.x;
ry = userAnnot.y;
rw = userAnnot.width;
rh = userAnnot.height;
//Best way I found to catch if pdf decoder is being used but never displayed
if (!isShowing && (rw == 0 || rh == 0)) {
rx = 0;
ry = 0;
rw = pageData.getScaledCropBoxWidth(pageNumber);
rh = pageData.getScaledCropBoxHeight(pageNumber);
if (pageData.getRotation(pageNumber) % 180 != 0) {
rh = pageData.getScaledCropBoxWidth(pageNumber);
rw = pageData.getScaledCropBoxHeight(pageNumber);
}
}
return userAnnot;
}
}