Skip to content

Commit def5fdf

Browse files
kevinwillems1993pavel-alay
authored andcommitted
Fix Checkbox field appearance stream for PDF/A
DEVSIX-2172
1 parent e86e59f commit def5fdf

23 files changed

+480
-17
lines changed

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -932,16 +932,16 @@ public static PdfButtonFormField createCheckBox(PdfDocument doc, Rectangle rect,
932932
String pdfAVersion = pdfAConformanceLevel != null ? pdfAConformanceLevel.getPart() : "";
933933
switch (pdfAVersion) {
934934
case "1":
935-
check.drawPdfA1CheckAppearance(rect.getWidth(), rect.getHeight(), value.equals("Off") ? "Yes" : value, checkType);
935+
check.drawPdfA1CheckAppearance(rect.getWidth(), rect.getHeight(), "Off".equals(value) ? "Yes" : value, checkType);
936936
break;
937937
case "2":
938-
check.drawPdfA2CheckAppearance(rect.getWidth(), rect.getHeight(), value.equals("Off") ? "Yes" : value, checkType);
938+
check.drawPdfA2CheckAppearance(rect.getWidth(), rect.getHeight(), "Off".equals(value) ? "Yes" : value, checkType);
939939
break;
940940
case "3":
941-
check.drawPdfA2CheckAppearance(rect.getWidth(), rect.getHeight(), value.equals("Off") ? "Yes" : value, checkType);
941+
check.drawPdfA2CheckAppearance(rect.getWidth(), rect.getHeight(), "Off".equals(value) ? "Yes" : value, checkType);
942942
break;
943943
default:
944-
check.drawCheckAppearance(rect.getWidth(), rect.getHeight(), value.equals("Off") ? "Yes" : value);
944+
check.drawCheckAppearance(rect.getWidth(), rect.getHeight(), "Off".equals(value) ? "Yes" : value);
945945
break;
946946
}
947947

@@ -2145,7 +2145,7 @@ public boolean regenerateField() {
21452145
String pdfAVersion = pdfAConformanceLevel != null ? pdfAConformanceLevel.getPart() : "";
21462146
switch (pdfAVersion) {
21472147
case "1":
2148-
drawPdfA1CheckAppearance(rect.getWidth(), rect.getHeight(), onStateName, checkType);
2148+
drawPdfA1CheckAppearance(rect.getWidth(), rect.getHeight(), value, checkType);
21492149
break;
21502150
case "2":
21512151
drawPdfA2CheckAppearance(rect.getWidth(), rect.getHeight(), onStateName, checkType);
@@ -3098,30 +3098,30 @@ protected void drawCheckAppearance(float width, float height, String onStateName
30983098
widget.setNormalAppearance(normalAppearance);
30993099
}
31003100

3101-
protected void drawPdfA1CheckAppearance(float width, float height, String value, int checkType) {
3101+
protected void drawPdfA1CheckAppearance(float width, float height, String selectedValue, int checkType) {
31023102
PdfStream stream = (PdfStream) new PdfStream().makeIndirect(getDocument());
31033103
PdfCanvas canvas = new PdfCanvas(stream, new PdfResources(), getDocument());
31043104
Rectangle rect = new Rectangle(0, 0, width, height);
31053105
PdfFormXObject xObject = new PdfFormXObject(rect);
31063106

31073107
this.checkType = checkType;
31083108
drawBorder(canvas, xObject, width, height);
3109-
drawPdfACheckBox(canvas, width, height, true);
3110-
3111-
PdfWidgetAnnotation widget = getWidgets().get(0);
3109+
drawPdfACheckBox(canvas, width, height, !"Off".equals(selectedValue));
31123110

31133111
xObject.getPdfObject().getOutputStream().writeBytes(stream.getBytes());
31143112

31153113
PdfDictionary normalAppearance = new PdfDictionary();
3116-
normalAppearance.put(new PdfName(value), xObject.getPdfObject());
3114+
normalAppearance.put(new PdfName(selectedValue), xObject.getPdfObject());
31173115

31183116
PdfDictionary mk = new PdfDictionary();
31193117
mk.put(PdfName.CA, new PdfString(text));
3118+
3119+
PdfWidgetAnnotation widget = getWidgets().get(0);
31203120
widget.put(PdfName.MK, mk);
3121-
widget.setNormalAppearance(xObject.getPdfObject());
3121+
widget.setNormalAppearance(normalAppearance);
31223122
}
31233123

3124-
protected void drawPdfA2CheckAppearance(float width, float height, String value, int checkType) {
3124+
protected void drawPdfA2CheckAppearance(float width, float height, String onStateName, int checkType) {
31253125
PdfStream streamOn = (PdfStream) new PdfStream().makeIndirect(getDocument());
31263126
PdfCanvas canvasOn = new PdfCanvas(streamOn, new PdfResources(), getDocument());
31273127
PdfStream streamOff = (PdfStream) new PdfStream().makeIndirect(getDocument());
@@ -3143,7 +3143,7 @@ protected void drawPdfA2CheckAppearance(float width, float height, String value,
31433143
xObjectOff.getResources();
31443144

31453145
PdfDictionary normalAppearance = new PdfDictionary();
3146-
normalAppearance.put(new PdfName(value), xObjectOn.getPdfObject());
3146+
normalAppearance.put(new PdfName(onStateName), xObjectOn.getPdfObject());
31473147
normalAppearance.put(new PdfName("Off"), xObjectOff.getPdfObject());
31483148

31493149
PdfDictionary mk = new PdfDictionary();

pdfa/src/main/java/com/itextpdf/pdfa/PdfAConformanceException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public class PdfAConformanceException extends PdfException {
114114
public static final String JPEG2000_ENUMERATED_COLOUR_SPACE_19_CIEJAB_SHALL_NOT_BE_USED = "jpeg2000 enumerated colour space 19 (CIEJab) shall not be used";
115115
public static final String LZWDECODE_FILTER_IS_NOT_PERMITTED = "LZWDecode filter is not permitted";
116116
public static final String MIME_TYPE_SHALL_BE_SPECIFIED_USING_THE_SUBTYPE_KEY_OF_THE_FILE_SPECIFICATION_STREAM_DICTIONARY = "Mime type shall be specified using the subtype key of the file specification stream dictionary";
117+
public static final String N_KEY_SHALL_BE_APPEARANCE_SUBDICTIONARY = "If an annotation dictionary's Subtype key has a value of Widget and its FT key has a value of Btn, the value of the N key shall be an appearance subdictionary";
117118
public static final String NAMED_ACTION_TYPE_0_IS_NOT_ALLOWED = "Named action type {0} not allowed";
118119
public static final String NEEDAPPEARANCES_FLAG_OF_THE_INTERACTIVE_FORM_DICTIONARY_SHALL_EITHER_NOT_BE_PRESENTED_OR_SHALL_BE_FALSE = "Needappearances flag of the interactive form dictionary shall either not be presented or shall be false";
119120
public static final String NO_KEYS_OTHER_THAN_UR3_AND_DOC_MDP_SHALL_BE_PRESENT_IN_A_PERMISSIONS_DICTIONARY = "No keys other than UR3 and DocMDP shall be present in a permissions dictionary";

pdfa/src/main/java/com/itextpdf/pdfa/checker/PdfA1Checker.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,11 +452,15 @@ protected void checkAnnotation(PdfDictionary annotDic) {
452452
if (ap.containsKey(PdfName.D) || ap.containsKey(PdfName.R)) {
453453
throw new PdfAConformanceException(PdfAConformanceException.APPEARANCE_DICTIONARY_SHALL_CONTAIN_ONLY_THE_N_KEY_WITH_STREAM_VALUE);
454454
}
455-
PdfStream n = ap.getAsStream(PdfName.N);
456-
if (n == null) {
457-
throw new PdfAConformanceException(PdfAConformanceException.APPEARANCE_DICTIONARY_SHALL_CONTAIN_ONLY_THE_N_KEY_WITH_STREAM_VALUE);
455+
if (PdfName.Widget.equals(annotDic.getAsName(PdfName.Subtype)) && PdfName.Btn.equals(annotDic.getAsName(PdfName.FT))) {
456+
if (ap.getAsDictionary(PdfName.N) == null) {
457+
throw new PdfAConformanceException(PdfAConformanceException.N_KEY_SHALL_BE_APPEARANCE_SUBDICTIONARY);
458+
}
459+
} else {
460+
if (ap.getAsStream(PdfName.N) == null) {
461+
throw new PdfAConformanceException(PdfAConformanceException.APPEARANCE_DICTIONARY_SHALL_CONTAIN_ONLY_THE_N_KEY_WITH_STREAM_VALUE);
462+
}
458463
}
459-
460464
checkResourcesOfAppearanceStreams(ap);
461465
}
462466

@@ -500,6 +504,7 @@ protected void checkForm(PdfDictionary form) {
500504
}
501505
}
502506

507+
@Override
503508
protected void checkAction(PdfDictionary action) {
504509
if (isAlreadyChecked(action)) return;
505510

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.itextpdf.pdfa;
2+
3+
import com.itextpdf.forms.PdfAcroForm;
4+
import com.itextpdf.forms.fields.PdfFormField;
5+
import com.itextpdf.kernel.colors.ColorConstants;
6+
import com.itextpdf.kernel.geom.Rectangle;
7+
import com.itextpdf.kernel.pdf.PdfAConformanceLevel;
8+
import com.itextpdf.kernel.pdf.PdfOutputIntent;
9+
import com.itextpdf.kernel.pdf.PdfWriter;
10+
import com.itextpdf.kernel.utils.CompareTool;
11+
import com.itextpdf.test.ExtendedITextTest;
12+
import com.itextpdf.test.annotations.type.IntegrationTest;
13+
import org.junit.Assert;
14+
import org.junit.BeforeClass;
15+
import org.junit.Test;
16+
import org.junit.experimental.categories.Category;
17+
18+
import java.io.FileInputStream;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
22+
@Category(IntegrationTest.class)
23+
public class PdfA1bCheckfieldAppearanceTest extends ExtendedITextTest {
24+
25+
public static final String sourceFolder = "./src/test/resources/com/itextpdf/pdfa/";
26+
public static final String cmpFolder = "./src/test/resources/com/itextpdf/pdfa/cmp/PdfA1bCheckfieldAppearanceTest/";
27+
public static final String destinationFolder = "./target/test/com/itextpdf/pdfa/PdfA1bCheckfieldAppearanceTest/";
28+
29+
@BeforeClass
30+
public static void beforeClass() {
31+
createDestinationFolder(destinationFolder);
32+
}
33+
34+
@Test
35+
public void pdfA1bCheckFieldOffAppearanceTest() throws IOException, InterruptedException {
36+
String name = "pdfA1b_checkFieldOffAppearance";
37+
String outPath = destinationFolder + name + ".pdf";
38+
String cmpPath = cmpFolder + "cmp_" + name + ".pdf";
39+
String diff = "diff_" + name + "_";
40+
41+
PdfWriter writer = new PdfWriter(outPath);
42+
InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm");
43+
PdfADocument doc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_1B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is));
44+
doc.addNewPage();
45+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
46+
PdfFormField chk = PdfFormField.createCheckBox(doc, new Rectangle(100,500,50,50), "name", "Off", PdfFormField.TYPE_CHECK, PdfAConformanceLevel.PDF_A_1B);
47+
chk.setBorderColor(ColorConstants.BLACK);
48+
chk.setBorderWidth(1);
49+
form.addField(chk);
50+
doc.close();
51+
52+
Assert.assertNull(new CompareTool().compareByContent(outPath, cmpPath, destinationFolder, diff));
53+
}
54+
55+
@Test
56+
public void pdfA1bCheckFieldOnAppearanceTest() throws IOException, InterruptedException {
57+
String name = "pdfA1b_checkFieldOnAppearance";
58+
String outPath = destinationFolder + name + ".pdf";
59+
String cmpPath = cmpFolder + "cmp_" + name + ".pdf";
60+
String diff = "diff_" + name + "_";
61+
62+
PdfWriter writer = new PdfWriter(outPath);
63+
InputStream is = new FileInputStream(sourceFolder + "sRGB Color Space Profile.icm");
64+
PdfADocument doc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_1B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", is));
65+
doc.addNewPage();
66+
PdfAcroForm form = PdfAcroForm.getAcroForm(doc, true);
67+
PdfFormField chk = PdfFormField.createCheckBox(doc, new Rectangle(100,500,50,50), "name", "On", PdfFormField.TYPE_CHECK, PdfAConformanceLevel.PDF_A_1B);
68+
chk.setBorderColor(ColorConstants.BLACK);
69+
chk.setBorderWidth(1);
70+
form.addField(chk);
71+
doc.close();
72+
73+
Assert.assertNull(new CompareTool().compareByContent(outPath, cmpPath, destinationFolder, diff));
74+
}
75+
}

0 commit comments

Comments
 (0)