Skip to content

Commit 8a53d43

Browse files
authored
Merge pull request #413 from syjer/151-npe-form-missing-name-attribute
handle more gracefully an input without name attribute + select without option child fixes #151
2 parents 45c8b51 + 72b50a4 commit 8a53d43

File tree

3 files changed

+69
-30
lines changed

3 files changed

+69
-30
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<html>
2+
<body>
3+
<form>
4+
<input type="text"/>
5+
<input type="radio"/>
6+
<textarea/>
7+
<input type="checkbox"/>
8+
<select/>
9+
<input type="hidden"/>
10+
</form>
11+
</body>
12+
</html>

openhtmltopdf-examples/src/test/java/com/openhtmltopdf/nonvisualregressiontests/NonVisualRegressionTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,21 @@ public void testFormControlAfterOverflowPage() throws IOException {
702702

703703
remove("form-control-after-overflow-page", doc);
704704
}
705+
706+
/**
707+
* Check that an input without name attribute does not launch a NPE.
708+
* Will now log a warning message.
709+
* See issue: https://github.com/danfickle/openhtmltopdf/issues/151
710+
*
711+
* Additionally, check that a select element without options will not launch a NPE too.
712+
*/
713+
@Test
714+
public void testInputWithoutNameAttribute() throws IOException {
715+
PDDocument doc = run("input-without-name-attribute");
716+
PDAcroForm form = doc.getDocumentCatalog().getAcroForm();
717+
assertEquals(0, form.getFields().size());
718+
remove("input-without-name-attribute", doc);
719+
}
705720

706721
// TODO:
707722
// + More form controls.

openhtmltopdf-pdfbox/src/main/java/com/openhtmltopdf/pdfboxout/PdfBoxForm.java

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import com.openhtmltopdf.util.ArrayUtil;
5353
import com.openhtmltopdf.util.OpenUtil;
5454
import com.openhtmltopdf.util.XRLog;
55+
import org.w3c.dom.NamedNodeMap;
56+
import org.w3c.dom.Node;
5557

5658

