Skip to content

Commit 0c65942

Browse files
committed
Fix radio button field regeneration
DEVSIX-2408
1 parent def5fdf commit 0c65942

File tree

6 files changed

+136
-33
lines changed

6 files changed

+136
-33
lines changed

forms/src/main/java/com/itextpdf/forms/fields/PdfFormField.java

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2118,20 +2118,34 @@ public boolean regenerateField() {
21182118
throw new PdfException(e);
21192119
}
21202120
} else if ((ff & PdfButtonFormField.FF_RADIO) != 0) {
2121-
PdfArray kids = getKids();
2122-
if (null != kids) {
2123-
for (int i = 0; i < kids.size(); i++) {
2124-
PdfObject kid = kids.get(i);
2121+
if (isRadioButton()) {
2122+
// TODO DEVSIX-2536
2123+
// Actually only radio group has FF_RADIO type.
2124+
// This means that only radio group shall have regeneration functionality.
2125+
Rectangle rect = getRect(getPdfObject());
2126+
value = getRadioButtonValue(value);
2127+
if (rect != null && !"".equals(value)) {
2128+
if (pdfAConformanceLevel != null && "1".equals(pdfAConformanceLevel.getPart())) {
2129+
drawPdfA1RadioAppearance(rect.getWidth(), rect.getHeight(), value);
2130+
} else {
2131+
drawRadioAppearance(rect.getWidth(), rect.getHeight(), value);
2132+
}
2133+
}
2134+
} else if (getKids() != null) {
2135+
for (PdfObject kid : getKids()) {
21252136
PdfFormField field = new PdfFormField((PdfDictionary) kid);
21262137
PdfWidgetAnnotation widget = field.getWidgets().get(0);
21272138
PdfDictionary apStream = field.getPdfObject().getAsDictionary(PdfName.AP);
2128-
String state;
2139+
if (apStream == null) { //widget annotation was not merged
2140+
apStream = widget.getPdfObject().getAsDictionary(PdfName.AP);
2141+
}
2142+
PdfName state;
21292143
if (null != apStream && null != getValueFromAppearance(apStream.get(PdfName.N), new PdfName(value))) {
2130-
state = value;
2144+
state = new PdfName(value);
21312145
} else {
2132-
state = "Off";
2146+
state = new PdfName("Off");
21332147
}
2134-
widget.setAppearanceState(new PdfName(state));
2148+
widget.setAppearanceState(state);
21352149
}
21362150
}
21372151
} else {
@@ -2168,6 +2182,40 @@ public boolean regenerateField() {
21682182
return true;
21692183
}
21702184

2185+
// TODO DEVSIX-2536
2186+
// Actually this entire method is a mess,
2187+
// because only radio group has FF_RADIO type and there is no RadioButton at all.
2188+
// So the goal of that method is just to save backward compatibility until refactoring.
2189+
private boolean isRadioButton() {
2190+
if (isWidgetAnnotation(getPdfObject())) {
2191+
return true;
2192+
} else if (getPdfObject().getAsName(PdfName.V) != null) {
2193+
return false;
2194+
} else if (getKids() != null) {
2195+
return isWidgetAnnotation(getKids().getAsDictionary(0));
2196+
} else {
2197+
return false;
2198+
}
2199+
}
2200+
2201+
private static boolean isWidgetAnnotation(PdfDictionary pdfObject) {
2202+
return pdfObject != null && PdfName.Widget.equals(pdfObject.getAsName(PdfName.Subtype));
2203+
}
2204+
2205+
private String getRadioButtonValue(String value) {
2206+
assert value != null; //Otherwise something wrong with getValueAsString().
2207+
if ("".equals(value)) {
2208+
value = "Yes"; //let it as default value
2209+
for (String state: getAppearanceStates()) {
2210+
if (!"Off".equals(state)) {
2211+
value = state;
2212+
break;
2213+
}
2214+
}
2215+
}
2216+
return value;
2217+
}
2218+
21712219
/**
21722220
* According to spec (ISO-32000-1, 12.7.3.3) zero font size should interpretaded as auto size.
21732221
*/
@@ -2599,7 +2647,7 @@ protected Rectangle getRect(PdfDictionary field) {
25992647
rect = ((PdfDictionary) kids.get(0)).getAsArray(PdfName.Rect);
26002648
}
26012649

2602-
return rect.toRectangle();
2650+
return rect != null ? rect.toRectangle() : null;
26032651
}
26042652

26052653
protected static PdfArray processOptions(String[][] options) {
@@ -2989,31 +3037,36 @@ protected void drawRadioBorder(PdfCanvas canvas, PdfFormXObject xObject, float w
29893037
* @param value the value of the button
29903038
*/
29913039
protected void drawRadioAppearance(float width, float height, String value) {
3040+
Rectangle rect = new Rectangle(0, 0, width, height);
3041+
PdfWidgetAnnotation widget = getWidgets().get(0);
3042+
widget.setNormalAppearance(new PdfDictionary());
3043+
3044+
//On state
29923045
PdfStream streamOn = (PdfStream) new PdfStream().makeIndirect(getDocument());
29933046
PdfCanvas canvasOn = new PdfCanvas(streamOn, new PdfResources(), getDocument());
2994-
Rectangle rect = new Rectangle(0, 0, width, height);
29953047
PdfFormXObject xObjectOn = new PdfFormXObject(rect);
2996-
PdfFormXObject xObjectOff = new PdfFormXObject(rect);
29973048

29983049
drawRadioBorder(canvasOn, xObjectOn, width, height);
29993050
drawRadioField(canvasOn, width, height, true);
30003051

3052+
xObjectOn.getPdfObject().getOutputStream().writeBytes(streamOn.getBytes());
3053+
widget.getNormalAppearanceObject().put(new PdfName(value), xObjectOn.getPdfObject());
3054+
3055+
//Off state
30013056
PdfStream streamOff = (PdfStream) new PdfStream().makeIndirect(getDocument());
30023057
PdfCanvas canvasOff = new PdfCanvas(streamOff, new PdfResources(), getDocument());
3003-
drawRadioBorder(canvasOff, xObjectOff, width, height);
3004-
if (pdfAConformanceLevel != null && (pdfAConformanceLevel.getPart().equals("2") || pdfAConformanceLevel.getPart().equals("3"))) {
3005-
xObjectOn.getResources();
3006-
xObjectOff.getResources();
3007-
}
3008-
3009-
PdfWidgetAnnotation widget = getWidgets().get(0);
3058+
PdfFormXObject xObjectOff = new PdfFormXObject(rect);
30103059

3011-
xObjectOn.getPdfObject().getOutputStream().writeBytes(streamOn.getBytes());
3012-
widget.setNormalAppearance(new PdfDictionary());
3013-
widget.getNormalAppearanceObject().put(new PdfName(value), xObjectOn.getPdfObject());
3060+
drawRadioBorder(canvasOff, xObjectOff, width, height);
30143061

30153062
xObjectOff.getPdfObject().getOutputStream().writeBytes(streamOff.getBytes());
30163063
widget.getNormalAppearanceObject().put(new PdfName("Off"), xObjectOff.getPdfObject());
3064+
3065+
if (pdfAConformanceLevel != null
3066+
&& (pdfAConformanceLevel.getPart().equals("2") || pdfAConformanceLevel.getPart().equals("3"))) {
3067+
xObjectOn.getResources();
3068+
xObjectOff.getResources();
3069+
}
30173070
}
30183071

30193072
/**
@@ -3031,12 +3084,15 @@ protected void drawPdfA1RadioAppearance(float width, float height, String value)
30313084

30323085

30333086
drawBorder(canvas, xObject, width, height);
3034-
drawRadioField(canvas, rect.getWidth(), rect.getHeight(), !value.equals("Off"));
3087+
drawRadioField(canvas, rect.getWidth(), rect.getHeight(), !"Off".equals(value));
3088+
3089+
PdfDictionary normalAppearance = new PdfDictionary();
3090+
normalAppearance.put(new PdfName(value), xObject.getPdfObject());
30353091

30363092
PdfWidgetAnnotation widget = getWidgets().get(0);
30373093

30383094
xObject.getPdfObject().getOutputStream().writeBytes(stream.getBytes());
3039-
widget.setNormalAppearance(xObject.getPdfObject());
3095+
widget.setNormalAppearance(normalAppearance);
30403096
}
30413097

30423098
/**

forms/src/test/java/com/itextpdf/forms/PdfFormFieldTest.java

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,9 @@ public void buttonFieldTest01() throws IOException, InterruptedException {
245245
}
246246
}
247247

248-
@Test //TODO DEVSIX-2408
249-
public void radiobuttonFieldTest01() throws IOException, InterruptedException {
250-
String file = "radiobuttonFieldTest01.pdf";
248+
@Test
249+
public void defaultRadiobuttonFieldTest() throws IOException, InterruptedException {
250+
String file = "defaultRadiobuttonFieldTest.pdf";
251251

252252
String filename = destinationFolder + file;
253253
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(filename));
@@ -264,18 +264,65 @@ public void radiobuttonFieldTest01() throws IOException, InterruptedException {
264264

265265
form.addField(group);
266266

267-
rect1 = new Rectangle(36, 600, 20, 20);
268-
rect2 = new Rectangle(36, 580, 20, 20);
269-
// TODO DEVSIX-2408
270-
// Both radio groups have the same appearances, despite additional properties.
271-
// Default value is lost for the second group.
267+
pdfDoc.close();
268+
269+
Assert.assertNull(new CompareTool().compareByContent(filename, sourceFolder + "cmp_" + file, destinationFolder, "diff_"));
270+
}
271+
272+
@Test
273+
public void customizedRadiobuttonFieldTest() throws IOException, InterruptedException {
274+
String file = "customizedRadiobuttonFieldTest.pdf";
275+
276+
String filename = destinationFolder + file;
277+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(filename));
278+
279+
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
280+
281+
Rectangle rect1 = new Rectangle(36, 700, 20, 20);
282+
Rectangle rect2 = new Rectangle(36, 680, 20, 20);
283+
284+
PdfButtonFormField group2 = PdfFormField.createRadioGroup(pdfDoc, "TestGroup2", "1");
285+
286+
PdfFormField.createRadioButton(pdfDoc, rect1, group2, "1")
287+
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY)
288+
.setVisibility(PdfFormField.VISIBLE);
289+
290+
291+
PdfFormField.createRadioButton(pdfDoc, rect2, group2, "2")
292+
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY)
293+
.setVisibility(PdfFormField.VISIBLE);
294+
295+
form.addField(group2);
296+
297+
pdfDoc.close();
298+
299+
Assert.assertNull(new CompareTool().compareByContent(filename, sourceFolder + "cmp_" + file, destinationFolder, "diff_"));
300+
}
301+
302+
@Test
303+
public void customizedRadiobuttonWithGroupRegeneratingFieldTest() throws IOException, InterruptedException {
304+
String file = "customizedRadiobuttonWithGroupRegeneratingFieldTest.pdf";
305+
306+
String filename = destinationFolder + file;
307+
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(filename));
308+
309+
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
310+
311+
Rectangle rect1 = new Rectangle(36, 700, 20, 20);
312+
Rectangle rect2 = new Rectangle(36, 680, 20, 20);
313+
272314
PdfButtonFormField group2 = PdfFormField.createRadioGroup(pdfDoc, "TestGroup2", "1");
273315

274316
PdfFormField.createRadioButton(pdfDoc, rect1, group2, "1")
275-
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY);
317+
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY)
318+
.setVisibility(PdfFormField.VISIBLE);
319+
320+
276321
PdfFormField.createRadioButton(pdfDoc, rect2, group2, "2")
277-
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY);
322+
.setBorderWidth(2).setBorderColor(ColorConstants.RED).setBackgroundColor(ColorConstants.LIGHT_GRAY)
323+
.setVisibility(PdfFormField.VISIBLE);
278324

325+
group2.regenerateField();
279326
form.addField(group2);
280327

281328
pdfDoc.close();

0 commit comments

Comments
 (0)