Skip to content

Commit 7d722c5

Browse files
committed
Extend SignatureFieldAppearance model element API
DEVSIX-7796
1 parent fc1a152 commit 7d722c5

File tree

41 files changed

+1070
-798
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1070
-798
lines changed

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

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This file is part of the iText (R) project.
2727
import com.itextpdf.forms.PdfAcroForm;
2828
import com.itextpdf.forms.fields.borders.FormBorderFactory;
2929
import com.itextpdf.forms.fields.properties.CheckBoxType;
30+
import com.itextpdf.forms.fields.properties.SignedAppearanceText;
3031
import com.itextpdf.forms.form.FormProperty;
3132
import com.itextpdf.forms.form.element.Button;
3233
import com.itextpdf.forms.form.element.CheckBox;
@@ -48,6 +49,7 @@ This file is part of the iText (R) project.
4849
import com.itextpdf.kernel.colors.DeviceRgb;
4950
import com.itextpdf.kernel.geom.Rectangle;
5051
import com.itextpdf.kernel.pdf.PdfArray;
52+
import com.itextpdf.kernel.pdf.PdfDate;
5153
import com.itextpdf.kernel.pdf.PdfDictionary;
5254
import com.itextpdf.kernel.pdf.PdfDocument;
5355
import com.itextpdf.kernel.pdf.PdfIndirectReference;
@@ -106,7 +108,7 @@ public class PdfFormAnnotation extends AbstractPdfFormField {
106108
public static final String ON_STATE_VALUE = "Yes";
107109
private static final Logger LOGGER = LoggerFactory.getLogger(PdfFormAnnotation.class);
108110
private static final String LINE_ENDINGS_REGEXP = "\\r\\n|\\r|\\n";
109-
private static float EPS = 1e-4f;
111+
private static final float EPS = 1e-4f;
110112
protected float borderWidth = 1;
111113
protected Color backgroundColor;
112114
protected Color borderColor;
@@ -1114,17 +1116,25 @@ void createSigField() {
11141116
((SignatureFieldAppearance) formFieldElement).setFontColor(color);
11151117
}
11161118

1117-
PdfString reason = parent.getPdfObject().getAsString(PdfName.Reason);
1118-
if (reason != null) {
1119-
((SignatureFieldAppearance) formFieldElement).setReason(reason.toUnicodeString());
1120-
}
1121-
PdfString location = parent.getPdfObject().getAsString(PdfName.Location);
1122-
if (location != null) {
1123-
((SignatureFieldAppearance) formFieldElement).setLocation(location.toUnicodeString());
1124-
}
1125-
PdfString contact = parent.getPdfObject().getAsString(PdfName.ContactInfo);
1126-
if (contact != null) {
1127-
((SignatureFieldAppearance) formFieldElement).setContact(contact.toUnicodeString());
1119+
if (((SignatureFieldAppearance) formFieldElement).getContentElements().isEmpty()) {
1120+
final SignedAppearanceText description = new SignedAppearanceText();
1121+
final PdfName name = parent.getPdfObject().getAsName(PdfName.Name);
1122+
if (name != null) {
1123+
description.setSignedBy(name.getValue());
1124+
}
1125+
final PdfString reason = parent.getPdfObject().getAsString(PdfName.Reason);
1126+
if (reason != null) {
1127+
description.setReasonLine("Reason: " + reason.toUnicodeString());
1128+
}
1129+
final PdfString location = parent.getPdfObject().getAsString(PdfName.Location);
1130+
if (location != null) {
1131+
description.setLocationLine("Location: " + location.toUnicodeString());
1132+
}
1133+
final PdfString date = parent.getPdfObject().getAsString(PdfName.M);
1134+
if (date != null) {
1135+
description.setSignDate(PdfDate.decode(date.getValue()));
1136+
}
1137+
((SignatureFieldAppearance) formFieldElement).setContent(description);
11281138
}
11291139
}
11301140

@@ -1359,16 +1369,16 @@ private PdfFormXObject createTopLayer(float width, float height) {
13591369
* @return n0 layer xObject.
13601370
*/
13611371
private PdfFormXObject createN0Layer(float width, float height) {
1362-
if (((SignatureFieldAppearance) formFieldElement).getBackgroundLayer() != null) {
1363-
return ((SignatureFieldAppearance) formFieldElement).getBackgroundLayer();
1372+
if (((PdfSignatureFormField) parent).getBackgroundLayer() != null) {
1373+
return ((PdfSignatureFormField) parent).getBackgroundLayer();
13641374
}
13651375
// Create blank n0.
13661376
PdfFormXObject n0LayerXObject = new PdfFormXObject(new Rectangle(0, 0, width, height));
13671377
n0LayerXObject.makeIndirect(getDocument());
13681378
PdfCanvas canvas = new PdfCanvas(n0LayerXObject, getDocument());
13691379
canvas.writeLiteral("% DSBlank\n");
13701380

1371-
if (((SignatureFieldAppearance) formFieldElement).isReuseAppearance()) {
1381+
if (((PdfSignatureFormField) parent).isReuseAppearance()) {
13721382
// Reuse existed field appearance as a background
13731383
PdfAcroForm acroForm = PdfFormCreator.getAcroForm(getDocument(), true);
13741384
PdfFormField field = acroForm.getField(parent.getFieldName().toUnicodeString());
@@ -1377,7 +1387,7 @@ private PdfFormXObject createN0Layer(float width, float height) {
13771387
if (oldAppearanceStream != null) {
13781388
n0LayerXObject = new PdfFormXObject(oldAppearanceStream);
13791389
} else {
1380-
((SignatureFieldAppearance) formFieldElement).setReuseAppearance(false);
1390+
((PdfSignatureFormField) parent).setReuseAppearance(false);
13811391
}
13821392
}
13831393
return n0LayerXObject;
@@ -1397,8 +1407,8 @@ private PdfFormXObject createN0Layer(float width, float height) {
13971407
* @return n2 layer xObject.
13981408
*/
13991409
private PdfFormXObject createN2Layer(float width, float height) {
1400-
if (((SignatureFieldAppearance) formFieldElement).getSignatureAppearanceLayer() != null) {
1401-
return ((SignatureFieldAppearance) formFieldElement).getSignatureAppearanceLayer();
1410+
if (((PdfSignatureFormField) parent).getSignatureAppearanceLayer() != null) {
1411+
return ((PdfSignatureFormField) parent).getSignatureAppearanceLayer();
14021412
}
14031413
PdfFormXObject n2LayerXObject = new PdfFormXObject(new Rectangle(0, 0, width, height));
14041414
Canvas n2LayerCanvas = new Canvas(n2LayerXObject, this.getDocument());

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,29 @@ This file is part of the iText (R) project.
2828
import com.itextpdf.kernel.pdf.PdfObject;
2929
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
3030
import com.itextpdf.forms.PdfSigFieldLock;
31+
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
3132

3233

3334
/**
3435
* An AcroForm field containing signature data.
3536
*/
3637
public class PdfSignatureFormField extends PdfFormField {
3738

39+
/**
40+
* Indicates if we need to reuse the existing appearance as a background layer.
41+
*/
42+
private boolean reuseAppearance = false;
43+
44+
/**
45+
* Background level of the signature appearance.
46+
*/
47+
private PdfFormXObject n0;
48+
49+
/**
50+
* Signature appearance layer that contains information about the signature.
51+
*/
52+
private PdfFormXObject n2;
53+
3854
protected PdfSignatureFormField(PdfDocument pdfDocument) {
3955
super(pdfDocument);
4056
}
@@ -79,4 +95,70 @@ public PdfSigFieldLock getSigFieldLockDictionary() {
7995
PdfDictionary sigLockDict = (PdfDictionary) getPdfObject().get(PdfName.Lock);
8096
return sigLockDict == null ? null : new PdfSigFieldLock(sigLockDict);
8197
}
98+
99+
/**
100+
* Sets the background layer that is present when creating the signature field.
101+
*
102+
* @param n0 layer xObject.
103+
*
104+
* @return this same {@link PdfSignatureFormField} instance.
105+
*/
106+
public PdfSignatureFormField setBackgroundLayer(PdfFormXObject n0) {
107+
this.n0 = n0;
108+
regenerateField();
109+
return this;
110+
}
111+
112+
/**
113+
* Sets the signature appearance layer that contains information about the signature, e.g. the line art for the
114+
* handwritten signature, the text giving the signer’s name, date, reason, location and so on.
115+
*
116+
* @param n2 layer xObject.
117+
*
118+
* @return this same {@link PdfSignatureFormField} instance.
119+
*/
120+
public PdfSignatureFormField setSignatureAppearanceLayer(PdfFormXObject n2) {
121+
this.n2 = n2;
122+
regenerateField();
123+
return this;
124+
}
125+
126+
/**
127+
* Indicates that the existing appearances needs to be reused as a background.
128+
*
129+
* @param reuseAppearance is an appearances reusing flag value to set.
130+
* @return this same {@link PdfSignatureFormField} instance.
131+
*/
132+
public PdfSignatureFormField setReuseAppearance(boolean reuseAppearance) {
133+
this.reuseAppearance = reuseAppearance;
134+
return this;
135+
}
136+
137+
/**
138+
* Gets the background layer that is present when creating the signature field if it was set.
139+
*
140+
* @return n0 layer xObject.
141+
*/
142+
PdfFormXObject getBackgroundLayer() {
143+
return n0;
144+
}
145+
146+
/**
147+
* Gets the signature appearance layer that contains information about the signature if it was set.
148+
*
149+
* @return n2 layer xObject.
150+
*/
151+
PdfFormXObject getSignatureAppearanceLayer() {
152+
return n2;
153+
}
154+
155+
/**
156+
* Indicates if the existing appearances needs to be reused as a background.
157+
*
158+
* @return appearances reusing flag value.
159+
*/
160+
boolean isReuseAppearance() {
161+
return reuseAppearance;
162+
}
163+
82164
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2023 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
package com.itextpdf.forms.fields.properties;
24+
25+
import com.itextpdf.commons.utils.DateTimeUtil;
26+
27+
import java.util.Calendar;
28+
29+
/**
30+
* Class representing the signature text identifying the signer.
31+
*/
32+
public class SignedAppearanceText {
33+
/**
34+
* The reason for signing.
35+
*/
36+
private String reason = "Reason: ";
37+
38+
/**
39+
* Holds value of property location.
40+
*/
41+
private String location = "Location: ";
42+
43+
/**
44+
* The name of the signer from the certificate.
45+
*/
46+
private String signedBy = "";
47+
48+
/**
49+
* Holds value of property signDate.
50+
*/
51+
private Calendar signDate;
52+
private boolean isSignDateSet = false;
53+
54+
/**
55+
* Creates a new {@link SignedAppearanceText} instance.
56+
*/
57+
public SignedAppearanceText() {
58+
// Empty constructor.
59+
}
60+
61+
/**
62+
* Returns the signing reason.
63+
*
64+
* @return reason for signing.
65+
*/
66+
public String getReasonLine() {
67+
return reason;
68+
}
69+
70+
/**
71+
* Sets the signing reason.
72+
*
73+
* @param reason signing reason.
74+
*
75+
* @return this same {@link SignedAppearanceText} instance.
76+
*/
77+
public SignedAppearanceText setReasonLine(String reason) {
78+
this.reason = reason;
79+
return this;
80+
}
81+
82+
/**
83+
* Returns the signing location.
84+
*
85+
* @return signing location.
86+
*/
87+
public String getLocationLine() {
88+
return location;
89+
}
90+
91+
/**
92+
* Sets the signing location.
93+
*
94+
* @param location new signing location.
95+
*
96+
* @return this same {@link SignedAppearanceText} instance.
97+
*/
98+
public SignedAppearanceText setLocationLine(String location) {
99+
this.location = location;
100+
return this;
101+
}
102+
103+
/**
104+
* Sets the name of the signer from the certificate.
105+
*
106+
* @param signedBy name of the signer.
107+
*
108+
* @return this same {@link SignedAppearanceText} instance.
109+
*/
110+
public SignedAppearanceText setSignedBy(String signedBy) {
111+
this.signedBy = signedBy;
112+
return this;
113+
}
114+
115+
/**
116+
* Gets the name of the signer from the certificate.
117+
*
118+
* @return signedBy name of the signer.
119+
*/
120+
public String getSignedBy() {
121+
return signedBy;
122+
}
123+
124+
/**
125+
* Returns the signature date.
126+
*
127+
* @return the signature date
128+
*/
129+
public java.util.Calendar getSignDate() {
130+
return signDate;
131+
}
132+
133+
/**
134+
* Sets the signature date.
135+
*
136+
* @param signDate new signature date.
137+
* @return this same {@link SignedAppearanceText} instance.
138+
*/
139+
public SignedAppearanceText setSignDate(java.util.Calendar signDate) {
140+
this.signDate = signDate;
141+
this.isSignDateSet = true;
142+
return this;
143+
}
144+
145+
/**
146+
* Generates the signature description text based on the provided parameters.
147+
*
148+
* @return signature description.
149+
*/
150+
public String generateDescriptionText() {
151+
final StringBuilder buf = new StringBuilder();
152+
if (!signedBy.isEmpty()) {
153+
buf.append("Digitally signed by ").append(signedBy);
154+
}
155+
if (isSignDateSet) {
156+
buf.append('\n').append("Date: ").append(DateTimeUtil.dateToString(signDate));
157+
}
158+
if (reason != null) {
159+
buf.append('\n').append(reason);
160+
}
161+
if (location != null) {
162+
buf.append('\n').append(location);
163+
}
164+
return buf.toString();
165+
}
166+
}

0 commit comments

Comments
 (0)