5759
public class PdfBoxForm {
@@ -263,6 +265,10 @@ private String getTextareaText(Element e) {
263265

264266
private String populateOptions(Element e, List<String> labels, List<String> values, List<Integer> selectedIndices) {
265267
List<Element> opts = DOMUtil.getChildren(e, "option");
268+
if (opts == null) {
269+
XRLog.general(Level.WARNING, "A <"+e.getTagName() + "> element does not have <option> children");
270+
return "";
271+
}
266272
String selected = "";
267273
int i = 0;
268274

@@ -297,10 +303,8 @@ private String populateOptions(Element e, List<String> labels, List<String> valu
297303
private void processMultiSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
298304
PDListBox field = new PDListBox(acro);
299305

300-
Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
301-
fObj.field = field;
302-
303-
field.setPartialName(fObj.partialName);
306+
setPartialNameToField(ctrl, field);
307+
304308
field.setMultiSelect(true);
305309

306310
List<String> labels = new ArrayList<String>();
@@ -346,11 +350,8 @@ private void processMultiSelectControl(ControlFontPair pair, Control ctrl, PDAcr
346350
*/
347351
private void processSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
348352
PDComboBox field = new PDComboBox(acro);
349-
350-
Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
351-
fObj.field = field;
352-
353-
field.setPartialName(fObj.partialName);
353+
354+
setPartialNameToField(ctrl, field);
354355

355356
List<String> labels = new ArrayList<String>();
356357
List<String> values = new ArrayList<String>();
@@ -397,11 +398,8 @@ private void processSelectControl(ControlFontPair pair, Control ctrl, PDAcroForm
397398

398399
private void processHiddenControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
399400
PDTextField field = new PDTextField(acro);
400-
401-
Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
402-
fObj.field = field;
403-
404-
field.setPartialName(fObj.partialName);
401+
402+
setPartialNameToField(ctrl, field);
405403

406404
String value = ctrl.box.getElement().getAttribute("value");
407405

@@ -418,11 +416,8 @@ private void processHiddenControl(ControlFontPair pair, Control ctrl, PDAcroForm
418416

419417
private void processTextControl(ControlFontPair pair, Control ctrl, PDAcroForm acro, int i, Box root) throws IOException {
420418
PDTextField field = new PDTextField(acro);
421-
422-
Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
423-
fObj.field = field;
424-
425-
field.setPartialName(fObj.partialName);
419+
420+
setPartialNameToField(ctrl, field);
426421

427422
FSColor color = ctrl.box.getStyle().getColor();
428423
String colorOperator = getColorOperator(color);
@@ -575,12 +570,9 @@ private COSString getCOSStringUTF16Encoded(String value) {
575570

576571
private void processCheckboxControl(ControlFontPair pair, PDAcroForm acro, int i, Control ctrl, Box root) throws IOException {
577572
PDCheckBox field = new PDCheckBox(acro);
578-
579-
Field fObj = allFieldMap.get(ctrl.box.getElement().getAttribute("name"));
580-
fObj.field = field;
581-
582-
field.setPartialName(fObj.partialName);
583-
573+
574+
setPartialNameToField(ctrl, field);
575+
584576
if (ctrl.box.getElement().hasAttribute("required")) {
585577
field.setRequired(true);
586578
}
@@ -640,11 +632,9 @@ private void processCheckboxControl(ControlFontPair pair, PDAcroForm acro, int i
640632
private void processRadioButtonGroup(List<Control> group, PDAcroForm acro, int i, Box root) throws IOException {
641633
String groupName = group.get(0).box.getElement().getAttribute("name");
642634
PDRadioButton field = new PDRadioButton(acro);
643-
635+
644636
Field fObj = allFieldMap.get(groupName);
645-
fObj.field = field;
646-
647-
field.setPartialName(fObj.partialName);
637+
setPartialNameToField(group.get(0).box.getElement(), fObj, field);
648638

649639
List<String> values = new ArrayList<String>(group.size());
650640
for (Control ctrl : group) {
@@ -767,6 +757,28 @@ private void processSubmitControl(PDAcroForm acro, int i, Control ctrl, Box root
767757
acro.getFields().add(btn);
768758
ctrl.page.getAnnotations().add(widget);
769759
}
760+
761+
private void setPartialNameToField(Control ctrl, PDField field) {
762+
Element elem = ctrl.box.getElement();
763+
Field fObj = allFieldMap.get(elem.getAttribute("name"));
764+
setPartialNameToField(elem, fObj, field);
765+
}
766+
767+
private static void setPartialNameToField(Element element, Field fObj, PDField field) {
768+
if (fObj != null) {
769+
fObj.field = field;
770+
field.setPartialName(fObj.partialName);
771+
} else {
772+
StringBuilder sb = new StringBuilder();
773+
NamedNodeMap attributes = element.getAttributes();
774+
int length = attributes.getLength();
775+
for (int i = 0; i < length; i++) {
776+
Node item = attributes.item(i);
777+
sb.append(' ').append(item.getNodeName()).append("=\"").append(item.getNodeValue()).append('"');
778+
}
779+
XRLog.general(Level.WARNING, "found a <" + element.getTagName() + sb.toString() +"> element without attribute name, the element will not work without this attribute");
780+
}
781+
}
770782

771783
public int process(PDAcroForm acro, int startId, Box root) throws IOException {
772784
processControlNames();
@@ -806,7 +818,7 @@ public int process(PDAcroForm acro, int startId, Box root) throws IOException {
806818
e.getAttribute("type").equals("hidden")) {
807819

808820
processHiddenControl(pair, ctrl, acro, i, root);
809-
}else if (e.getNodeName().equals("input") &&
821+
} else if (e.getNodeName().equals("input") &&
810822
e.getAttribute("type").equals("radio")) {
811823
// We have to do radio button groups in one hit so add them to a map of list keyed on name.
812824
List<Control> radioGroup = radioGroups.get(e.getAttribute("name"));

0 commit comments

Comments
 (0)