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

org.jpedal.objects.acroforms.SwingData 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
 *
 @LICENSE@
 *
 * ---------------
 * SwingData.java
 * ---------------
 */
package org.jpedal.objects.acroforms;


import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.text.JTextComponent;

import org.jpedal.display.Display;
import org.jpedal.external.CustomFormPrint;
import org.jpedal.objects.acroforms.creation.AnnotationFactory;
import org.jpedal.objects.acroforms.creation.FormFactory;
import org.jpedal.objects.acroforms.creation.JPedalBorderFactory;
import org.jpedal.objects.acroforms.overridingImplementations.CustomImageIcon;
import org.jpedal.objects.acroforms.overridingImplementations.FixImageIcon;
import org.jpedal.objects.acroforms.overridingImplementations.ReadOnlyTextIcon;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.utils.LogWriter;

/**
 * Swing specific implementation of Widget data
 * (all non-Swing variables defined in ComponentData)
 */
public class SwingData extends GUIData {

    //used to enable work around for bug in JDK1.6.0_10+
    //NEEDS to be public
    public static boolean JVMBugRightAlignFix;

    CustomFormPrint customFormPrint;

    JFrame dummyPanel;

    boolean g2SwingRenderComplete;

    //panel components attached to
    private JPanel panel;

    //scaling used for readOnly text icons drawn as images
    public static int readOnlyScaling = -1;

    public SwingData() {
    }

