diff options
Diffstat (limited to 'pdf-over-gui')
| -rw-r--r-- | pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SignaturePanel.java | 1098 | 
1 files changed, 549 insertions, 549 deletions
| diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SignaturePanel.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SignaturePanel.java index 193f5974..575c8f86 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SignaturePanel.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SignaturePanel.java @@ -1,549 +1,549 @@ -/* - * Copyright 2012 by A-SIT, Secure Information Technology Center Austria - * - * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by - * the European Commission - subsequent versions of the EUPL (the "Licence"); - * You may not use this work except in compliance with the Licence. - * You may obtain a copy of the Licence at: - * http://joinup.ec.europa.eu/software/page/eupl - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the Licence is distributed on an "AS IS" basis, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Licence for the specific language governing permissions and - * limitations under the Licence. - */ -package at.asit.pdfover.gui.composites; - -// Imports -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; - -import javax.swing.JPanel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import at.asit.pdfover.gui.Constants; -import at.asit.pdfover.gui.utils.Messages; - -import com.sun.pdfview.PDFFile; -import com.sun.pdfview.PDFPage; - -/** - *  - */ -public class SignaturePanel extends JPanel { -	/** -	 * SLF4J Logger instance -	 **/ -	private static final Logger log = LoggerFactory -			.getLogger(SignaturePanel.class); - -	/** Default serial version ID */ -	private static final long serialVersionUID = 1L; - -	/** The PDF file being displayed */ -	private PDFFile pdf = null; -	/** The image of the rendered PDF page being displayed */ -	Image currentImage = null; -	/** The current PDFPage that was rendered into currentImage */ -	private PDFPage currentPage = null; -	/** The current transform from screen to page space */ -	AffineTransform currentXform = null; -	/** The horizontal offset of the image from the left edge of the panel */ -	int offX = 0; -	/** The vertical offset of the image from the top of the panel */ -	int offY = 0; -	/** The size of the image */ -	private Dimension prevSize = null; -	/** The position of the signature, in page space */ -	Point2D sigPagePos = null; -	/** The position of the signature, in screen space */ -	Point2D sigScreenPos = null; -	/** The signature placeholder image */ -	private Image sigPlaceholder = null; -	/** Current scaled signature placeholder image */ -	BufferedImage sigPlaceholderScaled = null; -	/** Transparency of the signature placeholder (0-255) */ -	private int sigPlaceholderTransparency = Constants.DEFAULT_SIGNATURE_PLACEHOLDER_TRANSPARENCY; -	/** Previous Transparency of the signature placeholder */ -	private int prevSigPlaceholderTransparency = 0; -	/** Width of the signature placeholder in page space */ -	private int sigPageWidth = 0; -	/** Height of the signature placeholder in page space */ -	private int sigPageHeight = 0; -	/** Width of the signature placeholder in screen space */ -	int sigScreenWidth = 0; -	/** Height of the signature placeholder in screen space */ -	int sigScreenHeight = 0; -	/** Previous Width of the signature placeholder in screen space */ -	int prevSigScreenWidth = 0; -	/** Previous Height of the signature placeholder in screen space */ -	int prevSigScreenHeight = 0; -	/** Color of the signature placeholder border */ -	private Color sigPlaceholderBorderColor = Color.BLUE; -	/** Current page */ -	int page = 0; -	/** Number of pages in the document */ -	int numPages = 0; -	/** Cursor types */ -	static enum Cursors {DEFAULT, HAND, MOVE}; -	/** Default arrow cursor */ -	final Cursor defaultCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); -	/** Hand cursor */ -	final Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); -	/** Move cursor */ -	final Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR); -	/** Current cursor */ -	Cursors currentCursor = Cursors.DEFAULT; - -	/** -	 * Create a new PagePanel. -	 * @param pdf the PDFFile to display -	 */ -	public SignaturePanel(PDFFile pdf) { -		super(new BorderLayout()); -		setDocument(pdf); -		setPreferredSize(new Dimension(Constants.DEFAULT_MAINWINDOW_WIDTH, Constants.DEFAULT_MAINWINDOW_HEIGHT - Constants.MAINBAR_HEIGHT)); -		setFocusable(true); -		addMouseListener(this.mouseListener); -		addMouseMotionListener(this.mouseListener); -	} - -	/** -	 * Set a new document to be displayed -	 * @param pdf the PDFFile to be displayed -	 */ -	public void setDocument(PDFFile pdf) { -		this.pdf = pdf; -		this.sigPagePos = null; -		this.numPages = pdf.getNumPages(); -		showPage(this.numPages); -	} - -	/** -	 * Set the signature placeholder image -	 * @param placeholder signature placeholder -	 * @param width width of the placeholder in page space -	 * @param height height of the placeholder in page space  -	 * @param transparency transparency of the signature placeholder (0 - 255) -	 */ -	public void setSignaturePlaceholder(Image placeholder, int width, int height, int transparency) { -		this.sigPlaceholder = placeholder; -		this.sigPageWidth = width; -		this.sigPageHeight = height; -		this.sigPlaceholderTransparency = transparency; -	} - -	/** -	 * Set the color of the signature placeholder border -	 * @param color new signature placeholder border color -	 */ -	public void setSignaturePlaceholderBorderColor(Color color) { -		this.sigPlaceholderBorderColor = color; -	} - -	/** -	 * Change the currently displayed page -	 * @param page the number of the page to display -	 */ -	public void showPage(int page) { -		this.page = page; -		showPage(this.pdf.getPage(page)); -	} - -	/** -	 * Add and display a new page at the end of the document -	 *  -	 * This page has the same dimensions as the old last page -	 */ -	public void addNewLastPage() { -		showPage(this.numPages + 1); -	} - -	/** -	 * Translate the signature placeholder position -	 * @param sigXOffset signature placeholder horizontal position offset -	 * @param sigYOffset signature placeholder vertical position offset -	 */ -	public void translateSignaturePosition(int sigXOffset, int sigYOffset) { -		updateSigPos((int) this.sigScreenPos.getX() + sigXOffset, (int) this.sigScreenPos.getY() + sigYOffset); -	} - -	/** -	 * Set the signature placeholder position -	 * Call showPage afterwards to update actual position -	 * @param x the horizontal signature position -	 * @param y the vertical signature position -	 */ -	public void setSignaturePosition(float x, float y) -	{ -		this.sigPagePos = new Point2D.Double(x, y); -	} - -	/** -	 * Get the current horizontal position of the signature -	 * @return signature x coordinate -	 */ -	public float getSignaturePositionX() { -		if (this.sigPagePos == null) -			return 0; -		return Math.max((float) this.sigPagePos.getX(), 0); -	} - -	/** -	 * Get the current vertical position of the signature -	 * @return signature y coordinate -	 */ -	public float getSignaturePositionY() { -		if (this.sigPagePos == null) -			return 0; -		return Math.max((float) this.sigPagePos.getY(), 0); -	} - -	/** -	 * Stop the generation of any previous page, and draw the new one. -	 *  -	 * @param page the PDFPage to draw. -	 */ -	private synchronized void showPage(PDFPage page) { -		// stop drawing the previous page -		if (this.currentPage != null && this.prevSize != null) { -			this.currentPage.stop(this.prevSize.width, this.prevSize.height, null); -		} - -		boolean newPage = false; -		// set up the new page -		if (this.page > this.numPages) -		{ -			// New last page - use old last page as template -			this.currentPage = this.pdf.getPage(this.numPages); -			newPage = true; -		} -		else -			this.currentPage = page; - - -		if (this.currentPage == null) { -			// no page -			this.currentImage = null; -			this.currentXform = null; -			repaint(); -		} else { -			// start drawing -			Dimension sz = getSize(); -			if (sz.width + sz.height == 0) { -				// no image to draw. -				return; -			} - -			Dimension pageSize = this.currentPage.getUnstretchedSize(sz.width, sz.height, -					null); - -			// get the new image -			if (newPage) -			{ -				this.currentImage = new BufferedImage(pageSize.width, pageSize.height, BufferedImage.TYPE_INT_RGB); -				Graphics g = this.currentImage.getGraphics(); -				g.setColor(Color.WHITE); -				g.fillRect(0, 0, pageSize.width, pageSize.height); -			} -			else -				this.currentImage = this.currentPage.getImage(pageSize.width, pageSize.height, -						null, this); - -			// calculate the transform from page to screen space -			this.currentXform = new AffineTransform(1, 0, 0, -1, 0, pageSize.height); -			Rectangle2D clip = this.currentPage.getBBox(); -			double scaleX = pageSize.width / clip.getWidth(); -			double scaleY = pageSize.height / clip.getHeight(); -			this.currentXform.scale(scaleX, scaleY); -			this.currentXform.translate(-clip.getMinX(), -clip.getMinY()); - -			if (this.sigPagePos != null) -				this.sigScreenPos = this.currentXform.transform(this.sigPagePos, this.sigScreenPos); -			this.sigScreenWidth = (int) Math.round(this.sigPageWidth * this.currentXform.getScaleX()); -			this.sigScreenHeight = (int) Math.round(this.sigPageHeight * this.currentXform.getScaleX()); - -			// invert the transform (screen to page space) -			try { -				this.currentXform = this.currentXform.createInverse(); -			} catch (NoninvertibleTransformException nte) { -				log.error("Error inverting page transform!", nte); //$NON-NLS-1$ -			} - -			if (this.sigPagePos == null) -			{ -				this.sigScreenPos = new Point2D.Double( -						clamp((int) (pageSize.getWidth() / 2), 0, this.currentImage.getWidth(null) - this.sigScreenWidth), -						clamp((int) ((pageSize.getHeight() / 4) * 3), 0, this.currentImage.getHeight(null) - this.sigScreenHeight)); -				this.sigPagePos = this.currentXform.transform(this.sigScreenPos, this.sigPagePos); -			} -			else -				updateSigPos((int) this.sigScreenPos.getX(), (int) this.sigScreenPos.getY()); - -			this.prevSize = pageSize; - -			repaint(); -		} -	} - -	/** -	 * Draw the image. -	 */ -	@Override -	public void paint(Graphics g) { -		Dimension sz = getSize(); -		g.setColor(getBackground()); -		g.fillRect(0, 0, getWidth(), getHeight()); -		if (this.currentImage == null) { -			g.setColor(Color.black); -			g.drawString(Messages.getString("error.SignaturePanel.NoPage"), getWidth() / 2 - 30, //$NON-NLS-1$ -					getHeight() / 2); -			if (this.currentPage != null) { -				showPage(this.currentPage); -			} -		} else { -			// draw the image -			int imwid = this.currentImage.getWidth(null); -			int imhgt = this.currentImage.getHeight(null); - -			// draw it centered within the panel -			this.offX = (sz.width - imwid) / 2; -			this.offY = (sz.height - imhgt) / 2; - -			if ((imwid == sz.width && imhgt <= sz.height) -					|| (imhgt == sz.height && imwid <= sz.width)) { - -				// draw document -				g.drawImage(this.currentImage, this.offX, this.offY, this); - -				// draw signature -				int sigX = (int) (this.offX + this.sigScreenPos.getX()); -				int sigY = (int) (this.offY + this.sigScreenPos.getY()); -				if (this.sigPlaceholder == null) { -					g.setColor(Color.red); -					g.drawRect(sigX, sigY, 100, 40); -				} -				else { -					if ( -							((this.sigScreenWidth != this.prevSigScreenWidth) || -							(this.sigScreenHeight != this.prevSigScreenHeight) || -							(this.sigPlaceholderTransparency != this.prevSigPlaceholderTransparency)) && -							((this.sigScreenWidth != 0) && (this.sigScreenHeight != 0))) -					{ -						// redraw scaled transparent placeholder -						this.prevSigScreenWidth = this.sigScreenWidth; -						this.prevSigScreenHeight = this.sigScreenHeight; -						this.prevSigPlaceholderTransparency = this.sigPlaceholderTransparency; -						Image placeholder = this.sigPlaceholder.getScaledInstance( -								this.sigScreenWidth, this.sigScreenHeight, Image.SCALE_SMOOTH); - -						//Make placeholder transparent -						this.sigPlaceholderScaled = new BufferedImage(this.sigScreenWidth, this.sigScreenHeight, BufferedImage.TYPE_INT_ARGB); -						Graphics g_phs = this.sigPlaceholderScaled.getGraphics(); -						g_phs.drawImage(placeholder, 0, 0, null); - -						// Draw grey "Signature" overlay -						String overlay = Messages.getString("positioning.signature"); //$NON-NLS-1$ -						// Voodoo to get overlay font scale -						float scale = (((float) this.sigScreenWidth) / this.sigPlaceholder.getWidth(null)) * 150; -						g_phs.setFont(getFont().deriveFont(scale)); -						g_phs.setColor(this.sigPlaceholderBorderColor); -						Rectangle2D overlay_size = g_phs.getFontMetrics().getStringBounds(overlay, g_phs); -						int width = (int) overlay_size.getWidth(); -						int height = (int) overlay_size.getHeight(); -						int x = this.sigScreenWidth / 2 - width / 2; -						int y = this.sigScreenHeight / 2 - height / 2 + g_phs.getFontMetrics().getAscent(); -						g_phs.drawString(overlay, x, y); - -						g_phs.dispose(); -						int[] phpixels = new int[this.sigScreenWidth * this.sigScreenHeight]; -						phpixels = this.sigPlaceholderScaled.getRGB(0, 0, this.sigScreenWidth, this.sigScreenHeight, phpixels, 0, this.sigScreenWidth); -						for (int i = 0; i < phpixels.length; ++i) { -							Color c = new Color(phpixels[i]); -							c = new Color(c.getRed(), c.getGreen(), c.getBlue(), this.sigPlaceholderTransparency); -							phpixels[i] = c.getRGB(); -						} -						this.sigPlaceholderScaled.setRGB(0, 0, this.sigScreenWidth, this.sigScreenHeight, phpixels, 0, this.sigScreenWidth); -					} -					g.drawImage(this.sigPlaceholderScaled, sigX, sigY, null); -					g.setColor(this.sigPlaceholderBorderColor); -					g.drawRect(sigX, sigY, this.sigScreenWidth-1, this.sigScreenHeight-1); -				} - -			} else { -				// the image is bogus. try again, or give up. -				if (this.currentPage != null) { -					showPage(this.currentPage); -				} -				g.setColor(Color.black); -				g.drawString(Messages.getString("error.SignaturePanel.NoRender"), getWidth() / 2 - 30, //$NON-NLS-1$ -						getHeight() / 2); -			} -		} -	} - -	/** -	 * Handles notification of the fact that some part of the image changed. -	 * Repaints that portion. -	 *  -	 * @return true if more updates are desired. -	 */ -	@Override -	public boolean imageUpdate(Image img, int infoflags, int x, int y, -			int width, int height) { -		if ((infoflags & (SOMEBITS | ALLBITS)) != 0) { -			repaint(x + this.offX, y + this.offY, width, height); -		} -		return ((infoflags & (ALLBITS | ERROR | ABORT)) == 0); -	} - -	private MouseAdapter mouseListener = new MouseAdapter() { - -		private boolean doDrag = false; -		private int dragXOffset = 0; -		private int dragYOffset = 0; - -		private void updateSigPos(int sigx, int sigy) { -			SignaturePanel.this.updateSigPos( -					sigx - SignaturePanel.this.offX, -					sigy - SignaturePanel.this.offY); -		} - -		/** Handles a mouseMoved event */ -		@Override -		public void mouseMoved(MouseEvent evt) { -//			try { -			boolean onSig = isOnSignature(evt.getX(), evt.getY()); -			setCursor(onSig ? Cursors.HAND : Cursors.DEFAULT); -//			}catch (NullPointerException e) { -//				log.debug("Error " +e.getMessage()); //$NON-NLS-1$ -//			} -		} - -		/** Handles a mousePressed event */ -		@Override -		public void mousePressed(MouseEvent evt) { -			if (evt.getButton() == MouseEvent.BUTTON1) -			{ -				this.doDrag = true; -				if (isOnSignature(evt.getX(), evt.getY())) { -					this.dragXOffset = (int) -							(SignaturePanel.this.sigScreenPos.getX() - -							(evt.getX() - SignaturePanel.this.offX)); -					this.dragYOffset = (int) -							(SignaturePanel.this.sigScreenPos.getY() - -							(evt.getY() - SignaturePanel.this.offY)); -				} -				else { -					this.dragXOffset = 0; -					this.dragYOffset = 0; -				} -				updateSigPos(evt.getX() + this.dragXOffset, evt.getY() + this.dragYOffset); -				setCursor(Cursors.MOVE); -			} -		} - -		/** Handles a mouseReleased event */ -		@Override -		public void mouseReleased(MouseEvent evt) { -			this.doDrag = false; -			boolean onSig = isOnSignature(evt.getX(), evt.getY()); -			setCursor(onSig ? Cursors.HAND : Cursors.DEFAULT); -		} - -		/** -		 * Handles a mouseDragged event. -		 */ -		@Override -		public void mouseDragged(MouseEvent evt) { -			if (this.doDrag) -				updateSigPos(evt.getX() + this.dragXOffset, evt.getY() + this.dragYOffset); -		} -	}; - -	/** -	 * Sets the mouse cursor -	 * @param cursor cursor to set -	 */ -	void setCursor(Cursors cursor) -	{ -		if (this.currentCursor == cursor) -			return; -		this.currentCursor = cursor; -		Cursor cur = null; -		switch (cursor) { -			case DEFAULT: -				cur = this.defaultCursor; -				break; -			case HAND: -				cur = this.handCursor; -				break; -			case MOVE: -				cur = this.moveCursor; -				break; -		} -		this.getParent().setCursor(cur); -	} - -	/** -	 * Check whether given point is on signature placeholder -	 * @param x x coordinate -	 * @param y y coordinate -	 * @return true if given point is on signature placeholder -	 */ -	boolean isOnSignature(int x, int y) -	{ -		Rectangle2D sig = new Rectangle2D.Double( -				this.sigScreenPos.getX() + this.offX, -				this.sigScreenPos.getY() + this.offY, -				this.sigScreenWidth, -				this.sigScreenHeight); -		Point2D pos = new Point2D.Double(x, y); -		return (sig.contains(pos)); -	} - -	/** -	 * Update the signature placeholder position -	 * @param sigx X position on the document (screen coordinates) -	 * @param sigy Y position on the document (screen coordinates) -	 */ -	void updateSigPos(int sigx, int sigy) { -		if (this.currentImage == null) -			return; -		sigx = clamp(sigx, 0, this.currentImage.getWidth(null) - this.sigScreenWidth); -		sigy = clamp(sigy, 0, this.currentImage.getHeight(null) - this.sigScreenHeight); -		this.sigScreenPos = new Point2D.Double(sigx, sigy); -		this.sigPagePos = this.currentXform.transform(this.sigScreenPos, this.sigPagePos); -		repaint(); -	} - -	/** -	 * Clamp x to be within [min-max] -	 * @param x int to clamp -	 * @param min minimum value -	 * @param max maximum value -	 * @return clamped x -	 */ -	static int clamp(int x, int min, int max) -	{ -		if (x < min) -			x = min; -		else if (x > max) -			x = max; -		return x; -	} -} +/*
 + * Copyright 2012 by A-SIT, Secure Information Technology Center Austria
 + *
 + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 + * the European Commission - subsequent versions of the EUPL (the "Licence");
 + * You may not use this work except in compliance with the Licence.
 + * You may obtain a copy of the Licence at:
 + * http://joinup.ec.europa.eu/software/page/eupl
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the Licence is distributed on an "AS IS" basis,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the Licence for the specific language governing permissions and
 + * limitations under the Licence.
 + */
 +package at.asit.pdfover.gui.composites;
 +
 +// Imports
 +import java.awt.BorderLayout;
 +import java.awt.Color;
 +import java.awt.Cursor;
 +import java.awt.Dimension;
 +import java.awt.Graphics;
 +import java.awt.Image;
 +import java.awt.event.MouseAdapter;
 +import java.awt.event.MouseEvent;
 +import java.awt.geom.AffineTransform;
 +import java.awt.geom.NoninvertibleTransformException;
 +import java.awt.geom.Point2D;
 +import java.awt.geom.Rectangle2D;
 +import java.awt.image.BufferedImage;
 +
 +import javax.swing.JPanel;
 +
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +import at.asit.pdfover.gui.Constants;
 +import at.asit.pdfover.gui.utils.Messages;
 +
 +import com.sun.pdfview.PDFFile;
 +import com.sun.pdfview.PDFPage;
 +
 +/**
 + * 
 + */
 +public class SignaturePanel extends JPanel {
 +	/**
 +	 * SLF4J Logger instance
 +	 **/
 +	private static final Logger log = LoggerFactory
 +			.getLogger(SignaturePanel.class);
 +
 +	/** Default serial version ID */
 +	private static final long serialVersionUID = 1L;
 +
 +	/** The PDF file being displayed */
 +	private PDFFile pdf = null;
 +	/** The image of the rendered PDF page being displayed */
 +	Image currentImage = null;
 +	/** The current PDFPage that was rendered into currentImage */
 +	private PDFPage currentPage = null;
 +	/** The current transform from screen to page space */
 +	AffineTransform currentXform = null;
 +	/** The horizontal offset of the image from the left edge of the panel */
 +	int offX = 0;
 +	/** The vertical offset of the image from the top of the panel */
 +	int offY = 0;
 +	/** The size of the image */
 +	private Dimension prevSize = null;
 +	/** The position of the signature, in page space */
 +	Point2D sigPagePos = null;
 +	/** The position of the signature, in screen space */
 +	Point2D sigScreenPos = null;
 +	/** The signature placeholder image */
 +	private Image sigPlaceholder = null;
 +	/** Current scaled signature placeholder image */
 +	BufferedImage sigPlaceholderScaled = null;
 +	/** Transparency of the signature placeholder (0-255) */
 +	private int sigPlaceholderTransparency = Constants.DEFAULT_SIGNATURE_PLACEHOLDER_TRANSPARENCY;
 +	/** Previous Transparency of the signature placeholder */
 +	private int prevSigPlaceholderTransparency = 0;
 +	/** Width of the signature placeholder in page space */
 +	private int sigPageWidth = 0;
 +	/** Height of the signature placeholder in page space */
 +	private int sigPageHeight = 0;
 +	/** Width of the signature placeholder in screen space */
 +	int sigScreenWidth = 0;
 +	/** Height of the signature placeholder in screen space */
 +	int sigScreenHeight = 0;
 +	/** Previous Width of the signature placeholder in screen space */
 +	int prevSigScreenWidth = 0;
 +	/** Previous Height of the signature placeholder in screen space */
 +	int prevSigScreenHeight = 0;
 +	/** Color of the signature placeholder border */
 +	private Color sigPlaceholderBorderColor = Color.BLUE;
 +	/** Current page */
 +	int page = 0;
 +	/** Number of pages in the document */
 +	int numPages = 0;
 +	/** Cursor types */
 +	static enum Cursors {DEFAULT, HAND, MOVE};
 +	/** Default arrow cursor */
 +	final Cursor defaultCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
 +	/** Hand cursor */
 +	final Cursor handCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
 +	/** Move cursor */
 +	final Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
 +	/** Current cursor */
 +	Cursors currentCursor = Cursors.DEFAULT;
 +
 +	/**
 +	 * Create a new PagePanel.
 +	 * @param pdf the PDFFile to display
 +	 */
 +	public SignaturePanel(PDFFile pdf) {
 +		super(new BorderLayout());
 +		setDocument(pdf);
 +		setPreferredSize(new Dimension(Constants.DEFAULT_MAINWINDOW_WIDTH, Constants.DEFAULT_MAINWINDOW_HEIGHT - Constants.MAINBAR_HEIGHT));
 +		setFocusable(true);
 +		addMouseListener(this.mouseListener);
 +		addMouseMotionListener(this.mouseListener);
 +	}
 +
 +	/**
 +	 * Set a new document to be displayed
 +	 * @param pdf the PDFFile to be displayed
 +	 */
 +	public void setDocument(PDFFile pdf) {
 +		this.pdf = pdf;
 +		this.sigPagePos = null;
 +		this.numPages = pdf.getNumPages();
 +		showPage(this.numPages);
 +	}
 +
 +	/**
 +	 * Set the signature placeholder image
 +	 * @param placeholder signature placeholder
 +	 * @param width width of the placeholder in page space
 +	 * @param height height of the placeholder in page space 
 +	 * @param transparency transparency of the signature placeholder (0 - 255)
 +	 */
 +	public void setSignaturePlaceholder(Image placeholder, int width, int height, int transparency) {
 +		this.sigPlaceholder = placeholder;
 +		this.sigPageWidth = width;
 +		this.sigPageHeight = height;
 +		this.sigPlaceholderTransparency = transparency;
 +	}
 +
 +	/**
 +	 * Set the color of the signature placeholder border
 +	 * @param color new signature placeholder border color
 +	 */
 +	public void setSignaturePlaceholderBorderColor(Color color) {
 +		this.sigPlaceholderBorderColor = color;
 +	}
 +
 +	/**
 +	 * Change the currently displayed page
 +	 * @param page the number of the page to display
 +	 */
 +	public void showPage(int page) {
 +		this.page = page;
 +		showPage(this.pdf.getPage(page));
 +	}
 +
 +	/**
 +	 * Add and display a new page at the end of the document
 +	 * 
 +	 * This page has the same dimensions as the old last page
 +	 */
 +	public void addNewLastPage() {
 +		showPage(this.numPages + 1);
 +	}
 +
 +	/**
 +	 * Translate the signature placeholder position
 +	 * @param sigXOffset signature placeholder horizontal position offset
 +	 * @param sigYOffset signature placeholder vertical position offset
 +	 */
 +	public void translateSignaturePosition(int sigXOffset, int sigYOffset) {
 +		updateSigPos((int) this.sigScreenPos.getX() + sigXOffset, (int) this.sigScreenPos.getY() + sigYOffset);
 +	}
 +
 +	/**
 +	 * Set the signature placeholder position
 +	 * Call showPage afterwards to update actual position
 +	 * @param x the horizontal signature position
 +	 * @param y the vertical signature position
 +	 */
 +	public void setSignaturePosition(float x, float y)
 +	{
 +		this.sigPagePos = new Point2D.Double(x, y);
 +	}
 +
 +	/**
 +	 * Get the current horizontal position of the signature
 +	 * @return signature x coordinate
 +	 */
 +	public float getSignaturePositionX() {
 +		if (this.sigPagePos == null)
 +			return 0;
 +		return Math.max((float) this.sigPagePos.getX(), 0);
 +	}
 +
 +	/**
 +	 * Get the current vertical position of the signature
 +	 * @return signature y coordinate
 +	 */
 +	public float getSignaturePositionY() {
 +		if (this.sigPagePos == null)
 +			return 0;
 +		return Math.max((float) this.sigPagePos.getY(), 0);
 +	}
 +
 +	/**
 +	 * Stop the generation of any previous page, and draw the new one.
 +	 * 
 +	 * @param page the PDFPage to draw.
 +	 */
 +	private synchronized void showPage(PDFPage page) {
 +		// stop drawing the previous page
 +		if (this.currentPage != null && this.prevSize != null) {
 +			this.currentPage.stop(this.prevSize.width, this.prevSize.height, null);
 +		}
 +
 +		boolean newPage = false;
 +		// set up the new page
 +		if (this.page > this.numPages)
 +		{
 +			// New last page - use old last page as template
 +			this.currentPage = this.pdf.getPage(this.numPages);
 +			newPage = true;
 +		}
 +		else
 +			this.currentPage = page;
 +
 +
 +		if (this.currentPage == null) {
 +			// no page
 +			this.currentImage = null;
 +			this.currentXform = null;
 +			repaint();
 +		} else {
 +			// start drawing
 +			Dimension sz = getSize();
 +			if (sz.width + sz.height == 0) {
 +				// no image to draw.
 +				return;
 +			}
 +
 +			Dimension pageSize = this.currentPage.getUnstretchedSize(sz.width, sz.height,
 +					null);
 +
 +			// get the new image
 +			if (newPage)
 +			{
 +				this.currentImage = new BufferedImage(pageSize.width, pageSize.height, BufferedImage.TYPE_INT_RGB);
 +				Graphics g = this.currentImage.getGraphics();
 +				g.setColor(Color.WHITE);
 +				g.fillRect(0, 0, pageSize.width, pageSize.height);
 +			}
 +			else
 +				this.currentImage = this.currentPage.getImage(pageSize.width, pageSize.height,
 +						null, this);
 +
 +			// calculate the transform from page to screen space
 +			this.currentXform = new AffineTransform(1, 0, 0, -1, 0, pageSize.height);
 +			Rectangle2D clip = this.currentPage.getBBox();
 +			double scaleX = pageSize.width / clip.getWidth();
 +			double scaleY = pageSize.height / clip.getHeight();
 +			this.currentXform.scale(scaleX, scaleY);
 +			this.currentXform.translate(-clip.getMinX(), -clip.getMinY());
 +
 +			if (this.sigPagePos != null)
 +				this.sigScreenPos = this.currentXform.transform(this.sigPagePos, this.sigScreenPos);
 +			this.sigScreenWidth = (int) Math.round(this.sigPageWidth * this.currentXform.getScaleX());
 +			this.sigScreenHeight = (int) Math.round(this.sigPageHeight * this.currentXform.getScaleX());
 +
 +			// invert the transform (screen to page space)
 +			try {
 +				this.currentXform = this.currentXform.createInverse();
 +			} catch (NoninvertibleTransformException nte) {
 +				log.error("Error inverting page transform!", nte); //$NON-NLS-1$
 +			}
 +
 +			if (this.sigPagePos == null)
 +			{
 +				this.sigScreenPos = new Point2D.Double(
 +						clamp((int) (pageSize.getWidth() / 2), 0, this.currentImage.getWidth(null) - this.sigScreenWidth),
 +						clamp((int) ((pageSize.getHeight() / 4) * 3), 0, this.currentImage.getHeight(null) - this.sigScreenHeight));
 +				this.sigPagePos = this.currentXform.transform(this.sigScreenPos, this.sigPagePos);
 +			}
 +			else
 +				updateSigPos((int) this.sigScreenPos.getX(), (int) this.sigScreenPos.getY());
 +
 +			this.prevSize = pageSize;
 +
 +			repaint();
 +		}
 +	}
 +
 +	/**
 +	 * Draw the image.
 +	 */
 +	@Override
 +	public void paint(Graphics g) {
 +		Dimension sz = getSize();
 +		g.setColor(getBackground());
 +		g.fillRect(0, 0, getWidth(), getHeight());
 +		if (this.currentImage == null) {
 +			g.setColor(Color.black);
 +			g.drawString(Messages.getString("error.SignaturePanel.NoPage"), getWidth() / 2 - 30, //$NON-NLS-1$
 +					getHeight() / 2);
 +			if (this.currentPage != null) {
 +				showPage(this.currentPage);
 +			}
 +		} else {
 +			// draw the image
 +			int imwid = this.currentImage.getWidth(null);
 +			int imhgt = this.currentImage.getHeight(null);
 +
 +			// draw it centered within the panel
 +			this.offX = (sz.width - imwid) / 2;
 +			this.offY = (sz.height - imhgt) / 2;
 +
 +			if ((imwid == sz.width && imhgt <= sz.height)
 +					|| (imhgt == sz.height && imwid <= sz.width)) {
 +
 +				// draw document
 +				g.drawImage(this.currentImage, this.offX, this.offY, this);
 +
 +				// draw signature
 +				int sigX = (int) (this.offX + this.sigScreenPos.getX());
 +				int sigY = (int) (this.offY + this.sigScreenPos.getY());
 +				if (this.sigPlaceholder == null) {
 +					g.setColor(Color.red);
 +					g.drawRect(sigX, sigY, 100, 40);
 +				}
 +				else {
 +					if (
 +							((this.sigScreenWidth != this.prevSigScreenWidth) ||
 +							(this.sigScreenHeight != this.prevSigScreenHeight) ||
 +							(this.sigPlaceholderTransparency != this.prevSigPlaceholderTransparency)) &&
 +							((this.sigScreenWidth != 0) && (this.sigScreenHeight != 0)))
 +					{
 +						// redraw scaled transparent placeholder
 +						this.prevSigScreenWidth = this.sigScreenWidth;
 +						this.prevSigScreenHeight = this.sigScreenHeight;
 +						this.prevSigPlaceholderTransparency = this.sigPlaceholderTransparency;
 +						Image placeholder = this.sigPlaceholder.getScaledInstance(
 +								this.sigScreenWidth, this.sigScreenHeight, Image.SCALE_SMOOTH);
 +
 +						//Make placeholder transparent
 +						this.sigPlaceholderScaled = new BufferedImage(this.sigScreenWidth, this.sigScreenHeight, BufferedImage.TYPE_INT_ARGB);
 +						Graphics g_phs = this.sigPlaceholderScaled.getGraphics();
 +						g_phs.drawImage(placeholder, 0, 0, null);
 +
 +						// Draw grey "Signature" overlay
 +						String overlay = Messages.getString("positioning.signature"); //$NON-NLS-1$
 +						// Voodoo to get overlay font scale
 +						float scale = (((float) this.sigScreenWidth) / this.sigPlaceholder.getWidth(null)) * 150;
 +						g_phs.setFont(getFont().deriveFont(scale));
 +						g_phs.setColor(this.sigPlaceholderBorderColor);
 +						Rectangle2D overlay_size = g_phs.getFontMetrics().getStringBounds(overlay, g_phs);
 +						int width = (int) overlay_size.getWidth();
 +						int height = (int) overlay_size.getHeight();
 +						int x = this.sigScreenWidth / 2 - width / 2;
 +						int y = this.sigScreenHeight / 2 - height / 2 + g_phs.getFontMetrics().getAscent();
 +						g_phs.drawString(overlay, x, y);
 +
 +						g_phs.dispose();
 +						int[] phpixels = new int[this.sigScreenWidth * this.sigScreenHeight];
 +						phpixels = this.sigPlaceholderScaled.getRGB(0, 0, this.sigScreenWidth, this.sigScreenHeight, phpixels, 0, this.sigScreenWidth);
 +						for (int i = 0; i < phpixels.length; ++i) {
 +							Color c = new Color(phpixels[i]);
 +							c = new Color(c.getRed(), c.getGreen(), c.getBlue(), this.sigPlaceholderTransparency);
 +							phpixels[i] = c.getRGB();
 +						}
 +						this.sigPlaceholderScaled.setRGB(0, 0, this.sigScreenWidth, this.sigScreenHeight, phpixels, 0, this.sigScreenWidth);
 +					}
 +					g.drawImage(this.sigPlaceholderScaled, sigX, sigY, null);
 +					g.setColor(this.sigPlaceholderBorderColor);
 +					g.drawRect(sigX, sigY, this.sigScreenWidth-1, this.sigScreenHeight-1);
 +				}
 +
 +			} else {
 +				// the image is bogus. try again, or give up.
 +				if (this.currentPage != null) {
 +					showPage(this.currentPage);
 +				}
 +				g.setColor(Color.black);
 +				g.drawString(Messages.getString("error.SignaturePanel.NoRender"), getWidth() / 2 - 30, //$NON-NLS-1$
 +						getHeight() / 2);
 +			}
 +		}
 +	}
 +
 +	/**
 +	 * Handles notification of the fact that some part of the image changed.
 +	 * Repaints that portion.
 +	 * 
 +	 * @return true if more updates are desired.
 +	 */
 +	@Override
 +	public boolean imageUpdate(Image img, int infoflags, int x, int y,
 +			int width, int height) {
 +		if ((infoflags & (SOMEBITS | ALLBITS)) != 0) {
 +			repaint(x + this.offX, y + this.offY, width, height);
 +		}
 +		return ((infoflags & (ALLBITS | ERROR | ABORT)) == 0);
 +	}
 +
 +	private MouseAdapter mouseListener = new MouseAdapter() {
 +
 +		private boolean doDrag = false;
 +		private int dragXOffset = 0;
 +		private int dragYOffset = 0;
 +
 +		private void updateSigPos(int sigx, int sigy) {
 +			SignaturePanel.this.updateSigPos(
 +					sigx - SignaturePanel.this.offX,
 +					sigy - SignaturePanel.this.offY);
 +		}
 +
 +		/** Handles a mouseMoved event */
 +		@Override
 +		public void mouseMoved(MouseEvent evt) {
 +			try {
 +				boolean onSig = isOnSignature(evt.getX(), evt.getY());
 +				setCursor(onSig ? Cursors.HAND : Cursors.DEFAULT);
 +			} catch (NullPointerException e) {
 +				// do nothing
 +			}
 +		}
 +
 +		/** Handles a mousePressed event */
 +		@Override
 +		public void mousePressed(MouseEvent evt) {
 +			if (evt.getButton() == MouseEvent.BUTTON1)
 +			{
 +				this.doDrag = true;
 +				if (isOnSignature(evt.getX(), evt.getY())) {
 +					this.dragXOffset = (int)
 +							(SignaturePanel.this.sigScreenPos.getX() -
 +							(evt.getX() - SignaturePanel.this.offX));
 +					this.dragYOffset = (int)
 +							(SignaturePanel.this.sigScreenPos.getY() -
 +							(evt.getY() - SignaturePanel.this.offY));
 +				}
 +				else {
 +					this.dragXOffset = 0;
 +					this.dragYOffset = 0;
 +				}
 +				updateSigPos(evt.getX() + this.dragXOffset, evt.getY() + this.dragYOffset);
 +				setCursor(Cursors.MOVE);
 +			}
 +		}
 +
 +		/** Handles a mouseReleased event */
 +		@Override
 +		public void mouseReleased(MouseEvent evt) {
 +			this.doDrag = false;
 +			boolean onSig = isOnSignature(evt.getX(), evt.getY());
 +			setCursor(onSig ? Cursors.HAND : Cursors.DEFAULT);
 +		}
 +
 +		/**
 +		 * Handles a mouseDragged event.
 +		 */
 +		@Override
 +		public void mouseDragged(MouseEvent evt) {
 +			if (this.doDrag)
 +				updateSigPos(evt.getX() + this.dragXOffset, evt.getY() + this.dragYOffset);
 +		}
 +	};
 +
 +	/**
 +	 * Sets the mouse cursor
 +	 * @param cursor cursor to set
 +	 */
 +	void setCursor(Cursors cursor)
 +	{
 +		if (this.currentCursor == cursor)
 +			return;
 +		this.currentCursor = cursor;
 +		Cursor cur = null;
 +		switch (cursor) {
 +			case DEFAULT:
 +				cur = this.defaultCursor;
 +				break;
 +			case HAND:
 +				cur = this.handCursor;
 +				break;
 +			case MOVE:
 +				cur = this.moveCursor;
 +				break;
 +		}
 +		this.getParent().setCursor(cur);
 +	}
 +
 +	/**
 +	 * Check whether given point is on signature placeholder
 +	 * @param x x coordinate
 +	 * @param y y coordinate
 +	 * @return true if given point is on signature placeholder
 +	 */
 +	boolean isOnSignature(int x, int y)
 +	{
 +		Rectangle2D sig = new Rectangle2D.Double(
 +				this.sigScreenPos.getX() + this.offX,
 +				this.sigScreenPos.getY() + this.offY,
 +				this.sigScreenWidth,
 +				this.sigScreenHeight);
 +		Point2D pos = new Point2D.Double(x, y);
 +		return (sig.contains(pos));
 +	}
 +
 +	/**
 +	 * Update the signature placeholder position
 +	 * @param sigx X position on the document (screen coordinates)
 +	 * @param sigy Y position on the document (screen coordinates)
 +	 */
 +	void updateSigPos(int sigx, int sigy) {
 +		if (this.currentImage == null)
 +			return;
 +		sigx = clamp(sigx, 0, this.currentImage.getWidth(null) - this.sigScreenWidth);
 +		sigy = clamp(sigy, 0, this.currentImage.getHeight(null) - this.sigScreenHeight);
 +		this.sigScreenPos = new Point2D.Double(sigx, sigy);
 +		this.sigPagePos = this.currentXform.transform(this.sigScreenPos, this.sigPagePos);
 +		repaint();
 +	}
 +
 +	/**
 +	 * Clamp x to be within [min-max]
 +	 * @param x int to clamp
 +	 * @param min minimum value
 +	 * @param max maximum value
 +	 * @return clamped x
 +	 */
 +	static int clamp(int x, int min, int max)
 +	{
 +		if (x < min)
 +			x = min;
 +		else if (x > max)
 +			x = max;
 +		return x;
 +	}
 +}
 | 
