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

com.openhtmltopdf.pdfboxout.PdfBoxForm Maven / Gradle / Ivy

Go to download

Openhtmltopdf is a CSS 2.1 renderer written in Java. This artifact supports PDF output with Apache PDF-BOX 2.

There is a newer version: 1.0.10
Show newest version
package com.openhtmltopdf.pdfboxout;

import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.regex.Pattern;

import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.common.filespecification.PDFileSpecification;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionResetForm;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionSubmitForm;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceCharacteristicsDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDCheckBox;
import org.apache.pdfbox.pdmodel.interactive.form.PDComboBox;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDListBox;
import org.apache.pdfbox.pdmodel.interactive.form.PDNonTerminalField;
import org.apache.pdfbox.pdmodel.interactive.form.PDPushButton;
import org.apache.pdfbox.pdmodel.interactive.form.PDRadioButton;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import org.w3c.dom.Element;

import com.openhtmltopdf.css.constants.CSSName;
import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.css.parser.FSCMYKColor;
import com.openhtmltopdf.css.parser.FSColor;
import com.openhtmltopdf.css.parser.FSRGBColor;
import com.openhtmltopdf.render.Box;
import com.openhtmltopdf.render.RenderingContext;
import com.openhtmltopdf.util.ArrayUtil;
import com.openhtmltopdf.util.OpenUtil;
import com.openhtmltopdf.util.XRLog;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;


public class PdfBoxForm {
    // The global(per document) form state container
    private final PdfBoxPerDocumentFormState docFormsStateContainer;
    
    // The output device
    private final PdfBoxOutputDevice od;
    
    // The form element itself.
    private final Element element;
    
    // All controls, with a font name if needed.
    private final List controls = new ArrayList();

    // The submits have to be done after all other controls are processed.
    private final List submits = new ArrayList(2);

    // We've got to find all the radio button controls that belong to a group (common name).
    private final Map> radioGroups = new LinkedHashMap>();
    
    // Contains a tree of fields in the form:
    // person
    // person.details
    // person.details.name
    // person.details.phone
    // etc.
    private final Map allFieldMap = new HashMap();
    
    // A link in the tree of fields. We have this so that each field can look up
    // its parent field.
    private static class Field {
        private PDField field;
        private String partialName;   // eg. person.details.phone
        private String qualifiedName; // eg. phone.
        // If its non-terminal we'll have to create it explicitly and
        // set its kids appropriately.
        private boolean isTerminal;
    }
    
    public static class Control {
        public final Box box;
        private final PDPage page;
        private final AffineTransform transform;
        private final RenderingContext c;
        private final float pageHeight;
        
        public Control(Box box, PDPage page, AffineTransform transform, RenderingContext c, float pageHeight) {
            this.box = box;
            this.page = page;
            this.transform = transform;
            this.c = c;
            this.pageHeight = pageHeight;
        }
    }
    
    private static class ControlFontPair {
        private final String fontName;
        private final Control control;
        
        private ControlFontPair(Control control, String fontName) {
            this.control = control;
            this.fontName = fontName;
        }
    }
    
    private PdfBoxForm(Element element, PdfBoxPerDocumentFormState forms, PdfBoxOutputDevice od) {
        this.element = element;
        this.od = od;
        this.docFormsStateContainer = forms;
    }
    
    public static PdfBoxForm createForm(Element e, PdfBoxPerDocumentFormState forms, PdfBoxOutputDevice od) {
        return new PdfBoxForm(e, forms, od);
    }

    public void addControl(Control ctrl, String fontName) {
        controls.add(new ControlFontPair(ctrl, fontName));
    }
    