    @Override
    public void dispose() {
        super.dispose();

        if (dummyPanel != null) {
            if (SwingUtilities.isEventDispatchThread()) {
                dummyPanel.dispose();
            } else {
                try {
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            dummyPanel.dispose();
                        }
                    });
                } catch (final InvocationTargetException e) {
                    e.printStackTrace();
                } catch (final InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * render component onto G2 for print of image creation
     *
     * @param printcombo = tells us to print the raw combobox, and dont do aymore formatting of the combobox, should only be called from this method.
     */
    private void renderComponent(final Graphics2D g2, final FormObject formObject, final Component comp, final int rotation, boolean printcombo, final int indent, final boolean isPrinting) {

        if (comp != null) {

            boolean editable = false;
            final int page = formObject.getPageNumber();

            if (!printcombo && comp instanceof JComboBox) {

                //if we have the comobobox, adapt so we see what we want to
                //for the combobox we need to print the first item within it otherwise we doent see the contents.
                final JComboBox combo = (JComboBox) comp;

                if (combo.isEditable()) {
                    editable = true;
                    combo.setEditable(false);
                }

                if (combo.getComponentCount() > 0) {
                    final Object selected = combo.getSelectedItem();
                    if (selected != null) {

                        final JTextField text = new JTextField();

                        text.setText(selected.toString());

                        text.setBackground(combo.getBackground());
                        text.setForeground(combo.getForeground());
                        text.setFont(combo.getFont());

                        text.setBorder(combo.getBorder());

                        renderComponent(g2, formObject, text, rotation, false, indent, isPrinting);
                    }
                }

                //set flag to say this is the combobox.
                //(we dont want to print this, as we have printed it as a textfield )
                printcombo = true;
            }

            if (!printcombo) {

                final AffineTransform ax = g2.getTransform();

                //when true works on printing,
                //whnen false works for testrenderer, on most except eva_subjob_quer.pdf
                if (isPrinting) {
                    //if we dont have the combobox print it
                    scaleComponent(formObject, 1, rotation, comp, false, indent, isPrinting);

                    //work out new translate after rotate deduced from FixImageIcon
                    final AffineTransform at;
                    switch (360 - rotation) {
                        case 270:
                            at = AffineTransform.getRotateInstance(
                                    (270 * java.lang.Math.PI) / 180, 0, 0);
                            g2.translate(comp.getBounds().y + cropOtherY[page] - insetH,
                                    pageData.getCropBoxHeight(page) - comp.getBounds().x + insetW);


                            g2.transform(at);
                            g2.translate(-insetW, 0);

                            break;
                        case 90:
                            at = AffineTransform.getRotateInstance(
                                    (90 * java.lang.Math.PI) / 180, 0, 0);
                            g2.translate(comp.getBounds().y + cropOtherY[page] - insetH,
                                    comp.getBounds().x + insetW);


                            g2.transform(at);
                            g2.translate(0, -insetH);
                            break;
                        case 180://not tested
                            at = AffineTransform.getRotateInstance(
                                    (180 * java.lang.Math.PI) / 180, 0, 0);
                            //translate to x,y of comp before applying rotate.
                            g2.translate(comp.getBounds().x - insetW, comp.getBounds().y + cropOtherY[page]);

                            g2.transform(at);
                            g2.translate(-insetW, -insetH); //will prob need this to work

                            break;
                        default:
                            //translate to x,y of comp before applying rotate.
                            g2.translate(comp.getBounds().x - insetW, comp.getBounds().y + cropOtherY[page]);
                            break;
                    }
                } else { //used for testrenderer, images

                    //if we dont have the combobox print it
                    scaleComponent(formObject, 1, rotation, comp, false, indent, isPrinting);

                    Rectangle rect = comp.getBounds();

                    //translate to x,y of comp before applying rotate.
                    g2.translate(rect.x - insetW, rect.y + cropOtherY[page]);

                    //only look at rotate on text fields as other fields should be handled.
                    if (comp instanceof JTextComponent) {
                        if (pageData.getRotation(page) == 90 || pageData.getRotation(page) == 270) {
                            comp.setBounds(rect.x, rect.y, rect.height, rect.width);
                            rect = comp.getBounds();
                        }

                        //fix for file eva_subjob_quer.pdf as it has page rotations 90 0 90 0, which makes
                        //page 1 and 3 print wrong when using each pages rotation value.
                        int rotate = rotation - pageData.getRotation(0);
                        if (rotate < 0) {
                            rotate = 360 + rotate;
                        }

                        //work out new translate after rotate deduced from FixImageIcon
                        final AffineTransform at;
                        switch (rotate) {
                            case 270:
                                at = AffineTransform.getRotateInstance(
                                        (rotate * java.lang.Math.PI) / 180, 0, 0);
                                g2.transform(at);
                                g2.translate(-rect.width, 0);
                                break;
                            case 90://not tested
                                at = AffineTransform.getRotateInstance(
                                        (rotate * java.lang.Math.PI) / 180, 0, 0);
                                g2.transform(at);
                                g2.translate(0, -rect.height);

                                break;
                            case 180://not tested
                                at = AffineTransform.getRotateInstance(
                                        (rotate * java.lang.Math.PI) / 180, 0, 0);
                                g2.transform(at);
                                g2.translate(-rect.width, -rect.height);

                                break;
                        }
                    }
                }

                //fix for bug in Java 1.6.0_10 onwards with right aligned values
                boolean isPainted = false;

                //hack for a very sepcific issue so rather leave
                //Rog's code intack and take out for ME
                if (JVMBugRightAlignFix && comp instanceof JTextField) {

                    final JTextField field = new JTextField();
                    final JTextField source = (JTextField) comp;

                    if (source.getHorizontalAlignment() == JTextField.RIGHT) {

                        field.setFont(source.getFont());
                        field.setLocation(source.getLocation());
                        field.setSize(source.getSize());
                        field.setBorder(source.getBorder());
                        field.setHorizontalAlignment(JTextField.RIGHT);

                        //Rog's modified code
                        int additionalBlanks = 0;
                        int width = g2.getFontMetrics(comp.getFont()).stringWidth(new
                                String(createCharArray(' ', maxLengthForTextOnPage -
                                source.getText().length())) + source.getText());
                        final int eightPointWidth =
                                g2.getFontMetrics(comp.getFont().deriveFont(7.0F)).stringWidth(new
                                        String(createCharArray(' ', maxLengthForTextOnPage -
                                        source.getText().length())) + source.getText());
                        final int difference = width - eightPointWidth;
                        if (difference > 0) {
                            additionalBlanks = difference /
                                    g2.getFontMetrics(comp.getFont().deriveFont(7.0F)).stringWidth(" ");
                        }
                        final String originalTest = source.getText();
                        int bunchOfSpaces = (maxLengthForTextOnPage +
                                additionalBlanks) - source.getText().length();
                        field.setText(new String(createCharArray(' ',
                                bunchOfSpaces)) + originalTest);
                        width =
                                g2.getFontMetrics(comp.getFont()).stringWidth(field.getText());

                        int insets = 0;
                        if (field.getBorder() != null) {
                            insets = (field.getBorder().getBorderInsets(field).left + field.getBorder().getBorderInsets(field).right);
                        }
                        boolean needsChange = false;
                        String newText;
                        while (bunchOfSpaces > 0 && width > field.getWidth() - insets) {
                            bunchOfSpaces = (maxLengthForTextOnPage + additionalBlanks) - source.getText().length();
                            newText = new String(createCharArray(' ', bunchOfSpaces)) + originalTest;
                            field.setText(newText);
                            additionalBlanks--;
                            width = g2.getFontMetrics(comp.getFont().deriveFont(7.0F)).stringWidth(field.getText());
                            needsChange = true;
                        }

                        if (needsChange) {
                            additionalBlanks--;
                            bunchOfSpaces = (maxLengthForTextOnPage + additionalBlanks) - source.getText().length();
                            newText = new String(createCharArray(' ', bunchOfSpaces)) + originalTest;
                            field.setText(newText);
                        }

                        field.paint(g2);
                        isPainted = true;
                    }
                }

                if (!isPainted) {
                    if (SwingUtilities.isEventDispatchThread()) {
                        comp.paint(g2);
                    } else {
                        g2SwingRenderComplete = false;
                        SwingUtilities.invokeLater(new Runnable() {

                            @Override
                            public void run() {
                                comp.paint(g2);
                                g2SwingRenderComplete = true;
                            }
                        });

                        while (!g2SwingRenderComplete) {
                            try {
                                Thread.sleep(100);
                            } catch (final InterruptedException e) {
                                LogWriter.writeLog("Exception " + e.getMessage());
                            }
                        }

                        g2SwingRenderComplete = false;
                    }
                }

                //We need to set the popup back to the correct size otherwise they will be incorrectly sized
                if (isPrinting && comp instanceof JInternalFrame) {
                    scaleComponent(formObject, displayScaling, rotation, comp, false, indent, false);
                }

                g2.setTransform(ax);
            }

            if (editable) {
                ((JComboBox) comp).setEditable(true);
            }
        }
    }

    /**
     * render component onto G2 for print of image creation
     */
    private void renderComponent(final Graphics2D g2, final FormObject formObject, final boolean isPrinting) {

        final int page = formObject.getPageNumber();

        final AffineTransform ax = g2.getTransform();
        final Font backup = g2.getFont();
        final Stroke st = g2.getStroke();
        final Color old = g2.getColor();
        final Shape oldClip = g2.getClip();
        final Composite oldCom = g2.getComposite();

        //Translate to correct y coord for crop box
        g2.translate(0, pageData.getMediaBoxHeight(page) - pageData.getCropBoxHeight(page));

        if (formObject.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.Text) {

            final String name = formObject.getName(PdfDictionary.Name);

            if (name != null && name.equals("Comment")) {
                /* Name of the icon image to use for the icon of this annotation
                 * - predefined icons are needed for names:-
                 * Comment, Key, Note, Help, NewParagraph, Paragraph, Insert
                 */
                try {
                    final BufferedImage commentIcon = ImageIO.read(getClass().getResource("/org/jpedal/objects/acroforms/res/comment.png"));
                    g2.drawImage(commentIcon, formObject.getBoundingRectangle().x, pageData.getCropBoxHeight(page) - formObject.getBoundingRectangle().y, formObject.getBoundingRectangle().width, formObject.getBoundingRectangle().height, null);
                } catch (final Exception e) {
                    LogWriter.writeLog("Exception: " + e.getMessage());
                }
            }

            if (formObject.getFloatArray(PdfDictionary.C) == null) {
                formObject.setFloatArray(PdfDictionary.C, new float[]{1.0f, 1.0f, 0.0f});
            }

        }

        final float[] col = formObject.getFloatArray(PdfDictionary.C);
        Color bgColor = null;
        if (col != null) {
            if (col[0] > 1 || col[1] > 1 || col[2] > 1) {
                bgColor = new Color((int) col[0], (int) col[1], (int) col[2]);
            } else {
                bgColor = new Color(col[0], col[1], col[2]);
            }
        }

        if (formObject.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.Popup
                && formObject.getBoolean(PdfDictionary.Open)) {
            FormRenderUtilsG2.renderPopupWindow(g2, formObject, bgColor, isPrinting, pageData.getCropBoxHeight(page));
        }


        //Revert back font before continuing
        g2.setFont(backup);


        if (AcroRenderer.isAnnotation(formObject) && formObject.getParameterConstant(PdfDictionary.Subtype) != PdfDictionary.Widget) {
            final BufferedImage bi = AnnotationFactory.getIcon(formObject);
            g2.drawImage(bi, formObject.getBoundingRectangle().x, pageData.getCropBoxHeight(page) - (formObject.getBoundingRectangle().y + formObject.getBoundingRectangle().height), null);
        } else {

            /*
             * Type must be Border W width in points (if 0 no border, default =1) S
             * style - (default =S) S=solid, D=dashed (pattern specified by D entry
             * below), B=beveled(embossed appears to above page), I=inset(engraved
             * appeared to be below page), U=underline ( single line at bottom of
             * boundingbox) D array phase - e.g. [a b] c means:- a=on blocks,b=off
             * blocks(if not present default to a), c=start of off block preseded
             * index is on block. i.e. [4] 6 :- 4blocks on 4blocks off, block[6] if
             * off - 1=off 2=on 3=on 4=on 5=on 6=off 7=off 8=off 9=off etc...
             *
             */
            final int borderWidth = FormRenderUtilsG2.renderBorder(g2, formObject, pageData.getCropBoxHeight(page));

            //Revert back stroke before continuing
            g2.setStroke(st);

            final String textValue = formObject.getValue();
            if (textValue != null) {

                final FontMetrics metrics = FormRenderUtilsG2.renderFont(g2, formObject, textValue, borderWidth);

                //Text is drawn from the baseline so inorder to draw the highlights
                //correctly we need to add te fonts decent
                int justification = formObject.getAlignment();
                final Rectangle2D r = metrics.getStringBounds(textValue, g2);

                //Always center button output
                if (formObject.getFieldFlags()[FormObject.PUSHBUTTON_ID]) {
                    justification = 0;
                }

                if (formObject.getObjectArray(PdfDictionary.Opt) != null && !formObject.getFieldFlags()[FormObject.COMBO_ID]) {
                    FormRenderUtilsG2.renderComboForms(g2, formObject, metrics, r, borderWidth, justification, pageData.getCropBoxHeight(page));
                } else {
                    if (!textValue.isEmpty()) {
                        g2.setClip(new Rectangle(formObject.getBoundingRectangle().x + borderWidth - 1,
                                pageData.getCropBoxHeight(page) - (formObject.getBoundingRectangle().y + formObject.getBoundingRectangle().height) + borderWidth - 1,
                                formObject.getBoundingRectangle().width - (borderWidth * 2) + 2,
                                formObject.getBoundingRectangle().height - (borderWidth * 2) + 2));

                        if (formObject.getFieldFlags()[FormObject.MULTILINE_ID]) {
                            FormRenderUtilsG2.renderMultilineTextField(g2, formObject, metrics, r, textValue, borderWidth, justification, pageData.getCropBoxHeight(page));
                        } else { //Single Line Field
                            FormRenderUtilsG2.renderSingleLineTextField(g2, formObject, metrics, r, textValue, borderWidth, justification, pageData.getCropBoxHeight(page));
                        }
                    }
                }
            }

            FormRenderUtilsG2.renderQuadPoint(g2, formObject, bgColor, pageData.getCropBoxHeight(page));
        }

        g2.setTransform(ax);
        g2.setFont(backup);
        g2.setStroke(st);
        g2.setColor(old);
        g2.setClip(oldClip);
        g2.setComposite(oldCom);
    }

    /**
     * used by fix above
     *
     * @param c
     * @param count
     * @return
     */
    private static char[] createCharArray(final char c, final int count) {
        if (count <= 0) {
            return new char[0];
        }
        final char[] result = new char[count];
        Arrays.fill(result, 0, result.length, c);
        return result;
    }

    int maxLengthForTextOnPage;

    /**
     * draw the forms onto display for print of image. Note different routine to
     * handle forms also displayed at present
     */
    @Override
    public void renderFormsOntoG2(final Object raw, final int pageIndex, final int currentIndent,
                                  final int currentRotation, final Map componentsToIgnore, final FormFactory formFactory, final int pageHeight) {

        if (formsUnordered == null || rasterizeForms) {
        } else if (this.formFactory.getType() == FormFactory.HTML || this.formFactory.getType() == FormFactory.SVG) {
            renderFormsOntoG2WithHTML(pageIndex, componentsToIgnore);
        } else if (GraphicsEnvironment.isHeadless() || formFactory == null) ///mark - make 1==1 to enable
        {
            renderFormsOntoG2InHeadless(raw, pageIndex, componentsToIgnore, formFactory);
        } else {
            renderFormsOntoG2WithSwing(raw, pageIndex, currentIndent, currentRotation, componentsToIgnore, formFactory, pageHeight);
        }

    }

    /**
     * Draw forms without swing
     */
    @Override
    public void renderFormsOntoG2InHeadless(final Object raw, final int pageIndex, final Map componentsToIgnore, final FormFactory formFactory) {

        if (formsOrdered == null || formsOrdered[pageIndex] == null) {
            return;
        }

        this.componentsToIgnore = componentsToIgnore;

        FormObject formObject;

        //only passed in on print so also used as flag
        final boolean isPrinting = formFactory != null;

        final Graphics2D g2 = (Graphics2D) raw;

        final AffineTransform defaultAf = g2.getTransform();

        // setup scaling
        final AffineTransform aff = g2.getTransform();
        aff.scale(1, 1);
        g2.setTransform(aff);

        //get unsorted components and iterate over forms
        for (final FormObject nextVal : formsOrdered[pageIndex]) {

            if (nextVal != null) {

                formObject = nextVal;

                //is this form allowed to be printed
                final boolean[] flags = formObject.getCharacteristics();
                if (((flags[1] || (isPrinting && !flags[2])))) { //1 hidden, 2 print (hense !)
                    continue;
                }

                renderComponent(g2, formObject, isPrinting);

            }
        }

        g2.setTransform(defaultAf);

    }

    /**
     * use Swing to draw forms
     */
    private void renderFormsOntoG2WithSwing(final Object raw, final int pageIndex, final int currentIndent, final int currentRotation, final Map componentsToIgnore, final FormFactory formFactory, final int pageHeight) {


        this.componentsToIgnore = componentsToIgnore;

        Component comp;
        FormObject formObject;

        //only passed in on print so also used as flag
        final boolean isPrinting = formFactory != null;

        //fix for issue with display of items in 1.6.0_10+
        if (JVMBugRightAlignFix && isPrinting) {

            maxLengthForTextOnPage = 0;

            //get unsorted components and iterate over forms
            for (final FormObject o : formsOrdered[pageIndex]) {

                if (o != null) {

                    formObject = o;

                    comp = (Component) checkGUIObjectResolved(formObject);

                    if (comp instanceof JTextField) {
                        final JTextField text = (JTextField) comp;
                        final int newLength = text.getText().length();

                        if (newLength > maxLengthForTextOnPage && text.getHorizontalAlignment() == JTextField.RIGHT) {
                            maxLengthForTextOnPage = newLength;
                        }
                    }
                }
            }
        }

        final Graphics2D g2 = (Graphics2D) raw;

        final AffineTransform defaultAf = g2.getTransform();

        // setup scaling
        final AffineTransform aff = g2.getTransform();
        aff.scale(1, -1);
        aff.translate(0, -pageHeight - insetH);
        g2.setTransform(aff);

        if (dummyPanel == null) {
            dummyPanel = new JFrame();
            dummyPanel.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            dummyPanel.pack();
        }
        //get unsorted components and iterate over forms
        for (final FormObject nextVal : formsOrdered[pageIndex]) {

            if (nextVal != null) {

                formObject = nextVal;


                //is this form allowed to be printed
                final boolean[] flags = formObject.getCharacteristics();
                if (((flags[1] || (isPrinting && !flags[2])))) { //1 hidden, 2 print (hense !)
                    continue;
                }

                checkGUIObjectResolved(formObject);

                comp = (Component) formObject.getGUIComponent();

                if (comp != null && comp.isVisible()) {

                    final Rectangle bounds = formObject.getBoundingRectangle();
                    final float boundHeight = bounds.height;

                    final int swingHeight = comp.getPreferredSize().height + 6;

                    if (this.componentsToIgnore != null &&
                            (this.componentsToIgnore.containsKey(formObject.getParameterConstant(PdfDictionary.Subtype)) ||
                                    this.componentsToIgnore.containsKey(formObject.getParameterConstant(PdfDictionary.Type)))) {
                    } else if (comp instanceof JList && ((JList) comp).getSelectedIndex() != -1 && boundHeight < swingHeight) {

                        final JList comp2 = (JList) comp;

                        dummyPanel.add(comp);

                        final ListModel model = comp2.getModel();
                        final Object[] array = new Object[model.getSize()];

                        final int selectedIndex = comp2.getSelectedIndex();
                        int c = 0;
                        array[c++] = model.getElementAt(selectedIndex);

                        for (int i = 0; i < array.length; i++) {
                            if (i != selectedIndex) {
                                array[c++] = model.getElementAt(i);
                            }
                        }

                        comp2.setListData(array);
                        comp2.setSelectedIndex(0);

                        renderComponent(g2, formObject, comp2, currentRotation, false, currentIndent, isPrinting);
                        dummyPanel.remove(comp2);

                    } else {

                        boolean customPrintoverRide = false;
                        if (customFormPrint != null) {

                            //setup scalings
                            scaleComponent(formObject, 1, rotation, comp, false, indent, isPrinting);
                            customPrintoverRide = customFormPrint.print(g2, formObject, this);

                        }

                        if (!customPrintoverRide) {
                            //this is where the cust1/display_error file line went, but it affects costena printing.
                            if (comp instanceof AbstractButton) {
                                final Object obj = ((AbstractButton) comp).getIcon();

                                if (obj != null) {
                                    if (obj instanceof FixImageIcon) {
                                        ((FixImageIcon) (obj)).setPrinting(true, 1);
                                    } else if (readOnlyScaling > 0 && obj instanceof ReadOnlyTextIcon) {
                                        ((ReadOnlyTextIcon) (obj)).setPrinting(true, readOnlyScaling);
                                    }
                                }
                            }
                            dummyPanel.add(comp);

                            renderComponent(g2, formObject, comp, currentRotation, false, currentIndent, isPrinting);
                            dummyPanel.remove(comp);

                            if (comp instanceof AbstractButton) {
                                final Object obj = ((AbstractButton) comp).getIcon();
                                if (obj instanceof FixImageIcon) {
                                    ((FixImageIcon) (obj)).setPrinting(false, 1);
                                } else if (obj instanceof ReadOnlyTextIcon) {
                                    ((ReadOnlyTextIcon) (obj)).setPrinting(false, 1);
                                }
                            }
                        }
                    }
                }
            }
        }

        g2.setTransform(defaultAf);

        // put componenents back
        if (currentPage == pageIndex && panel != null) {
            resetScaledLocation(displayScaling, rotation, indent);
        }

    }


    /**
     * use Swing to draw forms
     */
    private void renderFormsOntoG2WithHTML(final int pageIndex, final Map componentsToIgnore) {

        this.componentsToIgnore = componentsToIgnore;

        FormObject formObject;

        //get unsorted components and iterate over forms
        for (final FormObject nextVal : formsOrdered[pageIndex]) {

            if (nextVal != null) {

                formObject = nextVal;
                checkGUIObjectResolved(formObject);
            }
        }
    }

    /**
     * alter font and size to match scaling. Note we pass in compoent so we can
     * have multiple copies (needed if printing page displayed).
     */
    private void scaleComponent(final FormObject formObject, final float scale, final int rotate, final Component curComp, final boolean redraw, int indent, final boolean isPrinting) {

        if (curComp == null || formObject.getPageNumber() == -1) {
            return;
        }

        final int curPage = formObject.getPageNumber();

        //work out if visible in Layer
        if (layers != null) {

            final String layerName = formObject.getLayerName();

            if (layerName != null && layers.isLayerName(layerName)) {
                final boolean isVisible = layers.isVisible(layerName);
                curComp.setVisible(isVisible);
            }
        }

        final int[] bounds;

        if (formObject.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.Popup && !isPrinting) {
            bounds = cropComponent(formObject, scale, rotate, redraw, true);
        } else {
            bounds = cropComponent(formObject, scale, rotate, redraw, false);
        }

        //rescale the font size
        final Font resetFont = curComp.getFont();
        if (resetFont != null) {
            //send in scale, rotation, and curComp as they could be from the print routines,
            //which define these parameters.
            //FreeText is a special case so handle here
            if (formObject.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.FreeText) {
                final byte[] DSString = formObject.getTextStreamValueAsByte(PdfDictionary.DS);
                if (DSString != null) {
                    AnnotationFactory.loadFontValues(DSString, curComp, scale);
                } else {
                    curComp.setFont(curComp.getFont().deriveFont(formObject.getFontSize()));
                }
            } else {
                if (formObject.getParameterConstant(PdfDictionary.Subtype) != PdfDictionary.Popup) {
                    recalcFontSize(scale, rotate, formObject, curComp);
                } else {
                    if (isPrinting) {
                        curComp.setFont(curComp.getFont().deriveFont(formObject.getFontSize() * (72.0f / 96.0f)));

                    } else {
                        curComp.setFont(curComp.getFont().deriveFont(formObject.getFontSize()));
                    }
                }
            }
        }

        //scale border if needed
        if ((curComp instanceof JComponent && ((JComponent) curComp).getBorder() != null) &&
                (formObject != null)) {
            ((JComponent) curComp).setBorder(generateBorderfromForm(formObject, scale));
        }

        //factor in offset if multiple pages displayed
        if (xReached != null) {
            bounds[0] += xReached[curPage];
            bounds[1] += yReached[curPage];
        }

        final int pageWidth;
        if ((pageData.getRotation(curPage) + rotate) % 180 == 90) {
            pageWidth = pageData.getCropBoxHeight(curPage);
        } else {
            pageWidth = pageData.getCropBoxWidth(curPage);
        }

        if (displayView == Display.CONTINUOUS) {
            final double newIndent;
            if (rotate == 0 || rotate == 180) {
                newIndent = (widestPageNR - (pageWidth)) / 2;
            } else {
                newIndent = (widestPageR - (pageWidth)) / 2;
            }

            indent = (int) (indent + (newIndent * scale));
        }

        final int totalOffsetX = userX + indent + insetW;
        final int totalOffsetY = userY + insetH;

        final Rectangle boundRect = new Rectangle(totalOffsetX + bounds[0], totalOffsetY + bounds[1], bounds[2], bounds[3]);

        curComp.setBounds(boundRect);

        //rescale the icons if any
        if (curComp instanceof AbstractButton) {
            final AbstractButton but = ((AbstractButton) curComp);

            Icon curIcon = but.getIcon();

            boolean displaySingle = false;
            if (displayView == Display.SINGLE_PAGE || displayView == Display.NODISPLAY) {
                displaySingle = true;
            }

            if (curIcon instanceof FixImageIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            } else if (curIcon instanceof ReadOnlyTextIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            }

            curIcon = but.getPressedIcon();
            if (curIcon instanceof FixImageIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            }

            curIcon = but.getSelectedIcon();
            if (curIcon instanceof FixImageIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            }

            curIcon = but.getRolloverIcon();
            if (curIcon instanceof FixImageIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            }

            curIcon = but.getRolloverSelectedIcon();
            if (curIcon instanceof FixImageIcon) {
                ((CustomImageIcon) curIcon).setAttributes(curComp.getWidth(), curComp.getHeight(), rotate, displaySingle);
            }

        }
    }

    /**
     * we take in curComp as it could be a JTextField showing the selected value from a JComboBox
     * also the scale and rotation could be from a print routine and not the same as the global variables
     */
    private static void recalcFontSize(final float scale, final int rotate, final FormObject formObject, final Component curComp) {

        final int size = GUIData.getFontSize(formObject, rotate, scale);

        final Font resetFont = curComp.getFont();
        final Font newFont = new Font(resetFont.getFontName(), resetFont.getStyle(), size);

        curComp.setFont(newFont);
    }

    /**
     * allows the text to be autosized at any point from anywhere by only knowing the ref.
     */
    @Override
    public void setAutoFontSize(final FormObject formObject) {
        recalcFontSize(displayScaling, rotation, formObject, (Component) formObject.getGUIComponent());
    }

    /**
     * returns Border as is swing specific class
     */
    public static Border generateBorderfromForm(final FormObject form, final float scaling) {
        float[] BC = form.getDictionary(PdfDictionary.MK).getFloatArray(PdfDictionary.BC);
        if (BC == null && form.getParameterConstant(PdfDictionary.Subtype) == PdfDictionary.Screen) {
            BC = form.getFloatArray(PdfDictionary.C);
        }

        Border newBorder = JPedalBorderFactory.createBorderStyle(form.getDictionary(PdfDictionary.BS),
                FormObject.generateColor(BC),
                Color.white, scaling);

        if (form.isXFAObject()) {
            final int[] t = form.getMatteBorderDetails();
            newBorder = BorderFactory.createMatteBorder(t[0], t[3], t[2], t[1], Color.black);
        }
        return newBorder;
    }

    private int[] cropComponent(final FormObject formObject, final float s, int r, final boolean redraw, final boolean positionOnly) {

        final Rectangle rect = formObject.getBoundingRectangle();
        final int curPage = formObject.getPageNumber();

        final float[] box = {rect.x, rect.y, rect.width + rect.x, rect.height + rect.y};

        //NOTE if needs adding in ULC check SpecialOptions.SINGLE_PAGE
        if (displayView != Display.SINGLE_PAGE && displayView != Display.NODISPLAY) {
            r = (r + pageData.getRotation(curPage)) % 360;
        }

        final int cropX = pageData.getCropBoxX(curPage);
        final int cropY = pageData.getCropBoxY(curPage);
        final int cropW = pageData.getCropBoxWidth(curPage);

        final int mediaW = pageData.getMediaBoxWidth(curPage);
        final int mediaH = pageData.getMediaBoxHeight(curPage);

        final int cropOtherX = (mediaW - cropW - cropX);

        float x100 = 0, y100 = 0, w100 = 0, h100 = 0;
        final int x;
        final int y;
        final int w;
        final int h;

        switch (r) {
            case 0:
                x100 = box[0];
                //if we are drawing on screen take off cropX if printing or extracting we dont need to do this.
                if (redraw) {
                    x100 -= cropX;
                }

                y100 = mediaH - box[3] - cropOtherY[curPage] + 1;
                w100 = (box[2] - box[0]);
                h100 = (box[3] - box[1]);

                break;
            case 90:
                x100 = box[1] - cropY;
                y100 = box[0] - cropX + 1;
                if (!positionOnly) {
                    w100 = (box[3] - box[1]);
                    h100 = (box[2] - box[0]);
                } else {
                    w100 = (box[2] - box[0]);
                    h100 = (box[3] - box[1]);
                }
                break;
            case 180:
                w100 = box[2] - box[0];
                h100 = box[3] - box[1];
                if (!positionOnly) {
                    y100 = box[1] - cropY + 1;
                    x100 = mediaW - box[2] - cropOtherX;
                } else {
                    w100 = (box[2] - box[0]);
                    h100 = (box[3] - box[1]);
                }
                break;
            case 270:
                if (!positionOnly) {
                    w100 = (box[3] - box[1]);
                    h100 = (box[2] - box[0]);
                } else {
                    w100 = (box[2] - box[0]);
                    h100 = (box[3] - box[1]);
                }
                x100 = mediaH - box[3] - cropOtherY[curPage];
                y100 = mediaW - box[2] - cropOtherX + 1;
                break;
        }

        x = (int) (x100 * s);
        y = (int) (y100 * s);
        if (!positionOnly) {
            w = (int) (w100 * s);
            h = (int) (h100 * s);
        } else {
            //Don't forget to factor in the resolution of the display
            w = (int) (w100 * dpi / 72);
            h = (int) (h100 * dpi / 72);
        }
        return new int[]{x, y, w, h};
    }

    /**
     * used to remove all components from display
     */
    @Override
    protected void removeAllComponentsFromScreen() {

        //System.out.println("removeAllComponentsFromScreen");
        //01032012 be care if you ever re-enable as big performace hit on Abacus code (see slow.pdf)

        //		Iterator formIter = rawFormData.values().iterator();
        //		while(formIter.hasNext()){
        //			FormObject formObj = (FormObject)formIter.next();
        //			pdfDecoder.getFormRenderer().getActionHandler().PI(formObj,PdfDictionary.AA);
        //			pdfDecoder.getFormRenderer().getActionHandler().PI(formObj,PdfDictionary.A);
        //			pdfDecoder.getFormRenderer().getActionHandler().PC(formObj,PdfDictionary.AA);
        //			pdfDecoder.getFormRenderer().getActionHandler().PC(formObj,PdfDictionary.A);
        //		}

        if (panel != null) {
            if (SwingUtilities.isEventDispatchThread()) {
                panel.removeAll();
            } else {
                final Runnable doPaintComponent = new Runnable() {
                    @Override
                    public void run() {
                        panel.removeAll();
                    }
                };
                SwingUtilities.invokeLater(doPaintComponent);
            }
        }

    }

    /**
     * pass in object components drawn onto
     *
     * @param rootComp
     */
    @Override
    public void setRootDisplayComponent(final Object rootComp) {
        if (SwingUtilities.isEventDispatchThread()) {
            panel = (JPanel) rootComp;
        } else {
            final Runnable doPaintComponent = new Runnable() {
                @Override
                public void run() {
                    panel = (JPanel) rootComp;
                }
            };
            SwingUtilities.invokeLater(doPaintComponent);
        }
    }

    @Override
    public void setGUIComp(final FormObject formObject, final Object rawField) {

        final Component retComponent = (Component) rawField;

        //append state to name so we can retrieve later if needed
        String name2 = formObject.getTextStreamValue(PdfDictionary.T);
        if (name2 != null) { //we have some empty values as well as null
            final String stateToCheck = formObject.getNormalOnState();
            if (stateToCheck != null && !stateToCheck.isEmpty()) {
                name2 = name2 + "-(" + stateToCheck + ')';
            }

            retComponent.setName(name2);
        }

        //make visible
        scaleComponent(formObject, displayScaling, rotation, retComponent, true, indent, false);

    }

    /**
     * alter location and bounds so form objects show correctly scaled
     */
    @Override
    public void resetScaledLocation(final float currentScaling, final int currentRotation, final int currentIndent) {

        // we get a spurious call in linux resulting in an exception
        if (formsUnordered == null || panel == null || startPage == 0) {
            return;
        }

        // needed as code called recursively otherwise
        if (forceRedraw || currentScaling != lastScaling || currentRotation != oldRotation || currentIndent != oldIndent) { // || SwingUtilities.isEventDispatchThread()) {

            oldRotation = currentRotation;
            lastScaling = currentScaling;
            oldIndent = currentIndent;
            forceRedraw = false;

            FormObject formObject;
            Component rawComp;
            int count;

            for (int currentPage = startPage; currentPage < endPage; currentPage++) {

                if (formsOrdered[currentPage] == null) {
                    count = 0;
                } else {
                    count = formsOrdered[currentPage].size();
                }

                // reset all locations
                for (int j = 0; j < count; j++) {

                    formObject = formsOrdered[currentPage].get(j); //example where order matters see 19071

                    formObject.setCurrentScaling(currentScaling);
                    rawComp = (Component) formObject.getGUIComponent();

                    if (rawComp != null) {
                        if (formObject.getBoundingRectangle().height < rawComp.getPreferredSize().height && rawComp instanceof JList) { //rawComp!=null check for xfa

                            final JList comp = (JList) rawComp;

                            rawComp = wrapComponentInScrollPane(comp);
                            formObject.setGUIComponent(comp, FormFactory.SWING);

                            // ensure visible (do it before we add)
                            final int index = comp.getSelectedIndex();
                            if (index > -1) {
                                comp.ensureIndexIsVisible(index);
                            }

                        }

                        if (SwingUtilities.isEventDispatchThread()) {

                            panel.remove(rawComp);
                            scaleComponent(formObject, currentScaling, currentRotation, rawComp, true, indent, false);
                            panel.add(rawComp);

                        } else {
                            final Component finalComp = rawComp;
                            final FormObject fo = formObject;
                            final Runnable doPaintComponent = new Runnable() {
                                @Override
                                public void run() {
                                    panel.remove(finalComp);
                                    scaleComponent(fo, currentScaling, currentRotation, finalComp, true, indent, false);
                                    panel.add(finalComp);
                                }
                            };
                            SwingUtilities.invokeLater(doPaintComponent);
                        }
                    }
                }
            }
            //Validate after adding components to ensure they appear.
            //Popups will sometimes vanish otherwise.
            panel.validate();
        }
    }

    /**
     * forms on invisible pages need removing as we do not update their position and they will appear when we scroll otherwise
     *
     * @param startPage
     * @param endPage
     */
    @Override
    void removeHiddenForms(final int startPage, final int endPage) {

        FormObject formObject;
        Object comp;

        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 = formObject.getGUIComponent();

                        if (comp != null) {
                            panel.remove((Component) comp);
                        }
                    }
                }
            }
        }
    }

    private static Component wrapComponentInScrollPane(final JList comp) {

        final JScrollPane scroll = new JScrollPane(comp);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scroll.setLocation(comp.getLocation());
        scroll.setPreferredSize(comp.getPreferredSize());
        scroll.setSize(comp.getSize());

        return scroll;
    }

    @Override
    protected void displayComponent(final FormObject formObject, final Object comp) {

        if (SwingUtilities.isEventDispatchThread()) {

            scaleComponent(formObject, displayScaling, rotation, (Component) comp, true, indent, false);

        } else {

            final Runnable doPaintComponent = new Runnable() {
                @Override
                public void run() {
                    scaleComponent(formObject, displayScaling, rotation, (Component) comp, true, indent, false);
                }
            };
            SwingUtilities.invokeLater(doPaintComponent);
        }
    }

    @Override
    public void setCustomPrintInterface(final CustomFormPrint customFormPrint) {
        this.customFormPrint = customFormPrint;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy