Skip to content

Commit dc76524

Browse files
committed
Add ability to set null values for SignedAppearanceText
1 parent ad761fc commit dc76524

File tree

8 files changed

+135
-29
lines changed

8 files changed

+135
-29
lines changed

forms/src/main/java/com/itextpdf/forms/fields/properties/SignedAppearanceText.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ public String getReasonLine() {
7979
* @return this same {@link SignedAppearanceText} instance.
8080
*/
8181
public SignedAppearanceText setReasonLine(String reason) {
82-
this.reason = reason.trim();
82+
if (reason != null) {
83+
reason = reason.trim();
84+
}
85+
this.reason = reason;
8386
return this;
8487
}
8588

@@ -99,12 +102,15 @@ public String getLocationLine() {
99102
* Note, that this location won't be passed to the signature dictionary. If none is set, value set by
100103
* {@code PdfSigner#setLocation} will be used.
101104
*
102-
* @param location new signing location.
105+
* @param location new signing location
103106
*
104-
* @return this same {@link SignedAppearanceText} instance.
107+
* @return this same {@link SignedAppearanceText} instance
105108
*/
106109
public SignedAppearanceText setLocationLine(String location) {
107-
this.location = location.trim();
110+
if (location != null) {
111+
location = location.trim();
112+
}
113+
this.location = location;
108114
return this;
109115
}
110116

@@ -114,19 +120,22 @@ public SignedAppearanceText setLocationLine(String location) {
114120
* <p>
115121
* Note, that the signer name will be replaced by the one from the signing certificate during the actual signing.
116122
*
117-
* @param signedBy name of the signer.
123+
* @param signedBy name of the signer
118124
*
119-
* @return this same {@link SignedAppearanceText} instance.
125+
* @return this same {@link SignedAppearanceText} instance
120126
*/
121127
public SignedAppearanceText setSignedBy(String signedBy) {
122-
this.signedBy = signedBy.trim();
128+
if (signedBy != null) {
129+
signedBy = signedBy.trim();
130+
}
131+
this.signedBy = signedBy;
123132
return this;
124133
}
125134

126135
/**
127136
* Gets the name of the signer from the certificate.
128137
*
129-
* @return signedBy name of the signer.
138+
* @return signedBy name of the signer
130139
*/
131140
public String getSignedBy() {
132141
return signedBy;
@@ -147,8 +156,9 @@ public java.util.Calendar getSignDate() {
147156
* <p>
148157
* Note, that the signing date will be replaced by the one from the {@code PdfSigner} during the signing.
149158
*
150-
* @param signDate new signature date.
151-
* @return this same {@link SignedAppearanceText} instance.
159+
* @param signDate new signature date
160+
*
161+
* @return this same {@link SignedAppearanceText} instance
152162
*/
153163
public SignedAppearanceText setSignDate(java.util.Calendar signDate) {
154164
this.signDate = signDate;
@@ -159,7 +169,7 @@ public SignedAppearanceText setSignDate(java.util.Calendar signDate) {
159169
/**
160170
* Generates the signature description text based on the provided parameters.
161171
*
162-
* @return signature description.
172+
* @return signature description
163173
*/
164174
public String generateDescriptionText() {
165175
final StringBuilder buf = new StringBuilder();

forms/src/test/java/com/itextpdf/forms/form/element/SignatureFieldAppearanceTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,44 @@ public void customizedSigFieldTest() throws IOException, InterruptedException {
165165
Assertions.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
166166
}
167167

168+
@Test
169+
public void emptySigFieldAppearanceTest() throws IOException, InterruptedException {
170+
String outPdf = DESTINATION_FOLDER + "emptySigFieldAppearance.pdf";
171+
String cmpPdf = SOURCE_FOLDER + "cmp_emptySigFieldAppearance.pdf";
172+
173+
try (Document document = new Document(new PdfDocument(new PdfWriter(outPdf)))) {
174+
SignedAppearanceText description = new SignedAppearanceText()
175+
.setSignedBy(null).setLocationLine(null).setReasonLine(null);
176+
177+
SignatureFieldAppearance formSigField = new SignatureFieldAppearance("Signature1").setContent(description);
178+
formSigField.setBackgroundColor(ColorConstants.LIGHT_GRAY);
179+
formSigField.setBorder(new SolidBorder(ColorConstants.GREEN, 2));
180+
formSigField.setHeight(100).setWidth(200);
181+
document.add(formSigField);
182+
}
183+
184+
Assertions.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
185+
}
186+
187+
@Test
188+
public void ignoreSignDateAndReasonInAppearanceTest() throws IOException, InterruptedException {
189+
String outPdf = DESTINATION_FOLDER + "ignoreSignDateAndReasonInAppearance.pdf";
190+
String cmpPdf = SOURCE_FOLDER + "cmp_ignoreSignDateAndReasonInAppearance.pdf";
191+
192+
try (Document document = new Document(new PdfDocument(new PdfWriter(outPdf)))) {
193+
SignedAppearanceText description = new SignedAppearanceText()
194+
.setSignedBy("Signer Name").setLocationLine("Test Location").setReasonLine(null);
195+
196+
SignatureFieldAppearance formSigField = new SignatureFieldAppearance("Signature1").setContent(description);
197+
formSigField.setBackgroundColor(ColorConstants.LIGHT_GRAY);
198+
formSigField.setBorder(new SolidBorder(ColorConstants.GREEN, 2));
199+
formSigField.setHeight(100).setWidth(200);
200+
document.add(formSigField);
201+
}
202+
203+
Assertions.assertNull(new CompareTool().compareByContent(outPdf, cmpPdf, DESTINATION_FOLDER));
204+
}
205+
168206
@Test
169207
@LogMessages(messages = @LogMessage(messageTemplate = IoLogMessageConstant.CLIP_ELEMENT))
170208
public void signatureFieldVerticalAlignmentTest() throws IOException, InterruptedException {

sign/src/main/java/com/itextpdf/signatures/PdfSigner.java

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ This file is part of the iText (R) project.
9494
import java.security.cert.X509Certificate;
9595
import java.util.ArrayList;
9696
import java.util.Arrays;
97+
import java.util.Calendar;
9798
import java.util.Collection;
9899
import java.util.HashMap;
99100
import java.util.List;
@@ -623,7 +624,10 @@ public void signDetached(IExternalDigest externalDigest, IExternalSignature exte
623624
dic.setLocation(this.signerProperties.getLocation());
624625
dic.setSignatureCreator(this.signerProperties.getSignatureCreator());
625626
dic.setContact(this.signerProperties.getContact());
626-
dic.setDate(new PdfDate(this.signerProperties.getClaimedSignDate())); // time-stamp will over-rule this
627+
Calendar claimedSignDate = this.signerProperties.getClaimedSignDate();
628+
if (claimedSignDate != TimestampConstants.UNDEFINED_TIMESTAMP_DATE) {
629+
dic.setDate(new PdfDate(claimedSignDate)); // time-stamp will over-rule this
630+
}
627631
cryptoDictionary = dic;
628632

629633
Map<PdfName, Integer> exc = new HashMap<>();
@@ -1357,8 +1361,9 @@ PdfSignature createSignatureDictionary(boolean includeDate) {
13571361
dic.setLocation(this.signerProperties.getLocation());
13581362
dic.setSignatureCreator(this.signerProperties.getSignatureCreator());
13591363
dic.setContact(this.signerProperties.getContact());
1360-
if (includeDate) {
1361-
dic.setDate(new PdfDate(this.signerProperties.getClaimedSignDate())); // time-stamp will over-rule this
1364+
Calendar claimedSignDate = this.signerProperties.getClaimedSignDate();
1365+
if (includeDate && claimedSignDate != TimestampConstants.UNDEFINED_TIMESTAMP_DATE) {
1366+
dic.setDate(new PdfDate(claimedSignDate)); // time-stamp will over-rule this
13621367
}
13631368
return dic;
13641369
}
@@ -1412,25 +1417,34 @@ private void setContent() {
14121417
}
14131418

14141419
private SignedAppearanceText generateSignatureText() {
1415-
return new SignedAppearanceText()
1416-
.setSignedBy(signerName)
1417-
.setSignDate(this.signerProperties.getClaimedSignDate())
1418-
.setReasonLine("Reason: " + this.signerProperties.getReason())
1419-
.setLocationLine("Location: " + this.signerProperties.getLocation());
1420+
SignedAppearanceText signedAppearanceText = new SignedAppearanceText();
1421+
fillInAppearanceText(signedAppearanceText);
1422+
return signedAppearanceText;
14201423
}
14211424

14221425
private void populateExistingModelElement() {
14231426
this.signerProperties.getSignatureAppearance().setSignerName(signerName);
1424-
SignedAppearanceText signedAppearanceText =
1425-
this.signerProperties.getSignatureAppearance().getSignedAppearanceText();
1426-
if (signedAppearanceText != null) {
1427-
signedAppearanceText.setSignedBy(signerName).setSignDate(this.signerProperties.getClaimedSignDate());
1428-
if (signedAppearanceText.getReasonLine().isEmpty()) {
1429-
signedAppearanceText.setReasonLine("Reason: " + this.signerProperties.getReason());
1430-
}
1431-
if (signedAppearanceText.getLocationLine().isEmpty()) {
1432-
signedAppearanceText.setLocationLine("Location: " + this.signerProperties.getLocation());
1433-
}
1427+
SignedAppearanceText appearanceText = this.signerProperties.getSignatureAppearance().getSignedAppearanceText();
1428+
if (appearanceText != null) {
1429+
fillInAppearanceText(appearanceText);
1430+
}
1431+
}
1432+
1433+
private void fillInAppearanceText(SignedAppearanceText appearanceText) {
1434+
appearanceText.setSignedBy(signerName);
1435+
Calendar claimedSignDate = this.signerProperties.getClaimedSignDate();
1436+
if (claimedSignDate != TimestampConstants.UNDEFINED_TIMESTAMP_DATE) {
1437+
appearanceText.setSignDate(claimedSignDate);
1438+
}
1439+
String reason = signerProperties.getReason();
1440+
boolean setReason = appearanceText.getReasonLine() != null && appearanceText.getReasonLine().isEmpty();
1441+
if (setReason && reason != null && !reason.isEmpty()) {
1442+
appearanceText.setReasonLine("Reason: " + reason);
1443+
}
1444+
String location = signerProperties.getLocation();
1445+
boolean setLocation = appearanceText.getLocationLine() != null && appearanceText.getLocationLine().isEmpty();
1446+
if (setLocation && location != null && !location.isEmpty()) {
1447+
appearanceText.setLocationLine("Location: " + location);
14341448
}
14351449
}
14361450

sign/src/main/java/com/itextpdf/signatures/SignerProperties.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ This file is part of the iText (R) project.
3535
* Properties to be used in signing operations.
3636
*/
3737
public class SignerProperties {
38+
/**
39+
* This string could be used to create the {@link SignatureFieldAppearance} instance which will be used for signing
40+
* since its ID will be ignored anyway in that case, and specified ID won't override the field name.
41+
*
42+
* @see #setSignatureAppearance(SignatureFieldAppearance)
43+
*/
3844
public static final String IGNORED_ID = "";
3945

4046
private PdfSigFieldLock fieldLock;

sign/src/test/java/com/itextpdf/signatures/sign/SignedAppearanceTextTest.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ This file is part of the iText (R) project.
6161
import com.itextpdf.signatures.PdfSigner;
6262
import com.itextpdf.signatures.PrivateKeySignature;
6363
import com.itextpdf.signatures.SignerProperties;
64+
import com.itextpdf.signatures.TimestampConstants;
6465
import com.itextpdf.signatures.testutils.PemFileHelper;
6566
import com.itextpdf.signatures.testutils.SignaturesCompareTool;
6667
import com.itextpdf.test.ExtendedITextTest;
@@ -75,9 +76,11 @@ This file is part of the iText (R) project.
7576
import java.security.cert.Certificate;
7677
import java.security.cert.CertificateException;
7778
import java.util.Arrays;
79+
import java.util.Calendar;
7880
import java.util.HashMap;
7981
import java.util.List;
8082
import java.util.Map;
83+
8184
import org.junit.jupiter.api.Assertions;
8285
import org.junit.jupiter.api.BeforeAll;
8386
import org.junit.jupiter.api.BeforeEach;
@@ -132,6 +135,41 @@ public void defaultSignedAppearanceTextTest() throws GeneralSecurityException, I
132135
Assertions.assertNull(SignaturesCompareTool.compareSignatures(outPdf, cmpPdf));
133136
}
134137

138+
@Test
139+
public void noReasonLocationSignDateInAppearanceTextTest() throws GeneralSecurityException, IOException, InterruptedException {
140+
String srcFile = SOURCE_FOLDER + "simpleDocument.pdf";
141+
String cmpPdf = SOURCE_FOLDER + "cmp_noReasonLocationSignDateInAppearanceText.pdf";
142+
String outPdf = DESTINATION_FOLDER + "noReasonLocationSignDateInAppearanceText.pdf";
143+
144+
Rectangle rect = new Rectangle(36, 648, 200, 100);
145+
146+
String fieldName = "Signature1";
147+
SignatureFieldAppearance appearance = new SignatureFieldAppearance(SignerProperties.IGNORED_ID)
148+
.setContent(new SignedAppearanceText().setReasonLine(null).setLocationLine(null));
149+
150+
PdfSigner signer = new PdfSigner(new PdfReader(srcFile), FileUtil.getFileOutputStream(outPdf),
151+
new StampingProperties());
152+
153+
SignerProperties signerProperties = new SignerProperties()
154+
.setCertificationLevel(AccessPermissions.UNSPECIFIED)
155+
.setFieldName(fieldName)
156+
.setReason("Test 1")
157+
.setLocation("TestCity 1")
158+
.setSignatureAppearance(appearance)
159+
.setClaimedSignDate((Calendar) TimestampConstants.UNDEFINED_TIMESTAMP_DATE)
160+
.setPageRect(rect);
161+
signer.setSignerProperties(signerProperties);
162+
163+
// Creating the signature
164+
IExternalSignature pks = new PrivateKeySignature(pk, DigestAlgorithms.SHA256, FACTORY.getProviderName());
165+
signer.signDetached(new BouncyCastleDigest(), pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CADES);
166+
167+
Assertions.assertNull(new CompareTool().compareVisually(outPdf, cmpPdf, DESTINATION_FOLDER, "diff_",
168+
getTestMap(new Rectangle(36, 676, 200, 15))));
169+
170+
Assertions.assertNull(SignaturesCompareTool.compareSignatures(outPdf, cmpPdf));
171+
}
172+
135173
@Test
136174
public void signPDFADocumentWithoutSettingFont() throws IOException {
137175
String srcFile = DESTINATION_FOLDER + "simplePDFA.pdf";

0 commit comments

Comments
 (0)