    /** 
     * This method will create a tree of names, both non-terminal
     * and terminal.
     */
    private void processControlNames() {
        for (ControlFontPair control : controls) {
            if (!control.control.box.getElement().hasAttribute("name")) {
                continue;
            }
            
            String name = control.control.box.getElement().getAttribute("name");
            
            if (!name.contains(".")) {
                // It's a root field!
                Field f = new Field();
                f.partialName = name;
                f.qualifiedName = name;
                f.isTerminal = true;
                allFieldMap.put(name, f);
            } else {
                String[] partials = name.split(Pattern.quote("."));
                
                for (int i = 1; i <= partials.length; i++) {
                    // Given a field name such as person.details.name
                    // we check that 'person' is created first, then 'person.details' and
                    // finally 'person.details.name'.
                    
                    String[] parent = new String[i];
                    System.arraycopy(partials, 0, parent, 0, i);
                    String parentQualifiedName = ArrayUtil.join(parent, ".");

                    Field f = allFieldMap.get(parentQualifiedName);
                    if (f == null) {
                        Field fCreated = new Field();
                        fCreated.qualifiedName = parentQualifiedName;
                        fCreated.partialName = parent[i - 1]; 
                        fCreated.isTerminal = (i == partials.length);
                        allFieldMap.put(parentQualifiedName, fCreated);
                    }
                }
            }
        }
    }
    
    /**
     * This method will create the non terminal fields.
     * It is called recursively to create all non-terminal field descendants.
     * It should be called after all the PDField objects are created.
     */
    private void createNonTerminalFields(Field f, PDAcroForm form) {
            if (!f.isTerminal) {
                COSArray kids = new COSArray();

                for (Field f2 : allFieldMap.values()) {
                    if (f2.qualifiedName.indexOf(f.qualifiedName) == 0 &&                          // Its a descendant or identical.
                        f2.qualifiedName.length() > f.qualifiedName.length() + 1 &&                // Its not identical.
                        !f2.qualifiedName.substring(f.qualifiedName.length() + 1).contains(".")) { // Its a direct child.

                        kids.add(f2.field.getCOSObject());
                        f2.field.getCOSObject().setItem(COSName.PARENT, f.field.getCOSObject());
                        createNonTerminalFields(f2, form);
                    }
                }
                

                f.field.getCOSObject().setItem(COSName.KIDS, kids); 
            }
      }

      /**
       * Calls createNonTerminalFields on all root non-terminal fields.
       * Otherwise, root fields are added to the acro form field collection.
       */
      private void createNonTerminalFields(PDAcroForm form) {
          for (Field f : allFieldMap.values()) {
              if (!f.isTerminal) {
                  PDNonTerminalField nonTerminal = new PDNonTerminalField(form);
                  nonTerminal.setPartialName(f.partialName);
                  f.field = nonTerminal;
              }
          }
          
          for (Field f : allFieldMap.values()) {
              if (!f.qualifiedName.contains(".")) {
                  createNonTerminalFields(f, form);
                  form.getFields().add(f.field);
              }
          }
      }
    
    /**
     * Get a PDF graphics operator for a specific color.
     */
    private static String getColorOperator(FSColor color) {
        String colorOperator = "";
        
        if (color instanceof FSRGBColor) {
            FSRGBColor rgb = (FSRGBColor) color;
            float r = (float) rgb.getRed() / 255;
            float g = (float) rgb.getGreen() / 255;
            float b = (float) rgb.getBlue() / 255;
            
            colorOperator =
                    String.format(Locale.US, "%.4f", r) + ' ' + 
                    String.format(Locale.US, "%.4f", g) + ' ' + 
                    String.format(Locale.US, "%.4f", b) + ' ' +
                    "rg";
        } else if (color instanceof FSCMYKColor) {
            FSCMYKColor cmyk = (FSCMYKColor) color;
            float c = cmyk.getCyan();
            float m = cmyk.getMagenta();
            float y = cmyk.getYellow();
            float k = cmyk.getBlack();
            
            colorOperator = 
                    String.format(Locale.US, "%.4f", c) + ' ' +
                    String.format(Locale.US, "%.4f", m) + ' ' +
                    String.format(Locale.US, "%.4f", y) + ' ' +
                    String.format(Locale.US, "%.4f", k) + ' ' +
                    "k";
        }
        
        return colorOperator;
    }
    
    private String getTextareaText(Element e) {
        return DOMUtil.getText(e);
    }
    
    private String populateOptions(Element e, List labels, List values, List selectedIndices) {
        List opts = DOMUtil.getChildren(e, "option");
        if (opts == null) {
            XRLog.general(Level.WARNING, "A <"+e.getTagName() + "> element does not have 




© 2015 - 2024 Weber Informatics LLC | Privacy Policy