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

org.jpedal.objects.acroforms.creation.SwingFormFactory Maven / Gradle / Ivy

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


 *
 * ---------------
 * SwingFormFactory.java
 * ---------------
 */
package org.jpedal.objects.acroforms.creation;

import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.text.JTextComponent;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import org.jpedal.objects.acroforms.GUIData;
import org.jpedal.objects.acroforms.SwingData;
import org.jpedal.objects.acroforms.actions.SwingDownIconListener;
import org.jpedal.objects.acroforms.actions.SwingFormButtonListener;
import org.jpedal.objects.acroforms.actions.SwingListener;
import org.jpedal.objects.acroforms.overridingImplementations.FixImageIcon;
import org.jpedal.objects.acroforms.overridingImplementations.PdfSwingPopup;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.FormStream;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.utils.Strip;

public class SwingFormFactory extends GenericFormFactory implements FormFactory {

    private JButton setupAnnotationButton(final FormObject form) {
        //point where testActions breaks - ignore this halting error as it is within the testActions flag.
        final JButton but = new JButton();

        setupButton(but, form);
        setupUniversalFeatures(but, form);

        return but;
    }

    private JButton createAnntoationHighlight(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));
        return but;
    }

    private JButton createAnnotationFreeText(final FormObject form) {
        final JButton but = setupAnnotationButton(form);

        final String rc = form.getTextStreamValue(PdfDictionary.RC);
        if (rc != null) {
            but.setText(rc);
        } else {
            final String contents = form.getTextStreamValue(PdfDictionary.Contents);
            if (contents != null) {
                but.setText("" + contents + "");
            }
        }
        //but.setText("" + form.getTextStreamValue(PdfDictionary.Contents) + "");

        but.setVerticalAlignment(SwingConstants.TOP);
        but.setHorizontalAlignment(SwingConstants.LEFT);

        final Font font = new Font("TimesRoman", Font.PLAIN, 12);
        form.setTextSize(12);
        but.setFont(font);

        form.setFontName("TimesRoman");
        form.setTextFont(font);

        //Adjust position to counteract swing values misplacing text
        final FontMetrics fm = but.getFontMetrics(font);
        final Rectangle r = form.getBoundingRectangle();
        int offset = fm.getMaxAscent() - 12;
        if (offset < 0) {
            offset = 0;
        }
        offset += fm.getDescent();
        form.setFloatArray(PdfDictionary.Rect, new float[]{r.x + 2, r.y + offset, r.x + r.width + 2, r.y + r.height + offset});

        return but;
    }

    private JButton createAnnotationText(final FormObject form) {
        final JButton but = setupAnnotationButton(form);

        final int rot = pageData.getRotation(form.getPageNumber());

        final BufferedImage commentIcon = AnnotationFactory.getIcon(form);

        if (commentIcon != null) {
            //Ensure sized correctly
            final float[] rect = form.getFloatArray(PdfDictionary.Rect);
            // rect[0] = rect[0];
            rect[1] = rect[3] - commentIcon.getHeight();
            rect[2] = rect[0] + commentIcon.getWidth();
            //   rect[3] = rect[3];
            form.setFloatArray(PdfDictionary.Rect, rect);
        }
        but.setIcon(new FixImageIcon(form, commentIcon, rot));

        return but;
    }

    private JComponent createAnnotationPopup(final FormObject form) {

        final JComponent comp = getPopupComponent(form, pageData.getCropBoxWidth(form.getPageNumber()));
        form.setGUIComponent(comp, FormFactory.SWING);
        //set visibility
        comp.setVisible(form.getBoolean(PdfDictionary.Open));

        return comp;

    }

    private JButton createAnnotationUnderline(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));
        return but;
    }

    private JButton createAnnotationInk(final FormObject form) {
        final JButton but = setupAnnotationButton(form);

        //we need this bit
        but.setToolTipText(form.getTextStreamValue(PdfDictionary.Contents));

        final Object[] InkListArray = form.getObjectArray(PdfDictionary.InkList);

        //resize ink size if entire ink is not contained
        final float[] r = AnnotationFactory.scanInkListTree(InkListArray, form, null);
        form.setFloatArray(PdfDictionary.Rect, new float[]{r[0], r[1], r[2], r[3]});

        //Create image to draw to
//        final BufferedImage icon1 = new BufferedImage(form.getBoundingRectangle().width, form.getBoundingRectangle().height, BufferedImage.TYPE_4BYTE_ABGR);
//        scanInkListTree(InkListArray, form, icon1.getGraphics());

        //Add image to button
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationStrikeOut(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));
        return but;
    }

    private JButton createAnnotationSquare(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationLine(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationPoly(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationCaret(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationFileAttachment(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationCircle(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationSquiggly(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    private JButton createAnnotationSound(final FormObject form) {
        final JButton but = setupAnnotationButton(form);
        but.setBackground(new Color(0, 0, 0, 0));
        but.setIcon(new FixImageIcon(form, AnnotationFactory.getIcon(form), 0));

        return but;
    }

    /**
     * Setup and return the Annotation specified in the FormObject
     *
     * @param form FormObject containing all values for the Annotation
     * @return Object to display the PDF Annotation in the viewer
     */
    @Override
    public Object annotationButton(final FormObject form) {

        final int subtype = form.getParameterConstant(PdfDictionary.Subtype);

        //Special case that does not return a button so handle separately.
        if (subtype == PdfDictionary.Popup) {
            return createAnnotationPopup(form);
        }
        /*
         * @kieran - there are several types of annotation (Underline, highlight, Ink).
         *
         * We implemented them by adding a button and putting the content on the button's image
         * We had not added others as no example. Can you use the text example to add for missing values?
         */
        // If we're using the icon from the AP Stream, this section isn't used
        if (!form.isAppearanceUsed()) {
            switch (subtype) {
                case PdfDictionary.Text:/* a sticky note which displays a popup when open. */
                    return createAnnotationText(form);
                case PdfDictionary.FreeText:/* we only have 11dec/itext_sample.pdf as example) */
                    return createAnnotationFreeText(form);
                case PdfDictionary.Highlight:
                    return createAnntoationHighlight(form);
                case PdfDictionary.Underline:
                    return createAnnotationUnderline(form);
                case PdfDictionary.Ink:
                    return createAnnotationInk(form);
                case PdfDictionary.StrickOut:
                    return createAnnotationStrikeOut(form);
                case PdfDictionary.Square:
                    return createAnnotationSquare(form);
                case PdfDictionary.Caret:
                    return createAnnotationCaret(form);
                case PdfDictionary.Line:
                    return createAnnotationLine(form);
                case PdfDictionary.Polygon:
                    return createAnnotationPoly(form);
                case PdfDictionary.PolyLine:
                    return createAnnotationPoly(form);
                case PdfDictionary.Circle:
                    return createAnnotationCircle(form);
                case PdfDictionary.Squiggly:
                    return createAnnotationSquiggly(form);
                case PdfDictionary.FileAttachment:
                    return createAnnotationFileAttachment(form);
                case PdfDictionary.Sound:
                    return createAnnotationSound(form);

            }
        }

        //If none of the above, just setup button
        return setupAnnotationButton(form);
    }

    static float[] curveInk(final float[] points) {

        double x0, y0;
        double x1, y1;
        double x2, y2;
        double x3, y3;

        final double smooth_value = 1.0;

        int currentIndex = 0;
        final float[] returnPoints = new float[((points.length - 2) / 2) * 8];

        for (int i = 0; i < points.length; i += 2) {

            if (i == 0) {

                x0 = points[i];
                y0 = points[i + 1];
                x1 = points[i];
                y1 = points[i + 1];
                x2 = points[i + 2];
                y2 = points[i + 3];
                x3 = points[i + 4];
                y3 = points[i + 5];

                final double[] cps = findControlPoint(x0, y0, x1, y1, x2, y2, x3, y3, smooth_value);
                returnPoints[currentIndex] = (float) x1;
                currentIndex++;
                returnPoints[currentIndex] = (float) y1;
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[0];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[1];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[2];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[3];
                currentIndex++;
                returnPoints[currentIndex] = (float) x2;
                currentIndex++;
                returnPoints[currentIndex] = (float) y2;
                currentIndex++;
            }

            if (i + 6 >= points.length) {

                x0 = points[i];
                y0 = points[i + 1];
                x1 = points[i + 2];
                y1 = points[i + 3];
                x2 = points[i + 4];
                y2 = points[i + 5];
                x3 = points[i + 4];
                y3 = points[i + 5];

                final double[] cps = findControlPoint(x0, y0, x1, y1, x2, y2, x3, y3, smooth_value);
                returnPoints[currentIndex] = (float) x1;
                currentIndex++;
                returnPoints[currentIndex] = (float) y1;
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[0];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[1];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[2];
                currentIndex++;
                returnPoints[currentIndex] = (float) cps[3];
                currentIndex++;
                returnPoints[currentIndex] = (float) x2;
                currentIndex++;
                returnPoints[currentIndex] = (float) y2;
                //currentIndex++;
                break;
            }


            x0 = points[i];
            y0 = points[i + 1];
            x1 = points[i + 2];
            y1 = points[i + 3];
            x2 = points[i + 4];
            y2 = points[i + 5];
            x3 = points[i + 6];
            y3 = points[i + 7];

            final double[] cps = findControlPoint(x0, y0, x1, y1, x2, y2, x3, y3, smooth_value);
            returnPoints[currentIndex] = (float) x1;
            currentIndex++;
            returnPoints[currentIndex] = (float) y1;
            currentIndex++;
            returnPoints[currentIndex] = (float) cps[0];
            currentIndex++;
            returnPoints[currentIndex] = (float) cps[1];
            currentIndex++;
            returnPoints[currentIndex] = (float) cps[2];
            currentIndex++;
            returnPoints[currentIndex] = (float) cps[3];
            currentIndex++;
            returnPoints[currentIndex] = (float) x2;
            currentIndex++;
            returnPoints[currentIndex] = (float) y2;
            currentIndex++;

        }

        return returnPoints;
    }

    private static double[] findControlPoint(final double x0, final double y0,
                                             final double x1, final double y1,
                                             final double x2, final double y2,
                                             final double x3, final double y3,
                                             final double smooth_value) {

        //Find mid points
        final double xc1 = (x0 + x1) / 2.0;
        final double yc1 = (y0 + y1) / 2.0;
        final double xc2 = (x1 + x2) / 2.0;
        final double yc2 = (y1 + y2) / 2.0;
        final double xc3 = (x2 + x3) / 2.0;
        final double yc3 = (y2 + y3) / 2.0;

        //Caculate lengths
        final double len1 = Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
        final double len2 = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
        final double len3 = Math.sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2));

        //Mid point proportion
        final double k1 = len1 / (len1 + len2);
        final double k2 = len2 / (len2 + len3);

        //Length of mid point line proportions
        final double xm1 = xc1 + (xc2 - xc1) * k1;
        final double ym1 = yc1 + (yc2 - yc1) * k1;
        final double xm2 = xc2 + (xc3 - xc2) * k2;
        final double ym2 = yc2 + (yc3 - yc2) * k2;

        //Calculate control points
        final double ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
        final double ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;

        final double ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
        final double ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;

        return new double[]{ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y};
    }

    /**
     * Setup and return the ComboBox specified in the FormObject
     *
     * @param form FormObject containing all values or the ComboBox
     * @return Object to display the PDF ComboBox in the viewer
     */
    @Override
    public Object comboBox(final FormObject form) {

        //populate items array with list from Opt
        final String[] items = form.getItemsList();
        final JComboBox comboBox;
        if (items == null) {
            comboBox = new JComboBox();
        } else {

            comboBox = new JComboBox(items);
            
            /*
             * allow background colour in cells
             */
            final Color backgroundColor = FormObject.generateColor(form.getDictionary(PdfDictionary.MK).getFloatArray(PdfDictionary.BG));
            if (backgroundColor != null) {
                final ListCellRenderer renderer = new ComboColorRenderer(backgroundColor);
                comboBox.setRenderer(renderer);
            }
        }

        //get and set currently selected value
        final String textValue = form.getSelectedItem();
        if (form.getValuesMap(true) != null) {
            comboBox.setSelectedItem(form.getValuesMap(true).get(textValue));
        } else {
            comboBox.setSelectedItem(textValue);
        }

        //sync to FormObject
        final int selectionIndex = comboBox.getSelectedIndex();
        form.setSelection(comboBox.getSelectedObjects(), (String) comboBox.getSelectedItem(), new int[]{selectionIndex}, selectionIndex);

        final boolean[] flags = form.getFieldFlags();
        if (flags[FormObject.EDIT_ID]) { //FormObject.EDIT_ID

            comboBox.setEditable(true);

        } else { //is not editable

            comboBox.setEditable(false);
        }

        setupUniversalFeatures(comboBox, form);

        if (flags[FormObject.READONLY_ID]) {
            comboBox.setEditable(false); //combo box
            comboBox.setEnabled(false);
        }

        if (org.jpedal.DevFlags.GUITESTINGINPROGRESS) {
            final javax.swing.text.Caret c = ((JTextComponent) comboBox.getEditor().getEditorComponent()).getCaret();
            c.setBlinkRate(0);
        }

        //listener to keep synced
        comboBox.addItemListener(new ComboListener(comboBox, form));

        return comboBox;
    }

    /**
     * Setup and return the CheckBox specified in the FormObject
     *
     * @param form FormObject containing all values or the CheckBox
     * @return Object to display the PDF CheckBox in the viewer
     */
    @Override
    public Object checkBoxBut(final FormObject form) {

        final JCheckBox checkBut = new JCheckBox();

        setupButton(checkBut, form);

        setupUniversalFeatures(checkBut, form);

        if (checkBut.getBorder() != null) {
            checkBut.setBorderPainted(true);
        }

        final boolean[] flags = form.getFieldFlags();
        if ((flags != null) && (flags[FormObject.READONLY_ID])) {
            checkBut.setEnabled(false);
            checkBut.setDisabledIcon(checkBut.getIcon());
            checkBut.setDisabledSelectedIcon(checkBut.getSelectedIcon());
        }

        return checkBut;
    }

    /**
     * Setup and return the List specified in the FormObject
     *
     * @param form FormObject containing all values or the List
     * @return Object to display the PDF List in the viewer
     */
    @Override
    public Object listField(final FormObject form) {

        //populate the items array with list from Opt
        final String[] items = form.getItemsList();

        //create list (note we catch null value)
        final JList list;
        if (items != null) {
            list = new JList(items);
        } else {
            list = new JList();
        }

        if (!form.getFieldFlags()[FormObject.MULTISELECT_ID])//mulitselect
        {
            list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        }

        //if there is a top index or selected value select it
        if (form.getSelectionIndices() != null) {
            list.setSelectedIndices(form.getSelectionIndices());
        } else if (form.getValuesMap(true) != null) {
            list.setSelectedValue(form.getValuesMap(true).get(form.getSelectedItem()), true);
        } else {
            list.setSelectedValue(form.getSelectedItem(), true);
        }


        //sync to FormObject
        form.setSelection(list.getSelectedValues(), (String) list.getSelectedValue(), list.getSelectedIndices(), list.getSelectedIndex());

        setupUniversalFeatures(list, form);

        //listener to keep synced
        list.addListSelectionListener(new ListListener(list, form));

        return list;
    }

    /**
     * Setup and return the MultiLine Password specified in the FormObject
     *
     * @param form FormObject containing all values or the MultiLine Password
     * @return Object to display the PDF MultiLine Password in the viewer
     */
    @Override
    public Object multiLinePassword(final FormObject form) {

        final JPasswordField multipass;
        final String textValue = form.getTextString();
        final int maxLength = form.getInt(PdfDictionary.MaxLen);

        if (maxLength != -1) {
            multipass = new JPasswordField(textValue, maxLength);
        } else {
            multipass = new JPasswordField(textValue);
        }

        multipass.setEchoChar('*');

        setupUniversalFeatures(multipass, form);

        setupTextFeatures(multipass, form);

        final boolean[] flags = form.getFieldFlags();
        if ((flags != null) && (flags[FormObject.READONLY_ID])) {
            multipass.setEditable(false);
        }

        setToolTip(form, multipass);

        return multipass;
    }

    /**
     * Setup and return the MultiLine Text Area specified in the FormObject
     *
     * @param form FormObject containing all values or the MultiLine Text Area
     * @return Object to display the PDF MultiLine Text Area in the viewer
     */
    @Override
    public Object multiLineText(final FormObject form) {

        final boolean[] flags = form.getFieldFlags();
        final boolean[] characteristics = form.getCharacteristics();

        final JComponent comp;
        final String text = form.getTextString();

        if (flags != null && flags[FormObject.READONLY_ID] ||
                characteristics != null && characteristics[9]) {

            final JTextPane newTextarea = new JTextPane();
            newTextarea.setText(text);
//            newTextarea.setLineWrap(true);
//            newTextarea.setWrapStyleWord(true);

            newTextarea.setEditable(false);

            if (form.getAlignment() != -1) {
                final StyledDocument doc = newTextarea.getStyledDocument();
                final SimpleAttributeSet center = new SimpleAttributeSet();

                switch (form.getAlignment()) {
                    case SwingConstants.CENTER:
                        StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
                        break;
                    case SwingConstants.RIGHT:
                        StyleConstants.setAlignment(center, StyleConstants.ALIGN_RIGHT);
                        break;
                }

                doc.setParagraphAttributes(0, doc.getLength(), center, false);
            }
            
            /*
             * ensure we sync back to FormObject if altered
             */
            newTextarea.getDocument().addDocumentListener(new TextDocumentListener(newTextarea, form));

            comp = newTextarea;

        } else {

            final JTextPane newTextarea = new JTextPane();
            newTextarea.setText(text);
//          newTextarea.setLineWrap(true);
//          newTextarea.setWrapStyleWord(true);

            if (form.getAlignment() != -1) {
                final StyledDocument doc = newTextarea.getStyledDocument();
                final SimpleAttributeSet center = new SimpleAttributeSet();

                switch (form.getAlignment()) {
                    case SwingConstants.CENTER:
                        StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
                        break;
                    case SwingConstants.RIGHT:
                        StyleConstants.setAlignment(center, StyleConstants.ALIGN_RIGHT);
                        break;
                }

                doc.setParagraphAttributes(0, doc.getLength(), center, false);
            }
            /*
             * ensure we sync back to FormObject if altered
             */
            newTextarea.getDocument().addDocumentListener(new TextDocumentListener(newTextarea, form));

            comp = newTextarea;

        }

        setToolTip(form, comp);
        setupUniversalFeatures(comp, form);

        return comp;
    }

    /**
     * Setup and return the Signature Field specified in the FormObject
     *
     * @param form FormObject containing all values or the Signature Field
     * @return Object to display the PDF Signature Field in the viewer
     */
    @Override
    public Object signature(final FormObject form) {

        final JButton sigBut = new JButton();

        setupButton(sigBut, form);

        setupUniversalFeatures(sigBut, form);

        final boolean[] flags = form.getFieldFlags();
        if (flags != null && flags[FormObject.READONLY_ID]) {
            sigBut.setEnabled(false);
            sigBut.setDisabledIcon(sigBut.getIcon());
            sigBut.setDisabledSelectedIcon(sigBut.getSelectedIcon());
        }

        //show as box similar to Acrobat if no values
        if (!form.isAppearanceUsed()) {
            sigBut.setOpaque(false);
            final BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
            final Graphics2D imgG2 = img.createGraphics();
            imgG2.setPaint(new Color(221, 228, 255, 175));
            imgG2.fillRect(0, 0, 1, 1);
            sigBut.setIcon(new FixImageIcon(form, img, 0));

            // sigBut.setBackground(new Color(221,228,255,175)); //r,g,b,a
        }

        return sigBut;
    }

    /**
     * Setup and return the Push Button specified in the FormObject
     *
     * @param form FormObject containing all values or the Push Button
     * @return Object to display the PDF Push Button in the viewer
     */
    @Override
    public Object pushBut(final FormObject form) {

        //the text value
        final JButton pushBut = new JButton();

        setupButton(pushBut, form);

        setupUniversalFeatures(pushBut, form);

        final boolean[] flags = form.getFieldFlags();
        if ((flags != null) && (flags[FormObject.READONLY_ID])) {
            pushBut.setEnabled(false);
            pushBut.setDisabledIcon(pushBut.getIcon());
            pushBut.setDisabledSelectedIcon(pushBut.getSelectedIcon());
        }

        return pushBut;
    }

    /**
     * Setup and return the Radio Button specified in the FormObject
     *
     * @param form FormObject containing all values or the Radio Button
     * @return Object to display the PDF Radio Button in the viewer
     */
    @Override
    public Object radioBut(final FormObject form) {

        //the text value
        final JRadioButton radioBut = new JRadioButton();

        setupButton(radioBut, form);

        setupUniversalFeatures(radioBut, form);

        final boolean[] flags = form.getFieldFlags();
        if ((flags != null) && (flags[FormObject.READONLY_ID])) {
            radioBut.setEnabled(false);
            radioBut.setDisabledIcon(radioBut.getIcon());
            radioBut.setDisabledSelectedIcon(radioBut.getSelectedIcon());
        }

        return radioBut;
    }

    /**
     * Setup and return the Single Line Password specified in the FormObject
     *
     * @param form FormObject containing all values or the Single Line Password
     * @return Object to display the PDF Single Line Password in the viewer
     */
    @Override
    public Object singleLinePassword(final FormObject form) {

        final JPasswordField newPassword = new JPasswordField(form.getTextString());
        newPassword.setEchoChar('*');

        //set length
        final int maxLength = form.getInt(PdfDictionary.MaxLen);
        if (maxLength != -1) {
            newPassword.setColumns(maxLength);
        }

        setupUniversalFeatures(newPassword, form);

        setupTextFeatures(newPassword, form);

        final boolean[] flags = form.getFieldFlags();
        if ((flags != null) && (flags[FormObject.READONLY_ID])) {
            newPassword.setEditable(false);
        }

        setToolTip(form, newPassword);


        return newPassword;
    }

    /**
     * Setup and return the Single Line Text Field specified in the FormObject
     *
     * @param form FormObject containing all values or the Single Line Text Field
     * @return Object to display the PDF Single Line Text Field in the viewer
     */
    @Override
    public Object singleLineText(final FormObject form) {

        final boolean[] flags = form.getFieldFlags();
        final boolean[] characteristics = form.getCharacteristics();

        //fix the form3 file.
        //check if appearance stream matches text string
        String aptext = readAPimagesForText(form);

        //strip out any ascii escape chars (ie 
)
        if (aptext != null && aptext.contains("&#")) {
            aptext = Strip.stripXML(aptext, true).toString();
        }

        //V value can be null so allow to be set if AP exists.
        //ie  /PDFdata/baseline_screens/abacus/eva_PDF_Felddefinitionen.pdf
        if (aptext != null && !aptext.equals(form.getTextStreamValue(PdfDictionary.V))) {
            //may need to check for inheritance in future if there are issues again.
            //use appearance text if different
            form.setTextStreamValue(PdfDictionary.V, aptext);
        }

        final JComponent retComp;
        if (((flags != null) && (flags[FormObject.READONLY_ID]))
                || (characteristics != null && characteristics[9])//characteristics[9] = LockedContents
            //p609 PDF ref ver 1-7, characteristics 'locked' flag does allow contents to be edited,
            //but the 'LockedContents' flag stops content being changed.
                ) {

            if (form.isXFAObject()) {
                final JTextField newTextfield = new JTextField(form.getTextString());

                setupTextFeatures(newTextfield, form);
                setToolTip(form, newTextfield);
                newTextfield.setEditable(false);
                retComp = newTextfield;
            } else {
                //set the XObject to our icon code so it can be altered later
//                ReadOnlyTextIcon textIcon = new ReadOnlyTextIcon(form,0,currentPdfFile,AcroRes);
//                textIcon.setAlignment(form.getAlignment());

//                if(!textIcon.decipherAppObject(form)){
                final JTextField newTextfield = new JTextField(form.getTextString());

                setupTextFeatures(newTextfield, form);
                setToolTip(form, newTextfield);

                newTextfield.setEditable(false);

                retComp = newTextfield;

//                }else{
//                    
//                    //****Now we know we are using our ReadOnlyTextIcon set it up.***
//                    
//                    //build new buttonTextField
//                    JButton textBut = new JButton();
//                    //force NO drawing of the AP image as we are going to do that after initial setup
//                    form.setAppreancesUsed(false);
//                    setupButton(textBut, form);
//                    
//                    //do our swing stuff here
//                    form.setAppreancesUsed(true);
//                    textBut.setText(null);
//                    
//                    //store the text for use in the font size Calculation
//                    String V = form.getTextStreamValue(PdfDictionary.V);
//                    if(V!=null)
//                        textIcon.setText(V);
//                    
//                    //most important apply the icon
//                    textBut.setIcon(textIcon);
//                    
//                    retComp = textBut;
//                	 
//                	 JTextField newTextfield = new JTextField(form.getTextString());
//                     
//                     setupTextFeatures(newTextfield, form);
//                     setToolTip(form, newTextfield);
//                     
//                     newTextfield.setEditable(false);
//                     
//                     retComp = newTextfield;
//                }
            }

        } else { //NOT read only
            final JTextField newTextfield = new JTextField(form.getTextString());

            setupTextFeatures(newTextfield, form);
            setToolTip(form, newTextfield);

            retComp = newTextfield;

        }

        setupUniversalFeatures(retComp, form);

        return retComp;
    }

    //############ below is all text setup ################ TAG

    /**
     * sets up all the required attributes for all text fields
     */
    static void setupTextFeatures(final JTextField textcomp, final FormObject form) {

        //set text field alignment
        if (form.getAlignment() != -1) {
            textcomp.setHorizontalAlignment(form.getAlignment());
        }

        if (org.jpedal.DevFlags.GUITESTINGINPROGRESS) {
            final javax.swing.text.Caret c = textcomp.getCaret();
            c.setBlinkRate(0);
        }
        
        /*
         * ensure we sync back to FormObject if altered
         */
        textcomp.getDocument().addDocumentListener(new TextDocumentListener(textcomp, form));

    }

    //################# below is buttons setup ################## TAG

    /**
     * sets up the buttons captions, images, etc
     * for normal, rollover, down and off or on if radio or check buttons
     */
    private void setupButton(final AbstractButton comp, final FormObject form) {
        
        /*
         * put in group (just store first time as you cannot have 1 button in group)
         */
        String name = form.getTextStreamValue(PdfDictionary.T);
        if (name == null) {
            name = "";
        }

        ButtonGroup bg = (ButtonGroup) groups.get(name);
        if (bg == null) {
            bg = new ButtonGroup();
            groups.put(name, bg);
            firstButtons.put(name, comp); //save to avoid buttonGroup if single
        } else {

            //add any first comp
            final AbstractButton firstButton = (AbstractButton) firstButtons.get(name);

            if (firstButton != null) {
                firstButtons.remove(name);
                bg.add(firstButton);
            }

            bg.add(comp);
        }

        //transparancy
        //    	((AbstractButton) comp).setContentAreaFilled(false); //false for transparency
        final String normalCaption = form.getDictionary(PdfDictionary.MK).getTextStreamValue(PdfDictionary.CA);
        comp.setText(normalCaption);

        comp.setContentAreaFilled(false);

        final String downCaption = form.getDictionary(PdfDictionary.MK).getTextStreamValue(PdfDictionary.AC);
        final String rolloverCaption = form.getDictionary(PdfDictionary.MK).getTextStreamValue(PdfDictionary.RC);
        if ((downCaption != null && !downCaption.isEmpty()) || (rolloverCaption != null && !rolloverCaption.isEmpty())) {
            comp.addMouseListener(new SwingFormButtonListener(normalCaption, rolloverCaption, downCaption));
        }

        if (form.isAppearanceUsed()) {
            setAPImages(form, comp); // pass in true to debug images by showing
        }

        final int textPosition = form.getTextPosition();
        if (textPosition != -1) {
            /*
             * if there are any appearance images, then the text is set back to null,
             * if the textPosition needs to be setup you need to either set the text back here or not
             * set it to null in appearanceImages.
             *
             * If you need to set this up check file acodabb.pdf page 4 as it has an icon with text being
             * set to overlay the icon, which doesn't work.
             */
            switch (textPosition) {
                case 0:

                    comp.setIcon(null);
                    comp.setText(normalCaption); //seems to need reset
                    break; //0=caption only
                case 1:
                    comp.setText(null);
                    break; //1=icon only
                case 2:
                    comp.setVerticalTextPosition(SwingConstants.BOTTOM);
                    break; //2=caption below icon
                case 3:
                    comp.setVerticalTextPosition(SwingConstants.TOP);
                    break; //3=caption above icon
                case 4:
                    comp.setHorizontalTextPosition(SwingConstants.RIGHT);
                    break; //4=caption on right of icon
                case 5:
                    comp.setHorizontalTextPosition(SwingConstants.LEFT);
                    break; //5=caption on left of icon
                case 6:
                    comp.setText(null);
                    break;
            }
        }

        //TODO get margin data from formobject
        final Insets insetZero = new Insets(0, 0, 0, 0);
        comp.setMargin(insetZero);

        comp.addMouseListener((MouseListener) formsActionHandler.setHoverCursor());
        
        /*
         * ensure we sync back to FormObject if altered
         */
        comp.addChangeListener(new RadioListener(comp, form));

    }

    /**
     * gets each appearance image from the map appearance and
     * and adds it to the relevant icon for the AbstractButton comp
     * showImages is to display the appearance images for that FormObject
     */
    public void setAPImages(final FormObject form, final AbstractButton rawComp) {

        final AbstractButton comp = rawComp;

        final PdfObject APobjN = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N);
        final PdfObject APobjD = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.D);
        final PdfObject APobjR = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.R);
        PdfObject normalOffDic = null, normalOnDic = null, downOffDic = null, downOnDic = null, rollOffDic = null, rollOnDic = null;

        //if 0 no change, 1 offset image, 2 invert image
        int offsetDownImage = 0;

        final int subtype = form.getParameterConstant(PdfDictionary.Subtype);

        if (APobjN != null || form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I) != null) {

            final Object[] values = FormStream.getNormalKeyValues(form, currentPdfFile.getObjectReader());
            form.setNormalOnState((String) values[0]);
            normalOnDic = (PdfObject) values[1];
            normalOffDic = (PdfObject) values[2];

            if (normalOffDic != null) {
                comp.setText(null);
                comp.setIcon(new FixImageIcon(form, normalOffDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, 0));
            }

            if (normalOnDic != null) {
                comp.setText(null);
                comp.setSelectedIcon(new FixImageIcon(form, normalOnDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, 0));

                if (comp.getIcon() == null) {
                    comp.setIcon(new FixImageIcon(form, null, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R)));
                }
            }
        }

        if (form.hasNoDownIcon()) {
            comp.setPressedIcon(comp.getIcon());
        } else {
            if (form.hasOffsetDownIcon()) {
                offsetDownImage = 1;
            } else if (form.hasInvertDownIcon()) {
                offsetDownImage = 2;
            }

            if (offsetDownImage != 0) {
                if (normalOffDic != null) {
                    if (normalOnDic != null) {
                        downOffDic = normalOffDic;
                        downOnDic = normalOnDic;
                    } else {
                        downOffDic = normalOffDic;
                    }
                } else if (normalOnDic != null) {
                    downOffDic = normalOnDic;
                }
            }

            if (APobjD != null) {

                if (1 == 2) {

                    final Object[] values = FormStream.getDownKeyValues(form, currentPdfFile.getObjectReader());
                    downOnDic = (PdfObject) values[1];
                    downOffDic = (PdfObject) values[2];

                } else {

                    //down off
                    //if we have a root stream then it is the off value
                    if (APobjD.getDecodedStream() != null) {
                        downOffDic = APobjD;
                    } else if (APobjD.getDictionary(PdfDictionary.Off) != null) {
                        downOffDic = APobjD.getDictionary(PdfDictionary.Off);
                    }

                    //down on
                    if (APobjD.getDictionary(PdfDictionary.On) != null) {
                        downOnDic = APobjD.getDictionary(PdfDictionary.On);
                    }

                }
            }

            if (downOffDic == null || downOnDic == null) {
                if (downOffDic != null) {
                    comp.setText(null);
                    comp.setPressedIcon(new FixImageIcon(form, downOffDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, offsetDownImage));
                } else if (downOnDic != null) {
                    comp.setText(null);
                    comp.setPressedIcon(new FixImageIcon(form, downOnDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, offsetDownImage));
                }
            } else {
                comp.setPressedIcon(new FixImageIcon(form, downOnDic, downOffDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), comp.isSelected() ? 1 : 0, currentPdfFile, subtype, offsetDownImage));
                comp.addActionListener(new SwingDownIconListener());
            }
        }

        if (APobjR != null) {

            if (1 == 2) {
                final Object[] RValues = FormStream.getRolloverKeyValues(form, currentPdfFile.getObjectReader());
                rollOffDic = (PdfObject) RValues[2];
                rollOnDic = (PdfObject) RValues[1];
            } else {
                //if we have a root stream then it is the off value
                if (APobjR.getDecodedStream() != null) {
                    rollOffDic = APobjR;
                } else if (APobjR.getDictionary(PdfDictionary.Off) != null) {
                    rollOffDic = APobjR.getDictionary(PdfDictionary.Off);
                }
                //if we have a root stream then it is the off value
                if (APobjR.getDictionary(PdfDictionary.On) != null) {
                    rollOnDic = APobjR.getDictionary(PdfDictionary.On);
                }
            }

            if (rollOffDic != null) {
                comp.setRolloverEnabled(true);
                comp.setText(null);
                comp.setRolloverIcon(new FixImageIcon(form, rollOffDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, 0));
            }

            if (rollOnDic != null) {
                comp.setRolloverEnabled(true);
                comp.setText(null);
                comp.setRolloverSelectedIcon(new FixImageIcon(form, rollOnDic, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R), currentPdfFile, subtype, 0));

                if (comp.getRolloverIcon() == null) {
                    comp.setRolloverIcon(new FixImageIcon(form, null, form.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R)));
                }
            }
        }

        //moved to end as flagLastUsed can call the imageicon
        if (form.isSelected()) {
            comp.setSelected(true);
            if (comp instanceof JToggleButton) {
                final Icon icn = comp.getPressedIcon();
                if (icn instanceof FixImageIcon) {
                    ((FixImageIcon) icn).swapImage(true);
                }
            }
        }
    }

    /**
     * sets up the features for all fields, transparancy, font, color, border, actions,
     * background color,
     */
    private void setupUniversalFeatures(final JComponent comp, final FormObject form) {

        comp.setOpaque(false);

        final Font textFont = form.getTextFont();
        if (textFont != null) {
            comp.setFont(textFont);
        }
        comp.setForeground(form.getTextColor());

        final Border newBorder = SwingData.generateBorderfromForm(form, 1);

        comp.setBorder(newBorder);

        final Color backgroundColor = FormObject.generateColor(form.getDictionary(PdfDictionary.MK).getFloatArray(PdfDictionary.BG));
        if (backgroundColor != null) {
            comp.setBackground(backgroundColor);
            comp.setOpaque(true);
        } else if (DecoderOptions.isRunningOnMac && (comp instanceof JButton)) {
            //hack because OS X does not f***king work properly
            ((AbstractButton) comp).setBorderPainted(false);
            comp.setBorder(null);

        }

        setupMouseListener(comp, form);

        if (org.jpedal.DevFlags.GUITESTINGINPROGRESS && comp instanceof javax.swing.text.JTextComponent) {
            final javax.swing.text.Caret c = ((javax.swing.text.JTextComponent) comp).getCaret();
            c.setBlinkRate(0);
        }
    }

    /**
     * setup the events for currentComp, from the specified parameters
     *
     * @Action - mouse events added here
     */
    private void setupMouseListener(final Component currentComp, final FormObject form) {
        /* bit 1 is index 0 in []
         * 1 = invisible
         * 2 = hidden - dont display or print
         * 3 = print - print if set, dont if not
         * 4 = nozoom
         * 5= norotate
         * 6= noview
         * 7 = read only (ignored by wiget)
         * 8 = locked
         * 9 = togglenoview
         * 10 = LockedContent
         */

        final boolean[] characteristic = form.getCharacteristics(); //F
        if (characteristic[0] || characteristic[1] || characteristic[5]) {
            currentComp.setVisible(false);
        }

        final SwingListener jpedalListener = new SwingListener(form, formsActionHandler);
        //if combobox wee need to add the listener to the component at position 0 as well as the normal one, so it works properly.
        if (currentComp instanceof JComboBox) {
            ((Container) currentComp).getComponent(0).addMouseListener(jpedalListener);
            ((Container) currentComp).getComponent(0).addKeyListener(jpedalListener);
            ((Container) currentComp).getComponent(0).addFocusListener(jpedalListener);
            ((JComboBox) currentComp).addActionListener(jpedalListener);
        }
        if (currentComp instanceof JList) {
            ((JList) currentComp).addListSelectionListener(jpedalListener);
        }

        currentComp.addMouseListener(jpedalListener);
        currentComp.addMouseMotionListener(jpedalListener);
        currentComp.addKeyListener(jpedalListener);
        currentComp.addFocusListener(jpedalListener);

        final PdfObject aData = form.getDictionary(PdfDictionary.A);
        if (aData != null && aData.getNameAsConstant(PdfDictionary.S) == PdfDictionary.URI) {

            final String noLinkToolTips = System.getProperty("org.jpedal.noURLaccess");

            if (noLinkToolTips == null || !noLinkToolTips.equals("true")) {
                final String text = aData.getTextStreamValue(PdfDictionary.URI); //+"ZZ"; deliberately broken first to test checking
                ((JComponent) currentComp).setToolTipText(text);
            }
        }
    }

    private static void setToolTip(final FormObject formObject, final JComponent retComponent) {
        //TU seems to be used as a tooltip in text fields so added
        final String userName = formObject.getTextStreamValue(PdfDictionary.TU);
        if (userName != null) {
            retComponent.setToolTipText(userName);
        }
    }

    /**
     * Get new data object to hold all widget implementations
     *
     * @return GUIData object to hold widget data
     */
    @Override
    public GUIData getCustomCompData() {
        return new SwingData();
    }


    @Override
    public int getType() {
        return FormFactory.SWING;
    }

    /**
     * Set the annot field lists tab order from the map provided.
     * Currently not implemented.
     *
     * @param annotOrder Map where keys are pdf object references as Strings and values are tab indices as Strings
     */
    @Override
    public void setAnnotOrder(final Map annotOrder) {
    }

    /**
     * public method to allow user to replace Popup with their own implementation
     *
     * @param form        FormObject representing the popup
     * @param cropBoxWith int value representing the width of the page for this popup
     * @return Swing component to use as popup (see org.jpedal.objects.acroforms.overridingImplementations.PdfSwingPopup)
     */
    @SuppressWarnings("MethodMayBeStatic")
    public PdfSwingPopup getPopupComponent(final FormObject form, final int cropBoxWith) {
        final SwingListener listener = new SwingListener(form, formsActionHandler);
        return new PdfSwingPopup(form, cropBoxWith, listener);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy