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

org.jpedal.objects.acroforms.GUIData Maven / Gradle / Ivy

There is a newer version: 20151002
Show newest version
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2016 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * GUIData.java
 * ---------------
 */
package org.jpedal.objects.acroforms;

import java.awt.Rectangle;
import java.util.*;
import org.jpedal.external.CustomFormPrint;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.acroforms.creation.FormFactory;
import org.jpedal.objects.layers.PdfLayerList;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfDictionary;

/**holds all data not specific to Swing/SWT/ULC*/
public abstract class GUIData {
    
    /**
     * flag to make forms draw as images, not swing components
     */
    protected boolean rasterizeForms;
   
    /** for if we add a popup to the panel, could be used for adding other objects*/
    protected boolean forceRedraw;
    
    //allow user to move relative draw position
    protected int userX, userY,widestPageNR,widestPageR;
    
    /** the current display view, Display.SINGLE, Display.CONTINOUS etc */
    protected int displayView;
    
    public float dpi=72.0f;

    protected final Map rawFormData=new HashMap();
    
    /**allow user to set Forms to ignore*/
    protected Map componentsToIgnore=new HashMap();
    
    protected int insetW,insetH;
    
    protected PdfPageData pageData;
    
    /**
     * local copy needed in rendering
     */
    protected int indent;
    
    protected int[] cropOtherY;
    /**
     * track page scaling
     */
    protected float displayScaling;
    protected int rotation;
    
    /**
     * used to only redraw as needed
     */
    protected float lastScaling = -1, oldRotation, oldIndent;
    
    /**
     * used for page tracking
     */
    protected int startPage, currentPage, endPage;

    /**
     * used to draw pages offset if not in SINGLE_PAGE mode
     */
    protected int[] xReached, yReached;
    
    /** stores the forms in there original order, accessable by page */
    protected List[] formsUnordered, formsOrdered;
    
    /**
     * array to hold page for each component so we can scan quickly on page change
     */
    private int formCount;
    protected PdfLayerList layers;
    
    protected FormFactory formFactory;
    
    public void setLayerData(final PdfLayerList layers){
	this.layers=layers;
    }
    
    public void setRasterizeForms(final boolean inlineForms) {
        rasterizeForms = inlineForms;
    } 
    
    protected void setListForPage(final int page, final List comps, final boolean isSorted){
        if(isSorted){
	    formsOrdered[page] = comps;
	}else{
	    formsUnordered[page] = comps;
	}
    }
    
    protected Object checkGUIObjectResolved(final FormObject formObject){

        Object comp=null;
        if(formObject!=null){
            comp= formObject.getGUIComponent();
        }
        
        if(formObject!=null && comp==null){

            comp= resolveGUIComponent(formObject);

            if(comp!=null){
                setGUIComp(formObject, comp);
            }
        }

        return comp;
    }
    
    
    protected Object resolveGUIComponent(final FormObject formObject) {

        Object retComponent=null;

        final int subtype=formObject.getParameterConstant(PdfDictionary.Subtype);
        final int formType=formObject.getNameAsConstant(PdfDictionary.FT);//FT

        final int formFactoryType=formFactory.getType();

		//ExternalHandlers.isXFAPresent() will only be true in forms version of PDF2HTML
        if(!ExternalHandlers.isXFAPresent() && (formFactoryType==FormFactory.HTML || formFactoryType==FormFactory.SVG)){
             
            if(subtype!=PdfDictionary.Widget && AcroRenderer.isAnnotation(formObject)){                
                retComponent = formFactory.annotationButton(formObject);
            }
            
        }else if(formType==-1){
            retComponent = formFactory.annotationButton(formObject);
        } else {
            retComponent = getFormComponent(formObject, formType, retComponent);
        }
		
        if (retComponent != null) {
            formObject.setGUIComponent(retComponent,formFactory.getType());
            setGUIComp(formObject, retComponent);
        }

        return retComponent;

    }

