Skip to content

Commit

Permalink
Fixes #252 - Major code refactoring for form controls.
Browse files Browse the repository at this point in the history
#252 was fixed by remembering to clone the page transform object when passing an element and associated information to be processed later.
  • Loading branch information
danfickle committed Jul 20, 2018
1 parent 16201df commit 4227ad2
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,32 @@ public static byte[] cloneOrEmpty(byte[] source){
public static int[] cloneOrEmpty(int[] source) {
return source == null ? Constants.EMPTY_INT_ARR : (int[]) source.clone();
}

/**
* Tests if left is equal to one of the rights. Also returns true if both are null.
*/
public static <T> boolean isOneOf(T left, T... rights) {
for (T candidate : rights) {
if (candidate == null && left == null) {
return true;
} else if (left != null && left.equals(candidate)) {
return true;
}
}
return false;
}

/**
* Joins a string array, with the given separator.
*/
public static String join(String[] partials, String separator) {
StringBuilder sb = new StringBuilder();
for (int i = 0;i < partials.length; i++) {
sb.append(partials[i]);
if (i < partials.length - 1) {
sb.append(separator);
}
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ public static boolean isCodePointPrintable(int codePoint) {
category == Character.PRIVATE_USE ||
category == Character.SURROGATE);
}

public static Integer parseIntegerOrNull(String possibleInteger) {
try {
return Integer.parseInt(possibleInteger);
} catch (NumberFormatException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.openhtmltopdf.util.XRLog;

public class DOMUtil {
public static Element getChild(Element parent, String name) {
NodeList children = parent.getChildNodes();
Expand Down Expand Up @@ -56,6 +59,21 @@ public static List<Element> getChildren(Element parent, String name) {
return result.size() == 0 ? null : result;
}

/**
* Helper function to find an enclosing element with given node name. Returns null on failure.
*/
public static Element findClosestEnclosingElementWithNodeName(Node e, String nodeName) {
Node parent;
while ((parent = e.getParentNode()) != null) {
if (parent.getNodeType() == Node.ELEMENT_NODE &&
parent.getNodeName().equals(nodeName)) {
return (Element) parent;
}
e = parent;
}
return null;
}

/**
* Loads all of the text content in all offspring of an element.
* Ignores all attributes, comments and processing instructions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@
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;


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;

Expand Down Expand Up @@ -110,12 +118,14 @@ private ControlFontPair(Control control, String fontName) {
}
}

private PdfBoxForm(Element element) {
private PdfBoxForm(Element element, PdfBoxPerDocumentFormState forms, PdfBoxOutputDevice od) {
this.element = element;
this.od = od;
this.docFormsStateContainer = forms;
}

public static PdfBoxForm createForm(Element e) {
return new PdfBoxForm(e);
public static PdfBoxForm createForm(Element e, PdfBoxPerDocumentFormState forms, PdfBoxOutputDevice od) {
return new PdfBoxForm(e, forms, od);
}

public void addControl(Control ctrl, String fontName) {
Expand Down Expand Up @@ -151,7 +161,7 @@ private void processControlNames() {

String[] parent = new String[i];
System.arraycopy(partials, 0, parent, 0, i);
String parentQualifiedName = join(parent, ".");
String parentQualifiedName = ArrayUtil.join(parent, ".");

Field f = allFieldMap.get(parentQualifiedName);
if (f == null) {
Expand Down Expand Up @@ -212,28 +222,6 @@ private void createNonTerminalFields(PDAcroForm form) {
}
}

/**
* Joins a string array, with the given separator.
*/
private String join(String[] partials, String separator) {
StringBuilder sb = new StringBuilder();
for (int i = 0;i < partials.length; i++) {
sb.append(partials[i]);
if (i < partials.length - 1) {
sb.append(separator);
}
}
return sb.toString();
}

private static Integer getNumber(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return null;
}
}

/**
* Get a PDF graphics operator for a specific color.
*/
Expand Down Expand Up @@ -306,7 +294,7 @@ private String populateOptions(Element e, List<String> labels, List<String> valu
return selected;
}

private void processMultiSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root, PdfBoxOutputDevice od) throws IOException {
private void processMultiSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
PDListBox field = new PDListBox(acro);

Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
Expand Down Expand Up @@ -356,7 +344,7 @@ private void processMultiSelectControl(ControlFontPair pair, Control ctrl, PDAcr
/**
* Processes select controls and the custom openhtmltopdf-combo control.
*/
private void processSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root, PdfBoxOutputDevice od) throws IOException {
private void processSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
PDComboBox field = new PDComboBox(acro);

Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
Expand Down Expand Up @@ -407,7 +395,7 @@ private void processSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm
ctrl.page.getAnnotations().add(widget);
}

private void processHiddenControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root, PdfBoxOutputDevice od) throws IOException {
private void processHiddenControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
PDTextField field = new PDTextField(acro);

Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
Expand All @@ -428,7 +416,7 @@ private void processHiddenControl(ControlFontPair pair, Control ctrl, PDAcroForm
ctrl.page.getAnnotations().add(widgy);
}

private void processTextControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root, PdfBoxOutputDevice od) throws IOException {
private void processTextControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
PDTextField field = new PDTextField(acro);

Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
Expand All @@ -449,8 +437,8 @@ private void processTextControl(ControlFontPair pair, Control ctrl, PDAcroForm a
field.setDefaultValue(value); // The reset value.
field.setValue(value); // The original value.

if (getNumber(ctrl.box.getElement().getAttribute("max-length")) != null) {
field.setMaxLen(getNumber(ctrl.box.getElement().getAttribute("max-length")));
if (OpenUtil.parseIntegerOrNull(ctrl.box.getElement().getAttribute("max-length")) != null) {
field.setMaxLen(OpenUtil.parseIntegerOrNull(ctrl.box.getElement().getAttribute("max-length")));
}

if (ctrl.box.getElement().hasAttribute("required")) {
Expand Down Expand Up @@ -585,7 +573,7 @@ private COSString getCOSStringUTF16Encoded(String value) throws UnsupportedEncod
return valueEncoded;
}

private void processCheckboxControl(ControlFontPair pair, PDAcroForm acro, int i, Control ctrl, Box root, PdfBoxOutputDevice od) throws IOException {
private void processCheckboxControl(ControlFontPair pair, PDAcroForm acro, int i, Control ctrl, Box root) throws IOException {
PDCheckBox field = new PDCheckBox(acro);

Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
Expand Down Expand Up @@ -640,16 +628,16 @@ private void processCheckboxControl(ControlFontPair pair, PDAcroForm acro, int i
widget.setAppearanceCharacteristics(appearanceCharacteristics);

COSDictionary dict = new COSDictionary();
dict.setItem(zero, od.checkboxAppearances.get(style));
dict.setItem(COSName.Off, od.checkboxOffAppearance);
dict.setItem(zero, this.docFormsStateContainer.getCheckboxStyle(style));
dict.setItem(COSName.Off, this.docFormsStateContainer.getCheckboxOffStream());
PDAppearanceDictionary appearanceDict = new PDAppearanceDictionary();
appearanceDict.getCOSObject().setItem(COSName.N, dict);
widget.setAppearance(appearanceDict);

ctrl.page.getAnnotations().add(widget);
}

private void processRadioButtonGroup(List<Control> group, PDAcroForm acro, int i, Box root, PdfBoxOutputDevice od) throws IOException {
private void processRadioButtonGroup(List<Control> group, PDAcroForm acro, int i, Box root) throws IOException {
String groupName = group.get(0).box.getElement().getAttribute("name");
PDRadioButton field = new PDRadioButton(acro);

Expand Down Expand Up @@ -679,8 +667,8 @@ private void processRadioButtonGroup(List<Control> group, PDAcroForm acro, int i
widget.setPrinted(true);

COSDictionary dict = new COSDictionary();
dict.setItem(COSName.getPDFName("" + radioCnt), od.radioBoxOnAppearance);
dict.setItem(COSName.Off, od.radioBoxOffAppearance);
dict.setItem(COSName.getPDFName("" + radioCnt), docFormsStateContainer.getRadioOnStream());
dict.setItem(COSName.Off, docFormsStateContainer.getRadioOffStream());
PDAppearanceDictionary appearanceDict = new PDAppearanceDictionary();
appearanceDict.getCOSObject().setItem(COSName.N, dict);

Expand All @@ -707,7 +695,7 @@ private void processRadioButtonGroup(List<Control> group, PDAcroForm acro, int i
}
}

private void processSubmitControl(PDAcroForm acro, int i, Control ctrl, Box root, PdfBoxOutputDevice od) throws IOException {
private void processSubmitControl(PDAcroForm acro, int i, Control ctrl, Box root) throws IOException {
final int FLAG_USE_GET = 1 << 3;
final int FLAG_USE_HTML_SUBMIT = 1 << 2;

Expand Down Expand Up @@ -780,7 +768,7 @@ private void processSubmitControl(PDAcroForm acro, int i, Control ctrl, Box root
ctrl.page.getAnnotations().add(widget);
}

public int process(PDAcroForm acro, int startId, Box root, PdfBoxOutputDevice od) throws IOException {
public int process(PDAcroForm acro, int startId, Box root) throws IOException {
processControlNames();

int i = startId;
Expand All @@ -800,24 +788,24 @@ public int process(PDAcroForm acro, int startId, Box root, PdfBoxOutputDevice od
e.getAttribute("type").equals("file"))) {

// Start with the text controls (text, password, file and textarea).
processTextControl(pair, ctrl, acro, i, root, od);
processTextControl(pair, ctrl, acro, i, root);
} else if ((e.getNodeName().equals("select") &&
!e.hasAttribute("multiple")) ||
(e.getNodeName().equals("openhtmltopdf-combo"))) {

processSelectControl(pair, ctrl, acro, i, root, od);
processSelectControl(pair, ctrl, acro, i, root);
} else if (e.getNodeName().equals("select") &&
e.hasAttribute("multiple")) {

processMultiSelectControl(pair, ctrl, acro, i, root, od);
processMultiSelectControl(pair, ctrl, acro, i, root);
} else if (e.getNodeName().equals("input") &&
e.getAttribute("type").equals("checkbox")) {

processCheckboxControl(pair, acro, i, ctrl, root, od);
processCheckboxControl(pair, acro, i, ctrl, root);
} else if (e.getNodeName().equals("input") &&
e.getAttribute("type").equals("hidden")) {

processHiddenControl(pair, ctrl, acro, i, root, od);
processHiddenControl(pair, ctrl, acro, i, root);
}else if (e.getNodeName().equals("input") &&
e.getAttribute("type").equals("radio")) {
// We have to do radio button groups in one hit so add them to a map of list keyed on name.
Expand All @@ -844,13 +832,13 @@ public int process(PDAcroForm acro, int startId, Box root, PdfBoxOutputDevice od
// Now process each group of radio buttons.
for (List<Control> group : radioGroups.values()) {
i++;
processRadioButtonGroup(group, acro, i, root, od);
processRadioButtonGroup(group, acro, i, root);
}

// We do submit controls last as we need all the fields in this form.
for (Control ctrl : submits) {
i++;
processSubmitControl(acro, i, ctrl, root, od);
processSubmitControl(acro, i, ctrl, root);
}

createNonTerminalFields(acro);
Expand Down
Loading

0 comments on commit 4227ad2

Please sign in to comment.