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

org.jpedal.SingleDisplay Maven / Gradle / Ivy

The newest version!
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     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


 *
 * ---------------
 * SingleDisplay.java
 * ---------------
 */
package org.jpedal;

//
//
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

import javax.swing.border.Border;

import org.jpedal.examples.viewer.gui.generic.GUIThumbnailPanel;
import org.jpedal.external.Options;
import org.jpedal.external.RenderChangeListener;
import org.jpedal.objects.PdfPageData;
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.repositories.Vector_Int;
//
//

public class SingleDisplay implements Display {

	// flag to track if page decoded twice
	// private int lastPageDecoded = -1;

	/**
	 * Flag if we should allow cursor to change
	 */
	public static boolean allowChangeCursor = true;

	// Animation enabled (currently just turnover in facing)
	public static boolean default_turnoverOn = true;// can be altered by user
	public boolean turnoverOn = default_turnoverOn;

	// Display the first page separately in Facing mode
	public static boolean default_separateCover = true;// can be altered by user
	public boolean separateCover = default_separateCover;

	/** Holds the x,y,w,h of the current highlighted image, null if none */
	private int[] highlightedImage = null;

	BufferedImage previewImage = null;

	String previewText;

	// used to flag we can decode multiple pages
	boolean isGeneratingOtherPages = false;

	// facing mode drag pages
	BufferedImage[] facingDragCachedImages = new BufferedImage[4];
	BufferedImage facingDragTempLeftImg, facingDragTempRightImg;
	int facingDragTempLeftNo, facingDragTempRightNo;

	protected Map keyToFilename = new HashMap();

	Rectangle userAnnot = null;

	AffineTransform rawAf;
	Shape rawClip;

	DynamicVectorRenderer currentDisplay;

	// flag shows if running
	boolean running = false;

	protected PageOffsets currentOffset;

	/** Normally null - user object to listen to paint events */
	RenderChangeListener customRenderChangeListener;

	/** used for creating the image size of volatileImage */
	protected int volatileWidth, volatileHeight;

	int startViewPage = 0;
	int endViewPage = 0;

	Map pagesDrawn = new HashMap();

	Map cachedPageViews = new WeakHashMap();

	Map currentPageViews = new HashMap();

	boolean screenNeedsRedrawing;

	PdfDecoder pdf;

	protected Vector_Int offsets = new Vector_Int(3);
	protected Map newPages = new HashMap();
	protected int currentXOffset = 0, additionalPageCount = 0;

	/**
	 * any scaling factor being used to convert co-ords into correct values and to alter image size
	 */
	float oldScaling = -1, oldRotation = -1, oldVolatileWidth = -1, oldVolatileHeight = -1;

	/** centering on page */
	int indent = 0;

	/** used to draw pages offset if not in SINGLE_PAGE mode */
	int[] xReached, yReached, pageW, pageH;

	/** Keep a record of cumalative offsets for SINGLE_PAGE mode */
	protected int[] pageOffsetH, pageOffsetW;

	boolean[] isRotated;

	/** optimise page redraws */
	Map accleratedPagesAlreadyDrawn = new HashMap();

	/** flag to switch back to unaccelerate screen if no enough memory for scaling */
	boolean overRideAcceleration = false;

	// to remind me to add feature back
	private boolean message = false;

	/** local copies */
	int displayRotation, displayView = SINGLE_PAGE;
	int lastDisplayRotation = 0;

	int insetW, insetH;

	float scaling;
	float lastScaling;

	int pageNumber;
	int pageCount = 0;

	PdfPageData pageData = null;

	/** flag to optimse calculations */
	// private int lastPageChecked=-1,lastState=-1;

	Graphics2D g2;

	Map areas;

	// stop multiple repaints
	private int lastAreasPainted = -1;

	/** render screen using hardware acceleration */
	boolean useAcceleration = true;

	boolean isInitialised;

	// rectangle onscreen
	int rx = 0, ry = 0, rw = 0, rh = 0;

	/** used to draw demo cross */
	private int crx, cry, crw, crh;

	AffineTransform current2 = null;
	Shape currentClip = null;

	protected Border myBorder;

	public SingleDisplay(int pageNumber, int pageCount, DynamicVectorRenderer currentDisplay) {

		if (pageNumber < 1) pageNumber = 1;

		this.pageNumber = pageNumber;
		this.pageCount = pageCount;
		this.currentDisplay = currentDisplay;
	}

	public SingleDisplay(PdfDecoder pdf) {
		this.pdf = pdf;
	}

	public static int CURRENT_BORDER_STYLE = 1;

	public static void setBorderStyle(int style) {
		CURRENT_BORDER_STYLE = style;
	}

	public static int getBorderStyle() {
		return CURRENT_BORDER_STYLE;
	}

	/**
	 * general method to pass in Objects - only takes RenderChangeListener at present
	 * 
	 * @param type
	 * @param newHandler
	 */
	@Override
	public void setObjectValue(int type, Object newHandler) {

		// set value
		switch (type) {
			case Options.RenderChangeListener:
				this.customRenderChangeListener = (RenderChangeListener) newHandler;
				break;

			default:
				throw new RuntimeException("setObjectValue does not take value " + type);
		}
	}

	/**
	 * used by Storypad to display split spreads not aprt of API
	 */
	@Override
	public void clearAdditionalPages() {

		this.offsets.clear();
		this.newPages.clear();
		this.currentXOffset = 0;
		this.additionalPageCount = 0;
	}

	/**
	 * used by Storypad to display split spreads not aprt of API
	 */
	@Override
	public void addAdditionalPage(DynamicVectorRenderer dynamicRenderer, int pageWidth, int origPageWidth) {

		// store
		this.offsets.addElement(this.currentXOffset + origPageWidth);
		this.newPages.put(this.currentXOffset + origPageWidth, dynamicRenderer);
		this.additionalPageCount++;

		// work out new offset
		this.currentXOffset = this.currentXOffset + pageWidth;

		// force redraw
		this.oldScaling = -this.oldScaling;
		this.refreshDisplay();
	}

	VolatileImage backBuffer = null;

	@Override
	public void dispose() {

		if (this.backBuffer != null) this.backBuffer.flush();

		this.keyToFilename.clear();

		this.currentOffset = null;
		this.areas = null;
		this.pageH = null;
		this.pageW = null;
		this.cachedPageViews = null;
		this.isRotated = null;

		this.backBuffer = null;

		this.accleratedPagesAlreadyDrawn = null;
	}

	protected void createBackBuffer() {

		final boolean debugBackBuffer = false;

		if (debugLayout) System.out.println("createBackBuffer");

		if (debugBackBuffer) System.out.println("Flushing back buffer");

		if (this.backBuffer != null) {
			this.backBuffer.flush();
			this.backBuffer = null;
		}

		int width = 0, height = 0;

		if (debugBackBuffer) System.out.println("Setting height and width");

		if (this.displayView == SINGLE_PAGE) {
			if ((this.displayRotation == 90) | (this.displayRotation == 270)) {
				width = (this.volatileHeight + this.currentXOffset);
				height = this.volatileWidth;
			}
			else {
				width = this.volatileWidth + this.currentXOffset;
				height = this.volatileHeight;
			}
		}
		else
			if (this.currentOffset != null) {

				if (debugBackBuffer) System.out.println("Also setting offset");

				// height for facing pages
				int biggestFacingHeight = 0;

				if ((this.displayView == FACING) && (this.pageW != null)) {
					// get 2 facing page numbers
					int p1, p2;
					if (this.separateCover) {
						p1 = this.pageNumber;
						if ((p1 & 1) == 1) p1--;
						p2 = p1 + 1;
					}
					else {
						p1 = this.pageNumber;
						if ((p1 & 1) == 0) p1--;
						p2 = p1 + 1;
					}

					/*
					 * if((displayRotation==90)|(displayRotation==270)){ biggestFacingHeight=pageH[p1]; if(p2 this.pageCount || (this.pageNumber == 1 && this.pageCount != 2)) {
							secondW = firstW;
						}
						else {
							if ((this.displayRotation + this.pdf.getPdfPageData().getRotation(this.pageNumber + 1)) % 180 == 90) secondW = this.pdf
									.getPdfPageData().getCropBoxHeight(this.pageNumber + 1);
							else secondW = this.pdf.getPdfPageData().getCropBoxWidth(this.pageNumber + 1);
						}

						// get total width
						int totalW = firstW + secondW;

						width = (int) (totalW * this.scaling) + PageOffsets.pageGap;
						height = (biggestFacingHeight); // NOTE scaled already!
						break;

					case CONTINUOUS:

						if ((this.displayRotation == 90) | (this.displayRotation == 270)) {
							width = (int) (this.currentOffset.biggestHeight * this.scaling);
							height = (int) ((this.currentOffset.totalSingleWidth) * this.scaling) + gaps + this.insetH;
						}
						else {
							width = (int) (this.currentOffset.biggestWidth * this.scaling);
							height = (int) ((this.currentOffset.totalSingleHeight) * this.scaling) + gaps + this.insetH;
						}
						break;

					case CONTINUOUS_FACING:

						if ((this.displayRotation == 90) | (this.displayRotation == 270)) {
							width = (int) ((this.currentOffset.doublePageHeight) * this.scaling) + (this.insetW * 2) + doubleGaps;
							height = (int) ((this.currentOffset.totalDoubleWidth) * this.scaling) + doubleGaps + this.insetH;
						}
						else {
							width = (int) ((this.currentOffset.doublePageWidth) * this.scaling) + (this.insetW * 2);
							height = (int) ((this.currentOffset.totalDoubleHeight) * this.scaling) + doubleGaps + this.insetH;
						}
						break;

				}
			}

		try {
			if (debugBackBuffer) System.out.println("Handle Huge pages if any");
			// avoid huge pages as VERY slow
			if (height > 15000) {
				this.volatileHeight = 0;
				height = 0;
				this.overRideAcceleration = true;
			}
			if (debugBackBuffer) System.out.println("Create back buffer");
			if ((width > 0) && (height > 0)) {
				this.backBuffer = this.pdf.createVolatileImage(width, height);
				this.oldVolatileWidth = this.volatileWidth;
				this.oldVolatileHeight = this.volatileHeight;

				Graphics2D gg = (Graphics2D) this.backBuffer.getGraphics();
				gg.setPaint(this.pdf.getBackground());
				gg.fillRect(0, 0, width, height);
			}

		}
		catch (Error e) { // switch off if not enough memory
			if (debugBackBuffer) System.out.println("Acceleration issue found " + e.getMessage());
			this.overRideAcceleration = true;
			this.backBuffer = null;
		}
		catch (Exception e) {
			if (debugBackBuffer) System.out.println("Issue found in display mode");
			e.printStackTrace();
			if (LogWriter.isOutput()) LogWriter.writeLog("Exception " + e + " is display mode");
		}

		if (debugBackBuffer) System.out.println("Painting optimised");
		// don't redraw
		this.currentDisplay.setOptimsePainting(true);
	}

	@Override
	public boolean isAccelerated() {

		if (!this.useAcceleration || this.overRideAcceleration) {
			return false;
		}
		else return true;
	}

	@Override
	public void resetCachedValues() {

		// rest page views
		// lastPageChecked = -1;
		// lastState = -1;
	}

	@Override
	public void stopGeneratingPage() {

		// request any processes die
		this.isGeneratingOtherPages = false;

		// wait to die
		while (this.running) {
			// System.out.println("Waiting to die");
			try {
				Thread.sleep(100);
			}
			catch (InterruptedException e) {
				// tell user and log
				if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
			}
		}
	}

	@Override
	public void disableScreen() {
		this.isInitialised = false;

		this.oldScaling = -1;
	}

	boolean testAcceleratedRendering() {

		boolean canDrawAccelerated = false;

		// force redraw if page rescaled
		if ((this.oldScaling != this.scaling) || (this.oldRotation != this.displayRotation) || (this.oldVolatileWidth != this.volatileWidth)
				|| (this.oldVolatileHeight != this.volatileHeight)) {
			this.backBuffer = null;
			this.overRideAcceleration = false;

		}

		if (DynamicVectorRenderer.debugPaint) System.err.println("acceleration called " + this.backBuffer);

		if (!this.overRideAcceleration && (this.backBuffer == null)) {// ||(screenNeedsRedrawing)){

			createBackBuffer();
			this.accleratedPagesAlreadyDrawn.clear();

		}

		if (this.backBuffer != null) {
			do {
				// First, we validate the back buffer
				int valCode = VolatileImage.IMAGE_INCOMPATIBLE;
				if (this.backBuffer != null) valCode = this.backBuffer.validate(this.pdf.getGraphicsConfiguration());

				if (valCode == VolatileImage.IMAGE_RESTORED) {
					// This case is just here for illustration
					// purposes. Since we are
					// recreating the contents of the back buffer
					// every time through this loop, we actually
					// do not need to do anything here to recreate
					// the contents. If our VImage was an image that
					// we were going to be copying _from_, then we
					// would need to restore the contents at this point
				}
				else
					if (valCode == VolatileImage.IMAGE_INCOMPATIBLE) {
						if (!this.overRideAcceleration) createBackBuffer();

					}

				// Now we've handled validation, get on with the rendering
				if ((this.backBuffer != null)) {

					canDrawAccelerated = true;

				}

				// Now we are done; or are we? Check contentsLost() and loop as necessary
			}
			while ((this.backBuffer == null) || (this.backBuffer.contentsLost()));
		}
		return canDrawAccelerated;
	}

	@Override
	public Dimension getPageSize(int displayView) {

		Dimension pageSize = null;

		// height for facing pages
		int biggestFacingHeight = 0;
		int facingWidth = 0;

		if ((displayView == FACING) && (this.pageW != null)) {
			// get 2 facing page numbers
			int p1, p2;
			if (this.separateCover) {
				p1 = this.pageNumber;
				if ((p1 & 1) == 1) p1--;
				p2 = p1 + 1;
			}
			else {
				p1 = this.pageNumber;
				if ((p1 & 1) == 0) p1--;
				p2 = p1 + 1;
			}

			if (p1 == 0) {
				biggestFacingHeight = this.pageH[p2];
				facingWidth = this.pageW[p2] * 2;
			}
			else {
				biggestFacingHeight = this.pageH[p1];
				if (p2 < this.pageH.length) {
					if (biggestFacingHeight < this.pageH[p2]) biggestFacingHeight = this.pageH[p2];

					facingWidth = this.pageW[p1] + this.pageW[p2];
				}
				else {
					facingWidth = this.pageW[p1] * 2;
				}
			}
		}

		int gaps = this.currentOffset.gaps;
		int doubleGaps = this.currentOffset.doubleGaps;

		switch (displayView) {

			case FACING:

				pageSize = new Dimension((facingWidth + this.insetW + this.insetW), (biggestFacingHeight + this.insetH + this.insetH));

				break;

			case CONTINUOUS:

				if ((this.displayRotation == 90) | (this.displayRotation == 270)) pageSize = new Dimension(
						(int) ((this.currentOffset.biggestHeight * this.scaling) + this.insetW + this.insetW),
						(int) ((this.currentOffset.totalSingleWidth * this.scaling) + gaps + this.insetH + this.insetH));
				else pageSize = new Dimension((int) ((this.currentOffset.biggestWidth * this.scaling) + this.insetW + this.insetW),
						(int) ((this.currentOffset.totalSingleHeight * this.scaling) + gaps + this.insetH + this.insetH));

				break;

			case CONTINUOUS_FACING:

				if ((this.displayRotation == 90) | (this.displayRotation == 270)) {
					if (this.pageCount == 2) pageSize = new Dimension(
							(int) ((this.currentOffset.doublePageHeight * this.scaling) + this.insetW + this.insetW),
							(int) ((this.currentOffset.biggestWidth * this.scaling) + gaps + this.insetH + this.insetH));
					else pageSize = new Dimension((int) ((this.currentOffset.doublePageHeight * this.scaling) + this.insetW + this.insetW),
							(int) ((this.currentOffset.totalDoubleWidth * this.scaling) + doubleGaps + this.insetH + this.insetH));
				}
				else {
					if (this.pageCount == 2) pageSize = new Dimension(
							(int) ((this.currentOffset.doublePageWidth * this.scaling) + this.insetW + this.insetW),
							(int) ((this.currentOffset.biggestHeight * this.scaling) + gaps + this.insetH + this.insetH));
					else pageSize = new Dimension((int) ((this.currentOffset.doublePageWidth * this.scaling) + this.insetW + this.insetW),
							(int) ((this.currentOffset.totalDoubleHeight * this.scaling) + doubleGaps + this.insetH + this.insetH));
				}
				break;

		}

		if (debugLayout) System.out.println("pageSize" + pageSize);

		return pageSize;
	}

	public Rectangle getDisplayedRectangle() {

		Rectangle userAnnot = this.pdf.getVisibleRect();

		// get raw rectangle
		/**
		 * if((this.displayRotation==90)||(this.displayRotation==270)){ ry =userAnnot.x; rx =userAnnot.y ; rh =userAnnot.width; rw =userAnnot.height;
		 * }else{
		 */
		this.rx = userAnnot.x;
		this.ry = userAnnot.y;
		this.rw = userAnnot.width;
		this.rh = userAnnot.height;

		// Best way I found to catch if pdf decoder is being used but never displayed
		if (this.pdf.isShowing() && userAnnot.width == 0 || userAnnot.height == 0) {
			this.rx = 0;
			this.ry = 0;
			this.rw = this.pageData.getScaledCropBoxWidth(this.pageNumber);
			this.rh = this.pageData.getScaledCropBoxHeight(this.pageNumber);

			if (this.pageData.getRotation(this.pageNumber) % 180 != 0) {
				this.rh = this.pageData.getScaledCropBoxWidth(this.pageNumber);
				this.rw = this.pageData.getScaledCropBoxHeight(this.pageNumber);
			}
		}

		// System.out.println("R's -> (x,y) " + rx + " " + ry);
		return userAnnot;
	}

	@Override
	public void drawBorder() {

		if (CURRENT_BORDER_STYLE == BORDER_SHOW) {

			if ((this.crw > 0) && (this.crh > 0) && (this.myBorder != null)) this.myBorder.paintBorder(this.pdf, this.g2, this.crx - 1, this.cry - 1,
					this.crw + 1, this.crh + 1);
		}
	}

	void setDisplacementOnG2(Graphics2D gBB) { // multi pages assumes all page 0 and rotates for each
		// translate the graphic to 0,0 on volatileImage java co-ords
		float cX = this.crx / this.scaling, cY = this.cry / this.scaling;
		if (this.displayRotation == 0 || this.displayView != Display.SINGLE_PAGE) gBB.translate(-cX, cY);
		else
			if (this.displayRotation == 90) gBB.translate(-cY, -cX);
			else
				if (this.displayRotation == 180) gBB.translate(cX, -cY);
				else
					if (this.displayRotation == 270) gBB.translate(cY, cX);
	}

	@Override
	public void refreshDisplay() {

		this.screenNeedsRedrawing = true;
		// reset over-ride which may have been enabled

		this.accleratedPagesAlreadyDrawn.clear();

		this.overRideAcceleration = false;
	}

	@Override
	public void flushPageCaches() {
		this.currentPageViews.clear();
		this.cachedPageViews.clear();
	}

	@Override
	public void init(float scaling, int pageCount, int displayRotation, int pageNumber, DynamicVectorRenderer currentDisplay, boolean isInit,
			PdfPageData pageData, int insetW, int insetH) {

		// if(debugLayout)
		// System.out.println("init");

		if (pageNumber < 1) pageNumber = 1;

		this.currentDisplay = currentDisplay;
		this.scaling = scaling;
		this.pageCount = pageCount;
		this.displayRotation = displayRotation;
		this.pageNumber = pageNumber;
		this.pageData = pageData;

		this.insetW = insetW;
		this.insetH = insetH;

		currentDisplay.setInset(insetW, insetH);

		// reset over-ride which may have been enabled
		pageData.setScalingValue(scaling); // ensure aligned
		this.volatileWidth = pageData.getScaledCropBoxWidth(this.pageNumber);
		this.volatileHeight = pageData.getScaledCropBoxHeight(this.pageNumber);

		if (isInit) {
			// lastPageChecked=-1;
			setPageOffsets(this.pageNumber);
			this.isInitialised = true;
		}

		// clear facing drag cache & set not drawing
		if (this.displayView == FACING && (isInit || (this.lastDisplayRotation != displayRotation) || (this.lastScaling != scaling))) {
			for (int i = 0; i < 4; i++) {
				this.facingDragCachedImages[i] = null;
			}

			this.pdf.setUserOffsets(0, 0, org.jpedal.external.OffsetOptions.INTERNAL_DRAG_BLANK);
		}

		this.lastScaling = scaling;
	}

	/**
	 * workout offsets so we can draw pages
	 * */
	@Override
	public void setPageOffsets(int pageNumber) {

		if (debugLayout) System.out.println("setPageOffsets " + pageNumber + ' ' + this.pageCount + " displayView=" + this.displayView + " scaling="
				+ this.scaling);

		if (this.displayView == SINGLE_PAGE) {
			this.xReached = null;
			this.yReached = null;

			// 
			/** pass in values for forms/annots */
			if (this.pdf.getFormRenderer() != null) this.pdf.getFormRenderer().getCompData().setPageDisplacements(this.xReached, this.yReached);

			// 
			return;
		}

		// lastPageChecked=pageNumber;
		// lastState=displayView;

		this.xReached = new int[this.pageCount + 1];
		this.yReached = new int[this.pageCount + 1];
		this.pageW = new int[this.pageCount + 1];
		this.pageH = new int[this.pageCount + 1];
		this.pageOffsetW = new int[this.pageCount + 1];
		this.pageOffsetH = new int[this.pageCount + 1];

		int heightCorrection;
		int displayRotation;

		this.isRotated = new boolean[this.pageCount + 1];
		int gap = PageOffsets.pageGap;// set.pageGap*scaling);

		if (this.turnoverOn && this.pageCount != 2 && !this.pdf.getPdfPageData().hasMultipleSizes() && this.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;

		/** work out page sizes - need to do it first as we can look ahead */
		for (int i = 1; i < this.pageCount + 1; i++) {

			/**
			 * get unrotated page sizes
			 */
			this.pageW[i] = this.pageData.getScaledCropBoxWidth(i);
			this.pageH[i] = this.pageData.getScaledCropBoxHeight(i);

			displayRotation = this.pageData.getRotation(i) + this.displayRotation;
			if (displayRotation >= 360) displayRotation = displayRotation - 360;

			// swap if this page rotated and flag
			if ((displayRotation == 90 || displayRotation == 270)) {
				int tmp = this.pageW[i];
				this.pageW[i] = this.pageH[i];
				this.pageH[i] = tmp;

				this.isRotated[i] = true; // flag page as rotated
			}

			if ((i & 1) == 1) {
				if (this.pageW[i] > RmaxWidth) RmaxWidth = this.pageW[i];
				if (this.pageH[i] > RmaxHeight) RmaxHeight = this.pageH[i];
			}
			else {
				if (this.pageW[i] > LmaxWidth) LmaxWidth = this.pageW[i];
				if (this.pageH[i] > LmaxHeight) LmaxHeight = this.pageH[i];
			}
		}

		// loop through all pages and work out positions
		for (int i = 1; i < this.pageCount + 1; i++) {
			heightCorrection = 0;
			if (((this.pageCount == 2) && (this.displayView == FACING || this.displayView == CONTINUOUS_FACING))
					|| (this.displayView == FACING && !this.separateCover)) { // special case
				// if only 2 pages display side by side
				if ((i & 1) == 1) {
					this.xReached[i] = 0;
					this.yReached[i] = 0;
				}
				else {
					this.xReached[i] = this.xReached[i - 1] + this.pageW[i - 1] + gap;
					this.yReached[i] = 0;
					if (!(i == 2 && this.pageData.getRotation(1) == 270)) {
						this.pageOffsetW[2] = (this.pageW[2] - this.pageW[1]) + this.pageOffsetW[1];
						this.pageOffsetH[2] = (this.pageH[2] - this.pageH[1]) + this.pageOffsetH[1];
					}
				}

			}
			else
				if (i == 1) { // first page is special case
					// First page should be on the left so indent
					if (this.displayView == CONTINUOUS) {
						this.xReached[1] = 0;
						this.yReached[1] = 0;
						this.pageOffsetW[1] = 0;
						this.pageOffsetH[1] = 0;
						this.pageOffsetW[0] = gap; // put the gap values in the empty entry in the offset array. A bit bodgy!
						this.pageOffsetH[0] = gap;

					}
					else
						if (this.displayView == CONTINUOUS_FACING) {
							this.pageOffsetW[0] = gap; // put the gap values in the empty entry in the offset array. A bit bodgy!
							this.pageOffsetH[0] = gap;
							this.pageOffsetW[1] = 0;
							this.pageOffsetH[1] = 0;
							this.xReached[1] = LmaxWidth + gap;
							this.yReached[1] = 0;
						}
						else
							if (this.displayView == FACING) {
								this.xReached[1] = this.pageW[1] + gap;
								this.yReached[1] = 0;
							}

				}
				else {
					// Calculate position for all other pages / cases
					if ((this.displayView == CONTINUOUS_FACING)) {

						if (!(i >= 2 && (((this.pageData.getRotation(i) == 270 || this.pageData.getRotation(i) == 90) && (this.pageData
								.getRotation(i - 1) != 270 || this.pageData.getRotation(i - 1) != 90)) || ((this.pageData.getRotation(i - 1) == 270 || this.pageData
								.getRotation(i - 1) == 90) && (this.pageData.getRotation(i) != 270 || this.pageData.getRotation(i) != 90))))) {
							this.pageOffsetW[i] = (this.pageW[i] - this.pageW[i - 1]) + this.pageOffsetW[i - 1];
							this.pageOffsetH[i] = (this.pageH[i] - this.pageH[i - 1]) + this.pageOffsetH[i - 1];
						}

						// Left Pages
						if ((i & 1) == 0) {
							// Last Page rotated so correct height
							if (i < this.pageCount) heightCorrection = (this.pageH[i + 1] - this.pageH[i]) / 2;
							if (heightCorrection < 0) heightCorrection = 0;// -heightCorrection;
							if (i > 3) {
								int temp = (this.pageH[i - 2] - this.pageH[i - 1]) / 2;
								if (temp > 0) heightCorrection = heightCorrection + temp;
							}
							this.yReached[i] = (this.yReached[i - 1] + this.pageH[i - 1] + gap) + heightCorrection;
						}
						else { // Right Pages
								// Last Page rotated so correct height
							heightCorrection = (this.pageH[i - 1] - this.pageH[i]) / 2;
							this.yReached[i] = (this.yReached[i - 1]) + heightCorrection;
						}

						if ((i & 1) == 0) {// Indent Left pages by diff between maxWidth and pageW (will only indent unrotated)
							this.xReached[i] = this.xReached[i] + (LmaxWidth - this.pageW[i]);
						}
						else {// Place Right Pages with a gap (This keeps pages centered)
							this.xReached[i] = this.xReached[i - 1] + this.pageW[i - 1] + gap;
						}

					}
					else
						if (this.displayView == CONTINUOUS) {
							// Place page below last with gap
							this.yReached[i] = (this.yReached[i - 1] + this.pageH[i - 1] + gap);

							if (!(i >= 2 && (((this.pageData.getRotation(i) == 270 || this.pageData.getRotation(i) == 90) && (this.pageData
									.getRotation(i - 1) != 270 || this.pageData.getRotation(i - 1) != 90)) || ((this.pageData.getRotation(i - 1) == 270 || this.pageData
									.getRotation(i - 1) == 90) && (this.pageData.getRotation(i) != 270 || this.pageData.getRotation(i) != 90))))) {
								this.pageOffsetW[i] = (this.pageW[i] - this.pageW[i - 1]) + this.pageOffsetW[i - 1];
								this.pageOffsetH[i] = (this.pageH[i] - this.pageH[i - 1]) + this.pageOffsetH[i - 1];
							}

						}
						else
							if ((this.displayView == FACING)) {
								if ((i & 1) == 1) { // If right page, place on right with gap
									this.xReached[i] = (this.xReached[i - 1] + this.pageW[i - 1] + gap);
									if (this.pageH[i] < this.pageH[i - 1]) // Drop page down to keep pages centred
									this.yReached[i] = this.yReached[i] + (((this.pageH[i - 1] - this.pageH[i]) / 2));
								}
								else { // If left page, indent by diff of max and current page
									this.xReached[i] = 0;
									if (i < this.pageCount) if (this.pageH[i] < this.pageH[i + 1]) // Drop page down to keep pages centered
									this.yReached[i] = this.yReached[i] + ((this.pageH[i + 1] - this.pageH[i]) / 2);
								}
							}
				}
		}

		// 
		if (this.pdf.getFormRenderer() != null) this.pdf.getFormRenderer().getCompData().setPageDisplacements(this.xReached, this.yReached);
		// 
	}

	@Override
	public void decodeOtherPages(int pageNumber, int pageCount) {}

	@Override
	public void completeForm(Graphics2D g2) {
		g2.drawLine(this.crx, this.cry, this.crx + this.crw, this.cry + this.crh);
		g2.drawLine(this.crx, this.crh + this.cry, this.crw + this.crx, this.cry);
	}

	@Override
	public void resetToDefaultClip() {

		if (this.current2 != null) this.g2.setTransform(this.current2);

		// reset transform and clip
		if (this.currentClip != null) this.g2.setClip(this.currentClip);
	}

	@Override
	public void initRenderer(Map areas, Graphics2D g2, Border myBorder, int indent) {

		// if(debugLayout)
		// System.out.println("initRenderer");

		this.rawAf = g2.getTransform();
		this.rawClip = g2.getClip();

		if (areas != null) this.lastAreasPainted = -2;
		this.areas = areas;
		this.g2 = g2;

		this.myBorder = myBorder;

		this.indent = indent;
		this.pagesDrawn.clear();

		setPageSize(this.pageNumber, this.scaling);
	}

	void setPageSize(int pageNumber, float scaling) {

		/**
		 * handle clip - crop box values
		 */
		this.pageData.setScalingValue(scaling); // ensure aligned
		this.topW = this.pageData.getScaledCropBoxWidth(pageNumber);
		this.topH = this.pageData.getScaledCropBoxHeight(pageNumber);
		double mediaH = this.pageData.getScaledMediaBoxHeight(pageNumber);

		this.cropX = this.pageData.getScaledCropBoxX(pageNumber);
		this.cropY = this.pageData.getScaledCropBoxY(pageNumber);
		this.cropW = this.topW;
		this.cropH = this.topH;

		/**
		 * actual clip values - for flipped page
		 */
		if (this.displayView == Display.SINGLE_PAGE) {
			this.crx = (int) (this.insetW + this.cropX);
			this.cry = (int) (this.insetH - this.cropY);
		}
		else {
			this.crx = this.insetW;
			this.cry = this.insetH;
		}
		// cry =(int)(insetH+(mediaH-cropH)-cropY);

		// amount needed to move cropped page into correct position
		// int offsetX=(int) (mediaW-cropW);
		int offsetY = (int) (mediaH - this.cropH);

		if ((this.displayRotation == 90 || (this.displayRotation == 270))) {
			this.crw = (int) (this.cropH);
			this.crh = (int) (this.cropW);

			int tmp = this.crx;
			this.crx = this.cry;
			this.cry = tmp;

			this.crx = this.crx + offsetY;
		}
		else {
			this.crw = (int) (this.cropW);
			this.crh = (int) (this.cropH);

			this.cry = this.cry + offsetY;
		}
		/**/
		this.g2.translate(this.insetW - this.crx, this.insetH - this.cry);

		// save any transform and stroke
		this.current2 = this.g2.getTransform();

		// save global clip and set to our values
		this.currentClip = this.g2.getClip();

		// if(!showCrop)
		this.g2.clip(new Rectangle(this.crx, this.cry, (int) (this.crw + ((this.insetW * this.additionalPageCount) + this.currentXOffset * scaling)),
				this.crh));

		// System.out.println("Set pagesize "+pageNumber+" scaling="+scaling+" dimensions="+crx+" "+cry+" "+crw+" "+crh+" insetW="+insetW+" currentXOffset="+currentXOffset);
	}

	int topW, topH;
	double cropX, cropY, cropW, cropH;

	// 
	protected GUIThumbnailPanel thumbnails = null;

	//  

	Rectangle calcVisibleArea(int topY, int topX) {

		Rectangle userAnnot;
		/** get area to draw */
		int x = 0, y = 0, h, w, gap;

		// update coords
		getDisplayedRectangle();

		if ((this.displayRotation != 270) && (this.displayRotation != 180) && (this.rx > this.insetW)) x = (int) ((this.rx - this.insetW) / this.scaling);

		// set defaults
		w = (int) ((this.rw + this.insetW) / this.scaling);
		h = topY;

		if (this.displayRotation == 0 || this.displayView != SINGLE_PAGE) {

			x = 0;
			// No need to stop drawing until view edge actualy cuts off part of page
			if (!((this.rx) < (this.insetW))) x = (int) (this.rx / this.scaling) - (int) (this.insetW / this.scaling);

			h = (int) ((this.rh + this.insetH) / this.scaling);
			w = (int) ((this.rw - this.insetW) / this.scaling) + (int) (this.insetW / this.scaling);

			// Following code does not allow for drawing for
			// the first insetW on the left hand side
			// if((x)<(insetW/scaling))
			// x=(int)(insetW/scaling);

			y = (int) (topY - ((this.ry + this.rh) / this.scaling));

		}
		else
			if (this.displayRotation == 90) {

				y = (int) ((this.rx - this.insetW) / this.scaling);
				h = (int) ((this.rw + this.insetW) / this.scaling);

				if (this.ry > this.insetW) {
					x = (int) ((this.ry - this.insetW) / this.scaling);
				}
				else {
					x = 0;
				}
				w = (int) ((this.rh) / this.scaling);

			}
			else
				if (this.displayRotation == 270) {

					w = (int) ((this.rh + this.insetW) / this.scaling);
					x = topX - ((int) ((this.ry + this.rh) / this.scaling));
					h = (int) ((this.rw + this.insetH) / this.scaling);
					y = topY - ((int) ((this.rx + this.rw) / this.scaling));

					if (x < this.insetH) {
						x = 0;
						w = w + this.insetH;
					}

					if (y < this.insetW) {
						y = 0;
						h = h + this.insetW;
					}

				}
				else
					if (this.displayRotation == 180) {

						h = (int) ((this.rh + this.insetH) / this.scaling) + this.insetH;
						y = ((int) ((this.ry - this.insetH) / this.scaling));
						w = (int) ((this.rw + this.insetW) / this.scaling);
						x = topX - ((int) ((this.rx - this.insetW) / this.scaling));
						x = x - w;

						// Check x or y is not negative
						if (x < 0 || x < this.insetH) {
							x = 0;
							w = w + this.insetH;
						}
						if (y < this.insetW) {
							y = 0;
							h = h + this.insetW;
						}

					}

		// do not use on hardware acceleration
		if (isAccelerated() || (this.scaling >= 2)) {
			int dx1 = this.pageData.getScaledCropBoxX(this.pageNumber);
			int dy1 = this.pageData.getScaledCropBoxY(this.pageNumber);

			// System.err.println("dx1 and dy1 == " + dx1 + " " + dy1);

			gap = 2;

			if (dx1 != 0 || dy1 != 0) {
				userAnnot = new Rectangle(x + (int) (dx1 / this.scaling), y + (int) (dy1 / this.scaling), w, h);
			}
			else {
				userAnnot = new Rectangle(x, y, w + (gap + gap), h + (gap + gap));
			}

		}
		else userAnnot = null;

		if ((this.displayView != Display.SINGLE_PAGE) && (this.message)) {
			this.message = true;
			System.out.println("SingleDisplay fast scrolling");
			userAnnot = null;
		}

		if (debugLayout) System.out.println("userAnnot=" + userAnnot + " scaling=" + this.scaling);

		return userAnnot;
	}

	@Override
	public Rectangle drawPage(AffineTransform viewScaling, AffineTransform displayScaling, int pageUsedForTransform) {

		Rectangle actualBox = null;

		/** add in other elements */
		if ((displayScaling != null) && (this.currentDisplay != null)) {

			// reset on scaling dropping back below 200 so retested below
			if (this.scaling < 2 && this.oldScaling > 2) this.useAcceleration = true;

			// redraw highlights
			if (this.useAcceleration || this.areas != null) {

				int areasPainted = -1;
				if (this.areas != null) areasPainted = this.areas.size();

				if (this.lastAreasPainted == -2 && areasPainted == -1) {}
				else
					if (areasPainted != this.lastAreasPainted) {
						this.screenNeedsRedrawing = true;
						this.lastAreasPainted = areasPainted;
					}
			}

			boolean canDrawAccelerated = false;
			// use hardware acceleration - it sucks on large image so
			// we check scaling as well...
			if ((this.useAcceleration) && (!this.overRideAcceleration) && (this.scaling < 2)) canDrawAccelerated = testAcceleratedRendering();
			else this.useAcceleration = false;

			/**
			 * pass in values as needed for patterns
			 */
			this.currentDisplay.setScalingValues(this.cropX, this.cropH + this.cropY, this.scaling);

			this.g2.transform(displayScaling);

			if (DynamicVectorRenderer.debugPaint) System.err.println("accelerate or redraw");

			if (canDrawAccelerated) {
				// rendering to the back buffer:
				Graphics2D gBB = (Graphics2D) this.backBuffer.getGraphics();

				if (this.screenNeedsRedrawing) {

					// fill background white to prevent memory overRun from previous graphics memory
					this.currentDisplay.setG2(gBB);
					this.currentDisplay.paintBackground(new Rectangle(0, 0, this.backBuffer.getWidth(), this.backBuffer.getHeight()));

					this.currentDisplay.setOptimsePainting(true);

					gBB.setTransform(displayScaling);
					setDisplacementOnG2(gBB);

					this.currentDisplay.setG2(gBB);
					if (this.areas != null) actualBox = this.currentDisplay.paint(((Rectangle[]) this.areas.get(this.pageNumber)), viewScaling,
							this.userAnnot);
					else actualBox = this.currentDisplay.paint(null, viewScaling, this.userAnnot);

					// drawSpreadPages(gBB,viewScaling,userAnnot,false);

					this.screenNeedsRedrawing = false;
				}

				gBB.dispose();

				if (this.backBuffer != null) {

					// draw the buffer
					AffineTransform affBefore = this.g2.getTransform();

					this.g2.setTransform(this.rawAf);
					this.g2.drawImage(this.backBuffer, this.insetW, this.insetH, this.pdf);
					this.g2.setTransform(affBefore);

					/**
					 * draw other page outlines and any decoded pages so visible
					 */

					// 20091124 I don't think is needed and it is faster without

					// actualBox =currentDisplay.paint(g2,areas,viewScaling,userAnnot,true,true);

					// drawSpreadPages(g2,viewScaling,userAnnot,true);
				}
			}
			else {
				if (DynamicVectorRenderer.debugPaint) System.err.println("standard paint called ");

				/**
				 * draw other page outlines and any decoded pages so visible
				 */
				// same rectangle works for any rotation so removed the rotation check
				// System.out.println("a");

				// The page clip already seems to have been taken into account, and occasionally this causes issues at high
				// scaling values, so I've commented it out for now.
				// g2.clip(new Rectangle((int)(cropX/scaling),(int)(cropY/scaling),(int)(topW/scaling),(int)(topH/scaling)));
				this.currentDisplay.setOptimsePainting(false);

				this.currentDisplay.setG2(this.g2);
				if (this.areas != null) actualBox = this.currentDisplay.paint(((Rectangle[]) this.areas.get(this.pageNumber)), viewScaling,
						this.userAnnot);
				else actualBox = this.currentDisplay.paint(null, viewScaling, this.userAnnot);

				// drawSpreadPages(g2,viewScaling,userAnnot,false);
			}

			// track scaling so we can update dependent values
			this.oldScaling = this.scaling;
			this.oldRotation = this.displayRotation;

		}

		return actualBox;
	}

	/**
	 * used internally by multiple pages scaling -1 to ignore, -2 to force reset
	 */
	@Override
	public int getXCordForPage(int page, float scaling) {

		if (scaling == -2 || (scaling != -1f && scaling != this.oldScaling)) {
			this.oldScaling = scaling;
			setPageOffsets(page);
		}
		return getXCordForPage(page);
	}

	/**
	 * used internally by multiple pages scaling -1 to ignore, -2 to force reset
	 */
	@Override
	public int getYCordForPage(int page, float scaling) {

		if (scaling == -2 || (scaling != -1f && scaling != this.oldScaling)) {
			this.oldScaling = scaling;
			setPageOffsets(page);
		}
		return getYCordForPage(page);
	}

	@Override
	public void setup(boolean useAcceleration, PageOffsets currentOffset, PdfDecoder pdf) {

		this.useAcceleration = useAcceleration;
		this.currentOffset = currentOffset;
		this.pdf = pdf;

		this.overRideAcceleration = false;
	}

	@Override
	public int getXCordForPage(int page) {
		if (this.xReached != null) return this.xReached[page] + this.insetW;
		else return this.insetW;
	}

	@Override
	public int getYCordForPage(int page) {
		if (this.yReached != null) return this.yReached[page] + this.insetH;
		else return this.insetH;
	}

	@Override
	public int getStartPage() {
		return this.startViewPage;
	}

	@Override
	public int getEndPage() {
		return this.endViewPage;
	}

	// 
	// 
	@Override
	public void setThumbnailPanel(GUIThumbnailPanel thumbnails) {
		this.thumbnails = thumbnails;
	}

	// 
	// 

	@Override
	public void setScaling(float scaling) {
		this.scaling = scaling;
		if (this.pageData != null) this.pageData.setScalingValue(scaling);
	}

	@Override
	public void setAcceleration(boolean enable) {
		this.useAcceleration = enable;
	}

	@Override
	public int getWidthForPage(int pageNumber) {
		return this.pageW[pageNumber];
	}

	/**
	 * internal method used by Viewer to provide preview of PDF in Viewer
	 */
	public void setPreviewThumbnail(BufferedImage previewImage, String previewText) {

		this.previewImage = previewImage;
		this.previewText = previewText;
	}

	/**
	 * put a little thumbnail of page on display for user in viewer as he scrolls through
	 * 
	 * @param g2
	 * @param visibleRect
	 */
	@Override
	public void drawPreviewImage(Graphics2D g2, Rectangle visibleRect) {

		if (this.previewImage != null) {
			int iw = this.previewImage.getWidth();
			int ih = this.previewImage.getHeight();

			int textWidth = g2.getFontMetrics().stringWidth(this.previewText);

			int width = iw > textWidth ? iw : textWidth;

			int x = visibleRect.x + visibleRect.width - 40 - width;
			int y = (visibleRect.y + visibleRect.height - 20 - ih) / 2;

			Composite original = g2.getComposite();
			g2.setPaint(Color.BLACK);
			g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f));
			g2.fill(new RoundRectangle2D.Double(x - 10, y - 10, width + 20, ih + 35, 10, 10));
			g2.setComposite(original);

			g2.setPaint(Color.WHITE);
			x += (width - iw) / 2;
			g2.drawImage(this.previewImage, x, y, null);

			int xOffset = (iw + 20 - textWidth) / 2;

			g2.drawString(this.previewText, x + xOffset - 10, y + ih + 15);
		}
	}

	@Override
	public void setHighlightedImage(int[] highlightedImage) {
		this.highlightedImage = highlightedImage;
	}

	@Override
	public boolean getBoolean(BoolValue option) {
		switch (option) {
			case SEPARATE_COVER:
				return this.separateCover;
			case TURNOVER_ON:
				return this.turnoverOn;
			default:
				return false;
		}
	}

	@Override
	public void setBoolean(BoolValue option, boolean value) {
		switch (option) {
			case SEPARATE_COVER:
				this.separateCover = value;
				return;
			case TURNOVER_ON:
				this.turnoverOn = value;
				return;
			default:
		}
	}

	@Override
	public float getOldScaling() {
		return this.oldScaling;
	}

	@Override
	public int[] getHighlightedImage() {
		return this.highlightedImage;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy