r4fterman/pdf.forms

View on GitHub
src/main/java/org/pdf/forms/widgets/Widget.java

Summary

Maintainability
D
2 days
Test Coverage
F
50%
package org.pdf.forms.widgets;

import java.awt.*;
import java.awt.font.TextAttribute;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.swing.*;
import javax.swing.border.Border;

import org.apache.commons.text.StringEscapeUtils;
import org.jpedal.objects.acroforms.creation.JPedalBorderFactory;
import org.pdf.forms.fonts.FontHandler;
import org.pdf.forms.gui.designer.listeners.DesignerMouseMotionListener;
import org.pdf.forms.model.des.BindingProperties;
import org.pdf.forms.model.des.Borders;
import org.pdf.forms.model.des.CaptionProperties;
import org.pdf.forms.model.des.FontCaption;
import org.pdf.forms.model.des.FontProperties;
import org.pdf.forms.model.des.JavaScriptContent;
import org.pdf.forms.model.des.ParagraphCaption;
import org.pdf.forms.model.des.ParagraphProperties;
import org.pdf.forms.model.des.SizeAndPosition;
import org.pdf.forms.widgets.components.IPdfComponent;
import org.pdf.forms.widgets.components.PdfCaption;
import org.pdf.forms.widgets.components.SplitComponent;
import org.pdf.forms.widgets.utils.WidgetFactory;
import org.pdf.forms.widgets.utils.WidgetSelection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Widget implements IWidget {

    private final Logger logger = LoggerFactory.getLogger(Widget.class);

    private final org.pdf.forms.model.des.Widget widget;
    private final JComponent baseComponent;
    private final int type;
    private final Icon icon;
    private final FontHandler fontHandler;

    private JComponent component;
    private boolean isComponentSplit;
    private boolean allowEditCaptionAndValue;
    private boolean allowEditOfCaptionOnClick;
    private String widgetName;
    private int arrayNumber;
    private int lastX;
    private int lastY;
    private double resizeHeightRatio;
    private double resizeWidthRatio;
    private double resizeFromTopRatio;
    private double resizeWidthFromLeftRatio;

    Widget(
            final org.pdf.forms.model.des.Widget widget,
            final int type,
            final JComponent baseComponent,
            final JComponent component,
            final String iconLocation,
            final FontHandler fontHandler) {
        this.widget = widget;
        this.type = type;
        this.baseComponent = baseComponent;
        this.component = component;
        this.fontHandler = fontHandler;
        this.icon = new ImageIcon(getClass().getResource(iconLocation));

//        final SizeAndPosition sizeAndPosition = widget.getProperties().getLayout().getSizeAndPosition();
//        if (sizeAndPosition.getX().map(Integer::valueOf).orElse(1).equals(1)) {
//            sizeAndPosition.setX(component.getX());
//        }
//        if (sizeAndPosition.getY().map(Integer::valueOf).orElse(1).equals(1)) {
//            sizeAndPosition.setY(component.getY());
//        }
//        if (sizeAndPosition.getWidth().map(Integer::valueOf).orElse(1).equals(1)) {
//            sizeAndPosition.setWidth(component.getWidth());
//        }
//        if (sizeAndPosition.getHeight().map(Integer::valueOf).orElse(1).equals(1)) {
//            sizeAndPosition.setHeight(component.getHeight());
//        }
    }

    @Override
    public JComponent getComponent() {
        return component;
    }

    @Override
    public Rectangle getBounds() {
        return new Rectangle(getX(), getY(), getWidth(), getHeight());
    }

    @Override
    public void setPosition(
            final int x,
            final int y) {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        sizeAndPosition.setX(x);
        sizeAndPosition.setY(y);
    }

    @Override
    public void setX(final int x) {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        sizeAndPosition.setX(x);
    }

    @Override
    public void setY(final int y) {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        sizeAndPosition.setY(y);
    }

    @Override
    public int getX() {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        return sizeAndPosition.getX().map(Integer::valueOf).orElse(1);
    }

    @Override
    public int getY() {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        return sizeAndPosition.getY().map(Integer::valueOf).orElse(1);
    }

    @Override
    public int getWidth() {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        return sizeAndPosition.getWidth().map(Integer::valueOf).orElse(1);
    }

    @Override
    public int getHeight() {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        return sizeAndPosition.getHeight().map(Integer::valueOf).orElse(1);
    }

    @Override
    public void setSize(
            final int width,
            final int height) {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();
        sizeAndPosition.setWidth(width);
        sizeAndPosition.setHeight(height);

        this.component = WidgetFactory.createResizedComponent(baseComponent, width, height);
    }

    @Override
    public boolean allowEditCaptionAndValue() {
        return allowEditCaptionAndValue;
    }

    @Override
    public boolean allowEditOfCaptionOnClick() {
        return allowEditOfCaptionOnClick;
    }

    @Override
    public Dimension getBoxSize() {
        return new Dimension(getWidth() + WidgetSelection.BOX_MARGIN * 2, getHeight() + WidgetSelection.BOX_MARGIN * 2);
    }

    @Override
    public int getResizeTypeForSplitComponent(
            final int mouseX,
            final int mouseY) {
        final SplitComponent sc = (SplitComponent) baseComponent;

        if (sc.getCaptionPosition() == SplitComponent.CAPTION_NONE) {
            return -1;
        }

        final int widgetX = getX();
        final int widgetY = getY();

        int resizeType = -1;

        final int dividorLocation = sc.getDividerLocation();
        final int orientation = sc.getOrientation();

        if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
            if (mouseY >= widgetY && mouseY <= widgetY + getHeight()
                    && mouseX > (widgetX + dividorLocation) - 3
                    && mouseX < (widgetX + dividorLocation + 3)) {

                resizeType = DesignerMouseMotionListener.RESIZE_SPLIT_HORIZONTAL_CURSOR;
            }
        } else {
            if (mouseX >= widgetX && mouseX <= widgetX + getWidth()
                    && mouseY > (widgetY + dividorLocation) - 3
                    && mouseY < (widgetY + dividorLocation + 3)) {

                resizeType = DesignerMouseMotionListener.RESIZE_SPLIT_VERTICAL_CURSOR;
            }
        }
        return resizeType;
    }

    @Override
    public JComponent getValueComponent() {
        final JComponent value;
        if (isComponentSplit) {
            value = ((SplitComponent) baseComponent).getValue();
        } else {
            value = baseComponent;
        }

        return value;
    }

    @Override
    public PdfCaption getCaptionComponent() {
        PdfCaption caption = null;
        if (isComponentSplit) {
            caption = ((SplitComponent) baseComponent).getCaption();
        }

        return caption;
    }

    @Override
    public void setLastX(final int lastX) {
        this.lastX = lastX;
    }

    @Override
    public void setLastY(final int lastY) {
        this.lastY = lastY;
    }

    @Override
    public int getLastX() {
        return lastX;
    }

    @Override
    public int getLastY() {
        return lastY;
    }

    @Override
    public Point getAbsoluteLocationsOfValue() {
        Point location = null;

        if (isComponentSplit) {
            final int captionPosition = ((SplitComponent) baseComponent).getCaptionPosition();

            switch (captionPosition) {
                case SplitComponent.CAPTION_LEFT:
                    location = new Point(getX() + getCaptionComponent().getBounds().width + SplitComponent.DIVIDER_SIZE,
                            getY());
                    break;
                case SplitComponent.CAPTION_TOP:
                    location = new Point(getX(),
                            getY() + getCaptionComponent().getBounds().height + SplitComponent.DIVIDER_SIZE);
                    break;
                case SplitComponent.CAPTION_RIGHT:
                case SplitComponent.CAPTION_BOTTOM:
                case SplitComponent.CAPTION_NONE:
                    location = new Point(getX(), getY());
                    break;
                default:
                    break;
            }
        }

        return location;
    }

    @Override
    public Point getAbsoluteLocationsOfCaption() {
        Point location = null;

        if (isComponentSplit) {
            final int captionPosition = ((SplitComponent) baseComponent).getCaptionPosition();

            switch (captionPosition) {
                case SplitComponent.CAPTION_LEFT:
                case SplitComponent.CAPTION_TOP:
                    location = new Point(getX(), getY());
                    break;
                case SplitComponent.CAPTION_RIGHT:
                    location = new Point(getX() + getValueComponent().getBounds().width + SplitComponent.DIVIDER_SIZE,
                            getY());
                    break;
                case SplitComponent.CAPTION_BOTTOM:
                    location = new Point(getX(),
                            getY() + getValueComponent().getBounds().height + SplitComponent.DIVIDER_SIZE);
                    break;
                default:
                    break;
            }
        }

        if (location == null) {
            location = new Point(getX(), getY());
        }

        return location;
    }

    @Override
    public boolean isComponentSplit() {
        return isComponentSplit;
    }

    @Override
    public void setResizeHeightRatio(final double resizeHeightRatio) {
        this.resizeHeightRatio = resizeHeightRatio;
    }

    @Override
    public void setResizeWidthRatio(final double resizeWidthRatio) {
        this.resizeWidthRatio = resizeWidthRatio;
    }

    @Override
    public double getResizeHeightRatio() {
        return resizeHeightRatio;
    }

    @Override
    public double getResizeWidthRatio() {
        return resizeWidthRatio;
    }

    @Override
    public void setResizeFromTopRatio(final double resizeFromTopRatio) {
        this.resizeFromTopRatio = resizeFromTopRatio;
    }

    @Override
    public void setResizeFromLeftRatio(final double resizeWidthFromLeftRatio) {
        this.resizeWidthFromLeftRatio = resizeWidthFromLeftRatio;
    }

    @Override
    public double getResizeFromTopRatio() {
        return resizeFromTopRatio;
    }

    @Override
    public double getResizeFromLeftRatio() {
        return resizeWidthFromLeftRatio;
    }

    @Override
    public List<IWidget> getWidgetsInGroup() {
        return Collections.emptyList();
    }

    @Override
    public void setWidgetsInGroup(final List<IWidget> widgetsInGroup) {
    }

    @Override
    public int getType() {
        return type;
    }

    @Override
    public String getWidgetName() {
        return widgetName;
    }

    @Override
    public int getArrayNumber() {
        return arrayNumber;
    }

    @Override
    public JavaScriptContent getJavaScript() {
        return widget.getJavaScript();
    }

    @Override
    public org.pdf.forms.model.des.Widget getWidgetModel() {
        return widget;
    }

    @Override
    public Icon getIcon() {
        return icon;
    }

    @Override
    public void setParagraphProperties(final int currentlyEditing) {
        // By default do nothing
    }

    @Override
    public void setLayoutProperties() {
        // By default do nothing
    }

    @Override
    public void setFontProperties(final int currentlyEditing) {
        // By default do nothing
    }

    protected void setFontProperties(final IPdfComponent component) {
        final Optional<FontProperties> font = getWidgetModel().getProperties().getFont();
        if (font.isEmpty()) {
            return;
        }

        final FontCaption fontCaption = font.get().getFontCaption();

        final String fontName = fontCaption.getFontName().orElse("Arial");
        final Font baseFont = fontHandler.getFontFromName(fontName);

        final Map<TextAttribute, Float> fontAttrs = new HashMap<>();
        final int fontSize = fontCaption.getFontSize().map(Integer::parseInt).orElse(12);
        fontAttrs.put(TextAttribute.SIZE, (float) fontSize);

        final int fontStyle = fontCaption.getFontStyle().map(Integer::parseInt).orElse(IWidget.STYLE_PLAIN);
        switch (fontStyle) {
            case IWidget.STYLE_PLAIN:
                fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
                break;
            case IWidget.STYLE_BOLD:
                fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
                break;
            case IWidget.STYLE_ITALIC:
                fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_REGULAR);
                fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
                break;
            case IWidget.STYLE_BOLDITALIC:
                fontAttrs.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
                fontAttrs.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
                break;
            default:
                break;
        }

        final Font fontToUse = baseFont.deriveFont(fontAttrs);

        component.setFont(fontToUse);

        final int underline = fontCaption.getUnderline().map(Integer::parseInt).orElse(IWidget.UNDERLINE_NONE);
        component.setUnderlineType(underline);

        final int strikeThroughOnOff = fontCaption.getStrikeThrough().map(Integer::parseInt)
                .orElse(IWidget.STRIKETHROUGH_OFF);
        component.setStrikethrough(strikeThroughOnOff == IWidget.STRIKETHROUGH_ON);

        final Color color = fontCaption.getColor().map(c -> new Color(Integer.parseInt(c))).orElse(Color.BLACK);
        component.setForeground(color);
    }

    void setSizeAndPosition() {
        final SizeAndPosition sizeAndPosition = getWidgetModel().getProperties().getLayout().getSizeAndPosition();

        final int x = sizeAndPosition.getX().map(Integer::parseInt).orElse(0);
        final int width = sizeAndPosition.getWidth().map(Integer::parseInt).orElse(25);
        final int y = sizeAndPosition.getY().map(Integer::parseInt).orElse(0);
        final int height = sizeAndPosition.getHeight().map(Integer::parseInt).orElse(25);

        setPosition(x, y);
        setSize(width, height);
    }

    protected void setParagraphProperties(final IPdfComponent component) {
        final Optional<ParagraphProperties> paragraphProperties = getWidgetModel().getProperties().getParagraph();
        final Optional<String> horizontalAlignment = paragraphProperties.flatMap(paragraph -> paragraph.getParagraphCaption().flatMap(ParagraphCaption::getHorizontalAlignment));

        if (component instanceof PdfCaption) {
            final String componentText = component.getText();
            final String escapedComponentText = StringEscapeUtils.escapeXml11(componentText);
            final String htmlText = "<html><p align=" + horizontalAlignment + ">" + escapedComponentText;
            component.setText(htmlText);
        }

        horizontalAlignment.ifPresent(alignment -> setHorizontalAlignmentToComponent(alignment, component));

        final Optional<String> verticalAlignment = paragraphProperties.flatMap(paragraph -> paragraph.getParagraphCaption().flatMap(ParagraphCaption::getVerticalAlignment));
        verticalAlignment.ifPresent(alignment -> setVerticalAlignmentToComponent(alignment, component));

        setSize(getWidth(), getHeight());
    }

    private void setHorizontalAlignmentToComponent(
            final String alignment,
            final IPdfComponent component) {
        if ("justify".equals(alignment) || "left".equals(alignment)) {
            component.setHorizontalAlignment(SwingConstants.LEFT);
        } else if ("right".equals(alignment)) {
            component.setHorizontalAlignment(SwingConstants.RIGHT);
        } else if ("center".equals(alignment)) {
            component.setHorizontalAlignment(SwingConstants.CENTER);
        } else {
            logger.warn("Unexpected horizontal alignment [{}]", alignment);
        }
    }

    private void setVerticalAlignmentToComponent(
            final String alignment,
            final IPdfComponent component) {
        if ("center".equals(alignment)) {
            component.setVerticalAlignment(SwingConstants.CENTER);
        } else if ("top".equals(alignment)) {
            component.setVerticalAlignment(SwingConstants.TOP);
        } else if ("bottom".equals(alignment)) {
            component.setVerticalAlignment(SwingConstants.BOTTOM);
        } else {
            logger.warn("Unexpected vertical alignment [{}]", alignment);
        }
    }

    @Override
    public void setAllProperties() {
        setParagraphProperties(IWidget.COMPONENT_BOTH);
        setLayoutProperties();
        setFontProperties(IWidget.COMPONENT_BOTH);
        setObjectProperties();
        setCaptionProperties();
    }

    void addJavaScript() {
        final JavaScriptContent javaScript = new JavaScriptContent();

        javaScript.setMouseEnter("");
        javaScript.setMouseExit("");
        javaScript.setChange("");
        javaScript.setMouseUp("");
        javaScript.setMouseDown("");

        if (getType() == IWidget.TEXT_FIELD) {
            javaScript.setKeystroke("");
        }

        getWidgetModel().setJavaScript(javaScript);
    }

    @Override
    public void setObjectProperties() {
    }

    void setBindingProperties() {
        final BindingProperties bindingProperties = getWidgetModel().getProperties().getObject().getBinding();

        widgetName = bindingProperties.getName().orElse("");
        arrayNumber = bindingProperties.getArrayNumber().map(Integer::parseInt).orElse(0);

        setSize(getWidth(), getHeight());
    }

    @Override
    public void setCaptionProperties() {
        if (isComponentSplit
                && ((SplitComponent) baseComponent).getCaptionPosition() != SplitComponent.CAPTION_NONE) {
            final CaptionProperties captionProperties = getWidgetModel().getProperties().getCaptionProperties();

            final String captionText = captionProperties.getTextValue().orElse("");
            getCaptionComponent().setText(captionText);

            captionProperties.getDividerLocation()
                    .filter(location -> !location.isEmpty())
                    .map(Integer::parseInt)
                    .ifPresent(((SplitComponent) baseComponent)::setDividerLocation);

            setSize(getWidth(), getHeight());
        }
    }

    @Override
    public void setBorderAndBackgroundProperties() {
        final JComponent valueComponent = getValueComponent();

        getWidgetModel().getProperties().getBorder()
                .ifPresent(borderProperties -> setBordersOnValueComponent(valueComponent,
                        borderProperties.getBorders()));

        setBackgroundOnValueComponent(valueComponent);

        setSize(getWidth(), getHeight());
    }

    private void setBordersOnValueComponent(
            final JComponent valueComponent,
            final Borders borders) {
        final String borderStyle = borders.getBorderStyle().orElse("None");
        if (borderStyle.equals("None")) {
            valueComponent.setBorder(null);
            return;
        }

        final Map<String, String> borderPropertiesMap = buildBorderPropertiesMap(borderStyle, borders.getBorderWidth());
        final Color color = borders.getBorderColor()
                .map(c -> new Color(Integer.parseInt(c)))
                .orElse(Color.BLACK);
        final Border border = JPedalBorderFactory.createBorderStyle(borderPropertiesMap, color, color);

        valueComponent.setBorder(border);
    }

    private Map<String, String> buildBorderPropertiesMap(
            final String borderStyle,
            final Optional<String> borderWidth) {
        final Map<String, String> borderPropertiesMap = new HashMap<>();
        if (borderStyle.equals("Beveled")) {
            borderPropertiesMap.put("S", "/B");
        }
        if (borderStyle.equals("Solid")) {
            borderPropertiesMap.put("S", "/S");
        }
        if (borderStyle.equals("Dashed")) {
            borderPropertiesMap.put("S", "/D");
        }
        borderWidth
                .map(Integer::valueOf)
                .filter(width -> width > 0)
                .ifPresent(width -> borderPropertiesMap.put("W", String.valueOf(width)));

        return borderPropertiesMap;
    }

    private void setBackgroundOnValueComponent(final JComponent valueComponent) {
        final Color backgroundColor = getWidgetModel().getProperties().getBorder()
                .map(borderProperties -> borderProperties.getBackgroundFill().getFillColor()
                        .map(c -> new Color(Integer.parseInt(c)))
                        .orElse(Color.WHITE))
                .orElse(Color.WHITE);

        valueComponent.setBackground(backgroundColor);
    }

    public void setComponent(final JComponent component) {
        this.component = component;
    }

    public void setComponentSplit(final boolean componentSplit) {
        isComponentSplit = componentSplit;
    }

    public void setAllowEditCaptionAndValue(final boolean allowEditCaptionAndValue) {
        this.allowEditCaptionAndValue = allowEditCaptionAndValue;
    }

    public void setAllowEditOfCaptionOnClick(final boolean allowEditOfCaptionOnClick) {
        this.allowEditOfCaptionOnClick = allowEditOfCaptionOnClick;
    }

    public void setWidgetName(final String widgetName) {
        this.widgetName = widgetName;
    }

    public void setArrayNumber(final int arrayNumber) {
        this.arrayNumber = arrayNumber;
    }

    public JComponent getBaseComponent() {
        return baseComponent;
    }

    FontHandler getFontHandler() {
        return fontHandler;
    }
}