/**
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info:  http://www.jpedal.org
* (C) Copyright 1997-2008, 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
*
* ---------------
* PdfPanel.java
* ---------------
*/
package org.jpedal;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.dnd.DropTarget;
import java.awt.event.MouseEvent;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Map;
import javax.swing.border.Border;
import org.jpedal.io.ObjectStore;
import org.jpedal.objects.PdfPageData;
//<start-jfr>
//<start-adobe>
import org.jpedal.objects.PageLines;
//<end-adobe>
import org.jpedal.objects.PdfData;
import org.jpedal.objects.PrinterOptions;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.layers.PdfLayerList;
import org.jpedal.objects.acroforms.rendering.AcroRenderer;
//<end-jfr>
import org.jpedal.render.DynamicVectorRenderer;
import org.jpedal.utils.repositories.Vector_Int;
import org.jpedal.utils.repositories.Vector_Rectangle;
import org.jpedal.utils.repositories.Vector_Shape;
import javax.swing.*;
/**
 * Do not create an instance of this class - provides GUI functionality for
 * PdfDecoder class to extend
 */
public class PdfPanel extends JPanel{
	private static final long serialVersionUID = -5480323101993399978L;
	
	/** Not part of the JPedal API - Required for Storypad */
	public JPanel[] extraButton;
	public boolean useParentButtons = false;
    protected PdfLayerList layers;    
    /** Holds the x,y,w,h of the current highlighted image, null if none */
	int[] highlightedImage = null;
	
	/** Enable / Disable Point and Click image extraction */
	private boolean ImageExtractionAllowed = true;
	//<start-jfr>
	protected Display pages;
	/** holds the extracted AcroForm data */
	//protected PdfFormData currentAcroFormData;
    //PdfArrayIterator fieldList=null;
	/**default renderer for acroforms*/
	protected AcroRenderer formRenderer;//=new DefaultAcroRenderer();
	/**hotspots for display*/
	//Hotspots displayHotspots;
	/**hotspots for printing*/
	//Hotspots printHotspots;
	//<start-adobe>
	/**holds page lines*/
	protected PageLines pageLines;
	
	
	//<end-adobe>
	//<end-jfr>
	/** 
	 * The colour of the highlighting box around the text
	 */
	public static Color highlightColor = new Color(10,100,170);
	
	/** 
	 * The colour of the text once highlighted
	 */
	public static Color backgroundColor = null;
	/** 
	 * The transparency of the highlighting box around the text stored as a float
	 */
	public static float highlightComposite = 0.35f;
    protected Rectangle[] alternateOutlines;
	String altName;
	/**tracks indent so changing to continuous does not disturb display*/
	private int lastIndent=-1;
	//<start-jfr>
	PageOffsets currentOffset;
	/**copy of flag to tell program whether to create
	 * (and possibly update) screen display
	 */
	protected boolean renderPage = false;
	/**type of printing*/
	protected boolean isPrintAutoRotateAndCenter=false;
	/**flag to show we use PDF page size*/
	protected boolean usePDFPaperSize=false;
	/**page scaling mode to use for printing*/
	protected int pageScalingMode=PrinterOptions.PAGE_SCALING_REDUCE_TO_PRINTER_MARGINS;
	//<end-jfr>
	/**display mode (continuous, facing, single)*/
	protected int displayView=Display.SINGLE_PAGE;
	/**amount we scroll screen to make visible*/
	private int scrollInterval=10;
	/** count of how many pages loaded */
	protected int pageCount = 0;
	/**
	 * if true
	 * show the crop box as a box with cross on it
	 * and remove the clip.
	 */
	private boolean showCrop = false;
	/** when true setPageParameters draws the page rotated for use with scale to window */
    boolean isNewRotationSet=false;
	/** displays the viewport border */
	protected boolean displayViewportBorder=false;
	/**flag to stop multiple attempts to decode*/
	protected boolean isDecoding=false;
	protected int alignment=Display.DISPLAY_LEFT_ALIGNED;
	/** used by setPageParameters to draw rotated pages */
	protected int displayRotation=0;
	/**current cursor location*/
	private Point current_p;
	/**allows user to create viewport on page and scale to this*/
	protected Rectangle viewableArea=null;
	/**shows merging for debugging*/
	private Vector_Int merge_level ;
	private Vector_Shape merge_outline;
	private boolean[] showDebugLevel;
	private Color[] debugColors;
	private boolean showMerging=false;
	/**used to draw demo cross*/
	AffineTransform demoAf=null;
	/**repaint manager*/
	private RepaintManager currentManager=RepaintManager.currentManager(this);
	/**current page*/
	protected int pageNumber=1;
	/**used to reduce or increase image size*/
	protected AffineTransform displayScaling;
	/**
	 * used to apply the imageable area to the displayscaling, used instead of
	 * displayScaling, as to preserve displayScaling
	 */
	protected AffineTransform viewScaling=null;
	/** holds page information used in grouping*/
	protected PdfPageData pageData = new PdfPageData();
	/**used to track highlight*/
	private Rectangle lastHighlight=null;
	/**rectangle drawn on screen by user*/
	protected Rectangle cursorBoxOnScreen = null,lastCursorBoxOnScreen=null;
	/** whether the cross-hairs are drawn */
	private boolean drawCrossHairs = false;
	/** which box the cursor is currently positioned over */
	private int boxContained = -1;
	/** color to highlight selected handle */
	private Color selectedHandleColor = Color.red;
	/** the gap around each point of reference for cursorBox */
	private int handlesGap = 5;
	/**colour of highlighted rectangle*/
	private Color outlineColor;
	/**rectangle of object currently under cursor*/
	protected Rectangle currentHighlightedObject = null;
	/**colour of a shape we highlight on the page*/
	private Color outlineHighlightColor;
	/**preferred colour to highliht page*/
	private Color[] highlightColors;
	/**gap around object to repaint*/
	static final private int strip=2;
	/**highlight around selected area*/
	private Rectangle2D[] outlineZone = null;
	private int[] processedByRegularExpression=null;
	/**allow for inset of display*/
	protected int insetW=0,insetH=0;
	/**flag to show if area selected*/
	private boolean[] highlightedZonesSelected = null;
	private boolean[] hasDrownedObjects = null;
	/**user defined viewport*/
	Rectangle userAnnot=null;
	/** default height width of bufferedimage in pixels */
	private int defaultSize = 100;
	/**height of the BufferedImage in pixels*/
    int y_size = defaultSize;
	/**unscaled page height*/
    int max_y;
	