    private Object getFormComponent(final FormObject formObject, final int formType, Object retComponent) {
        
        final boolean[] flags = formObject.getFieldFlags();//Ff
        
        switch (formType) {
            case PdfDictionary.Btn:
                boolean isPushButton = false, isRadio = false;// hasNoToggleToOff = false, radioinUnison = false;
                if (flags != null) {
                    isPushButton = flags[FormObject.PUSHBUTTON_ID];
                    isRadio = flags[FormObject.RADIO_ID];
                }
                
                if (isPushButton) {
                    retComponent = formFactory.pushBut(formObject);
                    
                }else if(isRadio){
                    retComponent = formFactory.radioBut(formObject);
                }else {
                    retComponent = formFactory.checkBoxBut(formObject);
                }
                break;
                
            case PdfDictionary.Tx:
                boolean isMultiline = false, hasPassword = false;// doNotScroll = false, richtext = false, fileSelect = false, doNotSpellCheck = false;
                if (flags != null) {
                    isMultiline = flags[FormObject.MULTILINE_ID] || (formObject.getTextString()!=null && formObject.getTextString().indexOf('\n')!=-1);
                    hasPassword = flags[FormObject.PASSWORD_ID];
                }   
                
                if (isMultiline) {
                    if (hasPassword) {
                        retComponent = formFactory.multiLinePassword(formObject);
                    } else {
                        retComponent = formFactory.multiLineText(formObject);
                    }
                } else {//singleLine
                    if (hasPassword) {
                        retComponent = formFactory.singleLinePassword(formObject);
                    } else {
                        retComponent = formFactory.singleLineText(formObject);
                    }
                }   
                break;
                
            case PdfDictionary.Ch: 
                boolean isCombo = false;// multiSelect = false, sort = false, isEditable = false, doNotSpellCheck = false, comminOnSelChange = false;
                if (flags != null) {
                    isCombo = flags[FormObject.COMBO_ID];
                }   
                
                if (isCombo) {// || (type==XFAFORM && ((XFAFormObject)formObject).choiceShown!=XFAFormObject.CHOICE_ALWAYS)){
                    retComponent = formFactory.comboBox(formObject);
                } else {//it is a list                   
                    retComponent = formFactory.listField(formObject);
                }   
                break;
                
            case PdfDictionary.Sig:
                retComponent = formFactory.signature(formObject);
                break;
        }
        return retComponent;
    }

    public void dispose(){
    	//Nothing to dispose of in generic method, overrode by swing version
    }
    
    protected abstract void displayComponent( final FormObject formObject, final Object comp);
    
    /**
     * put components onto screen display
     * @param startPage
     * @param endPage
     */
    protected void displayComponents(final int startPage, final int endPage) {
        
        
        if (rasterizeForms || formsOrdered==null) {
            return;
        }
        
        this.startPage = startPage;
        this.endPage=endPage;
        
        FormObject formObject;
        Object comp;
        
        //System.out.println("displayComponents "+startPage+" "+endPage+" "+SwingUtilities.isEventDispatchThread());
        
        if(startPage>1) {
            removeHiddenForms(1, startPage);
        }
        
        /*
         * forms currently visible
         */
        for (int page = startPage; page < endPage; page++) {
            
            //get unsorted components and iterate over forms
            if(formsOrdered[page]!=null){
                
                for (final FormObject o : formsOrdered[page]) {
                    
                    if (o != null) {
                        
                        formObject = o;
                        
                        comp= checkGUIObjectResolved(formObject);
                        
                        if (comp != null) {
                            displayComponent(formObject, comp);
                        }
                    }
                }
            }
        }
        
        removeHiddenForms(endPage,pageData.getPageCount()+1);
    }
    
    void removeHiddenForms(final int startPage, final int endPage){
        //comment out as could be called by Canoo
        //throw new UnsupportedOperationException("Not supported yet."); 
    }
    
    public boolean hasformsOnPageDecoded(final int page){
	return formsOrdered!=null && formsOrdered.length>page && formsOrdered[page]!=null;
    }
    
    /**
     * setup values needed for drawing page
     * @param pageData
     * @param page
     */
    protected void initParametersForPage(final PdfPageData pageData, final int page, final FormFactory formFactory, final float dpi){
	
	//ensure setup
	if(cropOtherY==null || cropOtherY.length<=page) {
        this.resetComponents(0, pageData.getPageCount(), false);
    }
	
	final int mediaHeight = pageData.getMediaBoxHeight(page);
	final int cropTop = (pageData.getCropBoxHeight(page) + pageData.getCropBoxY(page));
	
	//take into account crop
	if (mediaHeight != cropTop) {
        cropOtherY[page] = (mediaHeight - cropTop);
    } else {
        cropOtherY[page] = 0;
    }
	
	this.currentPage = page; //track page displayed
	
	this.formFactory = formFactory;
	
	this.dpi=dpi;
    }

    /**
     * used to flush/resize data structures on new document/page
     * @param formCount
     * @param pageCount
     * @param keepValues
     * return true if successful, false if formCount is less than current count.
     */
    public void resetComponents(final int formCount, final int pageCount, final boolean keepValues) {
	
	if(keepValues && this.formCount>formCount) {
        return;
    }
	
	this.formCount=formCount;
	
	if(!keepValues){
	    
            if(formsUnordered==null){
                formsUnordered = new List[pageCount+1];
                formsOrdered = new List[pageCount + 1];
            }
        
	    //reset offsets
	    cropOtherY=new int[pageCount+1];
	    
	    //reset the multi page shifting values so we dont get forms half way across the page on a single page view.
	    xReached = yReached = null;
	    
	}
    }
    
    /**
     * pass in current values used for all components
     * @param scaling
     * @param rotation
     * @param indent
     */
    public void setPageValues(final float scaling, final int rotation, final int indent, final int userX, final int userY, final int displayView, final int widestPageNR, final int widestPageR) {
	
	this.rotation=rotation;
	this.displayScaling=scaling;
	this.indent=indent;
	this.userX=userX;
	this.userY=userY;
	this.displayView = displayView;
	
	this.widestPageNR = widestPageNR;
	this.widestPageR = widestPageR;
    }
    
    /**
     * used to pass in offsets and PdfPageData object so we can access in rendering
     * @param pageData
     * @param insetW
     * @param insetH
     */
    protected void setPageData(final PdfPageData pageData, final int insetW, final int insetH) {
	
	//track inset on page
	this.insetW = insetW;
	this.insetH = insetH;
	
	this.pageData = pageData;
	
    }
    
    /**
     * offsets for forms in multi-page mode
     */
    public void setPageDisplacements(final int[] xReached, final int[] yReached) {
	
	this.xReached = xReached;
	this.yReached = yReached;
	
	//force redraw
	forceRedraw=true;
	
    }
    
    /**
     * force redraw (ie for refreshing layers)
     */
    public void setForceRedraw(final boolean forceRedraw) {
	
	this.forceRedraw=forceRedraw;
	
    }
    
    /**
     * store form data and allow lookup by PDF ref or name
     * (name may not be unique)
     * @param formObject
     */
    protected void storeRawData(final FormObject formObject) {
	
	final String ref=formObject.getObjectRefAsString();
	rawFormData.put(ref,formObject);
	
    }
    
    protected void flushFormData() {
	
	rawFormData.clear();
	
        formsOrdered=null;
        formsUnordered=null;
    
	this.oldIndent=-oldIndent;
	
    }
    
    /**
     * returns the raw formdata so that DefaultAcroRender can access
     */
    protected Map getRawFormData() {
	return Collections.unmodifiableMap(rawFormData);
    }
   
    
    protected abstract void setGUIComp(final FormObject formObject, final Object rawField);

    /**
     * Allow you to get a list of values from the Forms.
     * 
     * If possible we recommend you work with the Form Objects rather than 
     * resolve GUI components unless you need the GUI values
     *
     * null key will return all values
     * 
     * if pageNumber is -1 it will process whole document, otherwise just that page
     * 
     * NO values will return empty list, not null
     */
    public List getFormComponents(final String key, final ReturnValues value, final int pageNumber) {
	
	final Iterator i=rawFormData.keySet().iterator();
	final ArrayList selectedForms=new ArrayList();
	FormObject form;
	boolean isPageSelected;
	String name;
	while(i.hasNext()){
	  
	    form= rawFormData.get(i.next());
	    isPageSelected=pageNumber==-1 || form.getPageNumber()==pageNumber;
	    name=form.getTextStreamValue(PdfDictionary.T);
        
            switch(value){
		case GUI_FORMS_FROM_NAME:
		    if(isPageSelected && (key==null || (name != null && name.equals(key)) )){
			selectedForms.add(checkGUIObjectResolved(form));
		    }
		    break;
		    
		case FORMOBJECTS_FROM_NAME:
		    if (isPageSelected &&(key == null || (name != null && name.equals(key)) )) {
			selectedForms.add(form);
		    }
		    break; 
                    
               case FORMOBJECTS_FROM_REF:
		    if (isPageSelected &&(key == null || form.getObjectRefAsString().equals(key))) {
			selectedForms.add(form);
		    }
		    break;      
		    
		case FORM_NAMES:
		    if (isPageSelected && name!=null && !name.isEmpty() && !selectedForms.contains(name)) {
			selectedForms.add(name);
		    }
		    break;
		    
		default:
		    throw new RuntimeException("value "+ value+" not implemented");
	    }
	}
	
	return selectedForms;
    }

    public void resetAfterPrinting(){
	
	    forceRedraw=true;
    }

    public static int calculateFontSize(final int height, final int width, final boolean area, final String text) {
	
	//our new auto size routine, better size.
	int rawSize;
	final float areaFactor = 0.8f;
	
	final double h1 = height*areaFactor;
	if(text==null || text.isEmpty()) {
        return (int) h1;//12
    }
	
	//work out number of lines and chars on longest line
	final char[] textChrs = text.toCharArray();
	int maxLen = 0,curLen = 0,lines = 1;
	char lastChar=0;
	for (final char textChr : textChrs) {
	    switch (textChr) {
		case 10:
		case 13:
		    if ((textChr == 13 && lastChar == 10) ||
			    (textChr == 10 && lastChar == 13)) {
			//ignore;
		    }
		    lines++;
		    if (maxLen < curLen) {
                maxLen = curLen;
            }
		    curLen = 0;
		    break;
		default:
		    curLen++;
		    break;
	    }
	    lastChar = textChr;
	}
	
	if(maxLenx2*2){
	    rawSize = (int)x2*2;
	}else{
	    if(area && y2>14){
		    double fontsize = 14;
		    while(true){
			final double approxLines = height/fontsize;
			if(approxLines<5) {
                return (int) fontsize;
            } else {
                fontsize *= 1.1;
            }
		    }
		}
	    
	    rawSize = (int)y2;
	}
	
	//make sure its not over max of 14 in a textarea
	//or if we have a silly tiny number (probably flowing text) ignore it
	if(area && (rawSize<4 ||rawSize>14)) {
        rawSize = 12;
    }
	
	return rawSize;
	
    }
    
    public boolean formsRasterizedForDisplay() {
	return this.rasterizeForms;
    }
    
    protected static int getFontSize(final FormObject formObject, final int rotate, final float scale) {
	
	int rawSize = formObject.getTextSize();
	
	if (rawSize == -1) {
        rawSize = 0;//change -1 to best fit so that text is more visible
    }
	
	if (rawSize == 0) {// best fit
	    
	    // work out best size for bounding box of object
	    final Rectangle bounds=formObject.getBoundingRectangle();
	    
	    int width=bounds.width;
	    int height=bounds.height;
	    
	    if (rotate == 90 || rotate == 270) {
		final int tmp = height;
		height = width;
		width = tmp;
	    }
	    
	    rawSize = (int)(height * 0.85);
	    
	    final String textVal = formObject.getTextString();
	    
	    final int formType=formObject.getFormType();
	    
	    // check this code tested and change
	    if (formType==FormFactory.MULTILINETEXT ||  formType==FormFactory.MULTILINEPASSWORD) {
		
		rawSize = calculateFontSize(height, width, true,textVal);
		
	    } else if (formType==FormFactory.SINGLELINETEXT ||  formType==FormFactory.SINGLELINEPASSWORD) {
		
		rawSize = calculateFontSize(height, width, false,textVal);
		
	    } else if (textVal != null) {
		rawSize = calculateFontSize(height, width, false,textVal);
	    }
	}
	
	int size = (int) (rawSize * scale);
	if (size < 1) {
	    size = 1;
	}
	
	return size;
    }

    protected void removeAllComponentsFromScreen() {
        throw new UnsupportedOperationException("removeAllComponentsFromScreen Not supported yet.");
    }

    public void setAutoFontSize(final FormObject formObj) {
        throw new UnsupportedOperationException("setAutoFontSize Not supported yet."); 
    }

     public void renderFormsOntoG2(final Object raw, final int pageIndex, final int currentIndent,
                                  final int currentRotation, final Map componentsToIgnore, final FormFactory formFactory, final int pageHeight) {
        throw new UnsupportedOperationException("renderFormsOntoG2Not supported yet."); 
    }

    public void setCustomPrintInterface(final CustomFormPrint customFormPrint) {
        throw new UnsupportedOperationException("setCustomPrintInterface Not supported yet."); 
    }

    public void resetScaledLocation(final float scaling, final int i, final int i0) {
        throw new UnsupportedOperationException("resetScaledLocation Not supported yet."); 
    }

    public void setRootDisplayComponent(final Object formsPane) {
        throw new UnsupportedOperationException("setRootDisplayComponent Not supported yet."); 
    }

    /**
     * used internally by HTML  drawing code to flatten forms
     * @param isOrdered
     * @return 
     */
    public java.util.List[] getFormList(final boolean isOrdered) {

        if(isOrdered) {
            return this.formsOrdered;
        } else {
            return this.formsUnordered;
        }
    }

    public void renderFormsOntoG2InHeadless(final Object raw, final int pageIndex, final Map componentsToIgnore, final FormFactory formFactory)  {
        throw new UnsupportedOperationException("renderFormsOntoG2InHeadless Not supported yet.");
    }
}