	/**unscaled page Width*/
    int max_x;
	/**width of the BufferedImage in pixels*/
    int x_size = defaultSize;
	/**used to plot selection*/
	int[] cx=null,cy=null;
	/**any scaling factor being used to convert co-ords into correct values
	 * and to alter image size
	 */
	protected float scaling=1;
	/**mode for showing outlines*/
	private int highlightMode = 0;
	/**flag for showing all object outlines in PDFPanel*/
	public static final int SHOW_OBJECTS = 1;
	/**flag for showing all lines on page used for grouping */
	public static final int SHOW_LINES = 2;
	/**flag for showing all lines on page used for grouping */
	public static final int SHOW_BOXES = 4;
	/**size of font for selection order*/
	protected int size=20;
	/**font used to show selection order*/
	protected Font highlightFont=null;
	/**border for component*/
	protected Border myBorder=null;
	protected DropTarget dropTarget = null;
	/** the ObjectStore for this file */
	protected ObjectStore objectStoreRef = new ObjectStore();
	/**the actual display object*/
	protected DynamicVectorRenderer currentDisplay=new DynamicVectorRenderer(1,objectStoreRef,false); //
	/**flag to show if border appears on printed output*/
	protected boolean useBorder=true;
	private int[] selectionOrder;
	/**stores area of arrays in which text should be highlighted*/
	private Rectangle[] areas;
	private int[] areaDirection;
	private Object[] linkedItems,children;
	private int[] parents;
	protected boolean useAcceleration=true;
	/**all text blocks as a shape*/
	private Shape[] fragmentShapes;
	int x_size_cropped;
	int y_size_cropped;
	private AffineTransform cursorAf;
	private Rectangle actualBox;
	private boolean drawInteractively=false;
	protected int lastFormPage=-1,lastStart=-1,lastEnd=-1;
	private int pageUsedForTransform;
	protected int additionalPageCount=0,xOffset=0;
	private boolean displayForms = true;
	//private GraphicsDevice currentGraphicsDevice = null;
	public boolean extractingAsImage = false;
	private int highlightX = 0;
	private int highlightY = 0;
	
	public void setExtractingAsImage(boolean extractingAsImage) {
		this.extractingAsImage = extractingAsImage;
	}
	
	//<start-adobe>
	public void initNonPDF(PdfDecoder pdf){
		pages=new SingleDisplay(pageNumber,pageCount,currentDisplay);
		pages.setup(true,null, pdf);
	}
	/**workout combined area of shapes are in an area*/
	public  Rectangle getCombinedAreas(Rectangle targetRectangle,boolean justText){
		if(this.currentDisplay!=null)
			return currentDisplay.getCombinedAreas(targetRectangle, justText);
		return
		null;
	}
	/**
	 * put debugging info for grouping onscreen
	 * to aid in developing and debugging merging algorithms used by Storypad -
	 * (NOT PART OF API and subject to change)
	 */
	final public void addMergingDisplayForDebugging(Vector_Int merge_level,
			Vector_Shape merge_outline,int count,Color[] colors) {
		this.merge_level=merge_level;
		this.merge_outline=merge_outline;
		this.showDebugLevel=new boolean[count];
		this.debugColors=colors;
	}
	/**
	 * debug zone for Storypad - not part of API
	 */
	final public void setDebugView(int level, boolean enabled){
		if(showDebugLevel!=null)
			showDebugLevel[level]=enabled;
	}
	//<end-adobe>
	/**
	 * get pdf as Image of current page with height as required height in pixels -
	 * Page must be decoded first - used to generate thubnails for display.
	 * Use decodePageAsImage forcreating images of pages
	 */
	final public BufferedImage getPageAsThumbnail(int height,DynamicVectorRenderer currentDisplay) {
		if(currentDisplay==null){
			currentDisplay=this.currentDisplay;
			/**
			 * save to disk
			 */
			ObjectStore.cachePage(new Integer(pageNumber), currentDisplay);
		}
		BufferedImage image = getImageFromRenderer(height,currentDisplay,pageNumber);
		return image;
	}
	/**
	 */
	protected BufferedImage getImageFromRenderer(int height,DynamicVectorRenderer rend,int pageNumber) {
		//int mediaBoxW = pageData.getMediaBoxWidth(pageNumber);
		int mediaBoxH = pageData.getMediaBoxHeight(pageNumber);
		int mediaBoxX = pageData.getMediaBoxX(pageNumber);
		int mediaBoxY = pageData.getMediaBoxY(pageNumber);
		int crw=pageData.getCropBoxWidth(pageNumber);
		int crh=pageData.getCropBoxHeight(pageNumber);
		int crx=pageData.getCropBoxX(pageNumber);
		int cry=pageData.getCropBoxY(pageNumber);
		if(cry>0)
			cry=mediaBoxH-crh-cry;
		float scale=(float) height/(crh);
		int rotation=pageData.getRotation(pageNumber);
		/**allow for rotation*/
		int dr=-1;
		if((rotation==90)|(rotation==270)){
			int tmp=crw;
			crw=crh;
			crh=tmp;
			dr=1;
			tmp=crx;
			crx=cry;
			cry=tmp;
		}
		AffineTransform scaleAf = getScalingForImage(pageNumber,rotation,scale);//(int)(mediaBoxW*scale), (int)(mediaBoxH*scale),
		int cx=mediaBoxX-crx,cy=mediaBoxY-cry;
		scaleAf.translate(cx,dr*cy);
		return rend.getPageAsImage(scale,crx,cry,crw,crh,pageNumber,scaleAf,BufferedImage.TYPE_INT_RGB);
	}
	//<start-adobe>
	/**set zones we want highlighted onscreen
	 * @deprecated
	 * please look at  setFoundTextAreas(Rectangle areas),setHighlightedAreas(Rectangle[] areas)
	 * <b>This is NOT part of the API</b> (used in Storypad)
	 */
	final public void setHighlightedZones(
			int mode,
			int[] cx,int[] cy,
			Shape[] fragmentShapes,
			Object[] linkedItems,
			int[] parents,
			Object[] childItems,
			int[] childParents,
			Rectangle2D[] outlineZone,
			boolean[] highlightedZonesSelected,boolean[] hasDrownedObjects,Color[] highlightColors,int[] selectionOrder,int[] processedByRegularExpression) {
		this.cx=cx;
		this.cy=cy;
		this.fragmentShapes=fragmentShapes;
		this.linkedItems=linkedItems;
		this.parents=parents;
		this.children=childItems;
		this.outlineZone = outlineZone;
		this.processedByRegularExpression=processedByRegularExpression;
		this.highlightedZonesSelected = highlightedZonesSelected;
		this.hasDrownedObjects = hasDrownedObjects;
		this.highlightMode = mode;
		this.highlightColors=highlightColors;
		this.selectionOrder=selectionOrder;
		//and deselect alt highlights
		this.alternateOutlines=null;
	}
	/**set merging option for Storypad (not part of API)*/
	public void setDebugDisplay(boolean isEnabled){
		this.showMerging=isEnabled;
	}
	/**
	 * set an inset display so that display will not touch edge of panel*/
	final public void setInset(int width,int height) {
		this.insetW=width;
		this.insetH=height;
	}
	/**
	 * make screen scroll to ensure point is visible
	 */
	public void ensurePointIsVisible(Point p){
		super.scrollRectToVisible(new Rectangle(p.x,y_size-p.y,scrollInterval,scrollInterval));
	}
	//<end-adobe>
	/**
	 * get sizes of panel <BR>
	 * This is the PDF pagesize (as set in the PDF from pagesize) -
	 * It now includes any scaling factor you have set (ie a PDF size 800 * 600
	 * with a scaling factor of 2 will return 1600 *1200)
	 */
	final public Dimension getMaximumSize() {
		Dimension pageSize=null;
		if(displayView!=Display.SINGLE_PAGE)
			pageSize = pages.getPageSize(displayView);
		if(pageSize==null){
			if((displayRotation==90)|(displayRotation==270))
				pageSize= new Dimension((int)(y_size_cropped+insetW+insetW+(xOffset*scaling)+(additionalPageCount*(insetW+insetW))),x_size_cropped+insetH+insetH);
			else
				pageSize= new Dimension((int)(x_size_cropped+insetW+insetW+(xOffset*scaling)+(additionalPageCount*(insetW+insetW))),y_size_cropped+insetH+insetH);
		}
        if(pageSize==null)
        pageSize=getMinimumSize();
        return pageSize;
	}
	/**
	 * get width*/
	final public Dimension getMinimumSize() {
		return new Dimension(100+insetW,100+insetH);
	}
	/**
	 * get sizes of panel <BR>
	 * This is the PDF pagesize (as set in the PDF from pagesize) -
	 * It now includes any scaling factor you have set (ie a PDF size 800 * 600
	 * with a scaling factor of 2 will return 1600 *1200)
	 */
	public Dimension getPreferredSize() {
		return getMaximumSize();
	}
	
	public Rectangle[] getHighlightedAreas(){
		if(areas==null)
			return null;
		else{
			int count=areas.length;
			Rectangle[] returnValue=new Rectangle[count];
			for(int ii=0;ii<count;ii++){
				if(areas[ii]==null)
					returnValue[ii]=null;
				else
					returnValue[ii]=new Rectangle(areas[ii].x,areas[ii].y,
							areas[ii].width,areas[ii].height);
			}
			return returnValue;
		}
	}
	
	/**
	 * Highlights a section of lines that form a paragraph
	 */
	public Rectangle setFoundParagraph(int x, int y){
		Rectangle[] lines = PdfHighlights.getLineAreas();
		if(lines!=null){
			Rectangle point = new Rectangle(x,y,1,1);
			Rectangle current = new Rectangle(0,0,0,0);
			boolean lineFound = false;
			int selectedLine = 0;
			for(int i=0; i!=lines.length; i++){
				if(lines[i].intersects(point)){
					selectedLine = i;
					lineFound = true;
					break;
				}
			}
			if(lineFound){
				double left = lines[selectedLine].x;
				double cx = lines[selectedLine].getCenterX();
				double right = lines[selectedLine].x+lines[selectedLine].width;
				double cy = lines[selectedLine].getCenterY();
				int h = lines[selectedLine].height;
				current.x=lines[selectedLine].x;
				current.y=lines[selectedLine].y;
				current.width=lines[selectedLine].width;
				current.height=lines[selectedLine].height;
				boolean foundTop = true;
				boolean foundBottom = true;
				Vector_Rectangle selected = new Vector_Rectangle(0);
				selected.addElement(lines[selectedLine]);
				while(foundTop){
					foundTop = false;
					for(int i=0; i!=lines.length; i++){
						if(lines[i].contains(left, cy+h) || lines[i].contains(cx, cy+h) || lines[i].contains(right, cy+h)){
							selected.addElement(lines[i]);
							foundTop = true;
							cy = lines[i].getCenterY();
							h = lines[i].height;
							if(current.x>lines[i].x){
								current.width = (current.x+current.width)-lines[i].x;
								current.x = lines[i].x;
							}
							if((current.x+current.width)<(lines[i].x+lines[i].width))
								current.width = (lines[i].x+lines[i].width)-current.x;
							if(current.y>lines[i].y){
								current.height = (current.y+current.height)-lines[i].y;
								current.y = lines[i].y;
							}
							if((current.y+current.height)<(lines[i].y+lines[i].height)){
								current.height = (lines[i].y+lines[i].height)-current.y;
							}
							break;
						}
					}
				}
				//Return to selected item else we have duplicate highlights
				left = lines[selectedLine].x;


Answer: 				cx = lines[selectedLine].getCenterX();