Skip to content

Commit 8e5d358

Browse files
Robustness tweaks
- Only override colour settings in XFDF if unspecified in the configuration - Avoid NPEs on flushing by only setting certain fields if they're specified.
1 parent d9993b0 commit 8e5d358

File tree

2 files changed

+77
-38
lines changed

2 files changed

+77
-38
lines changed

src/main/java/com/itextpdf/forms/xfdf/XfdfObjectReadingUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ public static int convertFlagsFromString(String flagsString) {
141141
/**
142142
* Converts string containing hex color code into an array of 3 integer values representing rgb color.
143143
*/
144-
public static float[] convertColorFloatsFromString(String colorHexString) {
145-
float[] result = new float[3];
144+
public static int[] convertColorFloatsFromString(String colorHexString) {
145+
int[] result = new int[3];
146146
String colorString = colorHexString.substring(colorHexString.indexOf('#') + 1);
147147
if (colorString.length() == 6) {
148148
for (int i = 0; i < 3; i++) {

src/main/java/com/itextpdf/research/xfdfmerge/XfdfMerge.java

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.itextpdf.forms.xfdf.XfdfObject;
4040
import com.itextpdf.forms.xfdf.XfdfObjectReadingUtils;
4141
import com.itextpdf.io.logs.IoLogMessageConstant;
42+
import com.itextpdf.kernel.colors.Color;
4243
import com.itextpdf.kernel.colors.DeviceRgb;
4344
import com.itextpdf.kernel.geom.AffineTransform;
4445
import com.itextpdf.kernel.geom.Rectangle;
@@ -73,6 +74,7 @@
7374
public class XfdfMerge {
7475

7576
private static final Logger LOGGER = LoggerFactory.getLogger(XfdfMerge.class);
77+
private final Color DEFAULT_HIGHLIGHT_COLOR = new DeviceRgb(1f, 0.81f, 0f);
7678
private final PdfDocument pdfDocument;
7779
private final Map<String, PdfAnnotation> annotMap = new HashMap<>();
7880
private final Map<String, List<PdfMarkupAnnotation>> replyMap = new HashMap<>();
@@ -109,19 +111,37 @@ private void mergeAnnotations(AnnotsObject annotsObject) {
109111
}
110112
}
111113

112-
private void addCommonAnnotationAttributes(PdfAnnotation annotation, AnnotObject annotObject) {
114+
private Color getAnnotColor(AnnotObject annotObject, Color defaultColor) {
115+
String colorString = annotObject.getAttributeValue(XfdfConstants.COLOR);
116+
if(colorString != null) {
117+
int[] rgbValues = XfdfObjectReadingUtils.convertColorFloatsFromString(colorString);
118+
return new DeviceRgb(rgbValues[0], rgbValues[1], rgbValues[2]);
119+
} else {
120+
return defaultColor;
121+
}
122+
}
123+
124+
private void addCommonAnnotationAttributes(PdfAnnotation annotation, AnnotObject annotObject, Color color) {
113125
annotation.setFlags(XfdfObjectReadingUtils.convertFlagsFromString(annotObject.getAttributeValue(XfdfConstants.FLAGS)));
114-
annotation.setColor(XfdfObjectReadingUtils.convertColorFloatsFromString(annotObject.getAttributeValue(XfdfConstants.COLOR)));
115-
annotation.setDate(new PdfString(annotObject.getAttributeValue(XfdfConstants.DATE)));
126+
annotation.setColor(color);
127+
String dateString = annotObject.getAttributeValue(XfdfConstants.DATE);
128+
if(dateString != null) {
129+
annotation.setDate(new PdfString(dateString));
130+
}
116131
String name = annotObject.getAttributeValue(XfdfConstants.NAME);
117-
annotation.setName(new PdfString(name));
118-
annotMap.put(name, annotation);
119-
// add pending replies
120-
for(PdfMarkupAnnotation reply : replyMap.getOrDefault(name, Collections.emptyList())) {
121-
reply.setInReplyTo(annotation);
132+
if(name != null) {
133+
annotation.setName(new PdfString(name));
134+
annotMap.put(name, annotation);
135+
// add pending replies
136+
for(PdfMarkupAnnotation reply : replyMap.getOrDefault(name, Collections.emptyList())) {
137+
reply.setInReplyTo(annotation);
138+
}
139+
replyMap.remove(name);
140+
}
141+
String titleString = annotObject.getAttributeValue(XfdfConstants.TITLE);
142+
if(titleString != null) {
143+
annotation.setTitle(new PdfString(titleString));
122144
}
123-
replyMap.remove(name);
124-
annotation.setTitle(new PdfString(annotObject.getAttributeValue(XfdfConstants.TITLE)));
125145
}
126146

127147
private void addPopupAnnotation(int page, PdfMarkupAnnotation parent, AnnotObject popup) {
@@ -136,8 +156,14 @@ private void addPopupAnnotation(int page, PdfMarkupAnnotation parent, AnnotObjec
136156
}
137157

138158
private void addMarkupAnnotationAttributes(PdfMarkupAnnotation annotation, AnnotObject annotObject) {
139-
annotation.setCreationDate(new PdfString(annotObject.getAttributeValue(XfdfConstants.CREATION_DATE)));
140-
annotation.setSubject(new PdfString(annotObject.getAttributeValue(XfdfConstants.SUBJECT)));
159+
String creationDateString = annotObject.getAttributeValue(XfdfConstants.CREATION_DATE);
160+
if(creationDateString != null) {
161+
annotation.setCreationDate(new PdfString(creationDateString));
162+
}
163+
String subjectString = annotObject.getAttributeValue(XfdfConstants.SUBJECT);
164+
if(subjectString != null) {
165+
annotation.setSubject(new PdfString(subjectString));
166+
}
141167
String intent = annotObject.getAttributeValue("IT");
142168
if(intent != null && !intent.isBlank()) {
143169
annotation.setIntent(new PdfName(intent));
@@ -193,14 +219,14 @@ private int readAnnotPage(AnnotObject annotObject) {
193219
return this.pageShift + page;
194220
}
195221

196-
private PdfFormXObject getCaretAppearance() {
222+
private PdfFormXObject getCaretAppearance(Color color) {
197223
if(this.caretXObj != null) {
198224
return this.caretXObj;
199225
}
200226
// draw a caret on a 30x30 canvas
201227
this.caretXObj = new PdfFormXObject(new Rectangle(30, 30));
202228
PdfCanvas canvas = new PdfCanvas(this.caretXObj, this.pdfDocument);
203-
canvas.setFillColor(DeviceRgb.BLUE)
229+
canvas.setFillColor(color)
204230
.moveTo(15, 30)
205231
.curveTo(15, 30, 15, 0, 0, 0)
206232
.lineTo(30, 0)
@@ -210,16 +236,15 @@ private PdfFormXObject getCaretAppearance() {
210236
return this.caretXObj;
211237
}
212238

213-
private PdfFormXObject getCommentAppearance() {
239+
private PdfFormXObject getCommentAppearance(Color color) {
214240
if(this.commentXObj != null) {
215241
return this.commentXObj;
216242
}
217243
// draw a speech bubble on a 30x30 canvas
218244
this.commentXObj = new PdfFormXObject(new Rectangle(30, 30));
219245
PdfCanvas canvas = new PdfCanvas(this.commentXObj, this.pdfDocument);
220246

221-
canvas.setFillColorRgb(1, 1, 0)
222-
.setLineWidth(0.85f)
247+
canvas.setFillColor(color).setLineWidth(0.85f)
223248
.moveTo(6, 27.5)
224249
.curveTo(4.3, 27.5, 3, 26.5, 3, 25)
225250
.lineTo(3, 12)
@@ -235,65 +260,79 @@ private PdfFormXObject getCommentAppearance() {
235260
return this.commentXObj;
236261
}
237262

238-
private PdfTextMarkupAnnotation addTextMarkupAnnotationToPdf(PdfName subtype, AnnotObject annotObject) {
263+
private void addTextMarkupAnnotationToPdf(PdfName subtype, AnnotObject annotObject, Color color) {
239264
Rectangle rect = readAnnotRect(annotObject);
240265
float[] quads = readAnnotQuadPoints(annotObject);
241266
PdfTextMarkupAnnotation pdfAnnot = new PdfTextMarkupAnnotation(rect, subtype, quads);
242267

243-
addCommonAnnotationAttributes(pdfAnnot, annotObject);
268+
addCommonAnnotationAttributes(pdfAnnot, annotObject, color);
244269
addMarkupAnnotationAttributes(pdfAnnot, annotObject);
245270
int page = readAnnotPage(annotObject);
246271
pdfDocument.getPage(page).addAnnotation(pdfAnnot);
247272
addPopupAnnotation(page, pdfAnnot, annotObject.getPopup());
248-
return pdfAnnot;
273+
}
274+
275+
private Color getDefaultColor(String annotName) {
276+
switch (annotName) {
277+
case XfdfConstants.TEXT:
278+
case XfdfConstants.HIGHLIGHT:
279+
return DEFAULT_HIGHLIGHT_COLOR;
280+
case XfdfConstants.UNDERLINE:
281+
case XfdfConstants.STRIKEOUT:
282+
case XfdfConstants.SQUIGGLY:
283+
return DeviceRgb.RED;
284+
case XfdfConstants.CARET:
285+
return DeviceRgb.BLUE;
286+
default:
287+
return DeviceRgb.BLACK;
288+
}
249289
}
250290

251291
private void addAnnotationToPdf(AnnotObject annotObject) {
252292
String annotName = annotObject.getName();
253293
int page;
254294
if (annotName != null) {
295+
Color color = getAnnotColor(annotObject, getDefaultColor(annotName));
255296
switch (annotName) {
256297
case XfdfConstants.TEXT:
257298
PdfTextAnnotation pdfTextAnnotation = new PdfTextAnnotation(readAnnotRect(annotObject));
258-
addCommonAnnotationAttributes(pdfTextAnnotation, annotObject);
299+
addCommonAnnotationAttributes(pdfTextAnnotation, annotObject, color);
259300
addMarkupAnnotationAttributes(pdfTextAnnotation, annotObject);
260301

261302
String icon = annotObject.getAttributeValue(XfdfConstants.ICON);
262303
if("Comment".equals(icon)) {
263-
pdfTextAnnotation.setNormalAppearance(this.getCommentAppearance().getPdfObject());
304+
pdfTextAnnotation.setNormalAppearance(this.getCommentAppearance(color).getPdfObject());
264305
}
265306
pdfTextAnnotation.setIconName(new PdfName(icon));
266-
if(annotObject.getAttributeValue(XfdfConstants.STATE) != null) {
267-
pdfTextAnnotation.setState(new PdfString(annotObject.getAttributeValue(XfdfConstants.STATE)));
307+
String stateString = annotObject.getAttributeValue(XfdfConstants.STATE);
308+
if(stateString != null) {
309+
pdfTextAnnotation.setState(new PdfString(stateString));
268310
}
269-
if(annotObject.getAttributeValue(XfdfConstants.STATE_MODEL) != null) {
270-
pdfTextAnnotation.setStateModel(new PdfString(annotObject.getAttributeValue(XfdfConstants.STATE_MODEL)));
311+
String stateModelString = annotObject.getAttributeValue(XfdfConstants.STATE_MODEL);
312+
if(stateModelString != null) {
313+
pdfTextAnnotation.setStateModel(new PdfString(stateModelString));
271314
}
272315

273316
page = readAnnotPage(annotObject);
274317
pdfDocument.getPage(page).addAnnotation(pdfTextAnnotation);
275318
addPopupAnnotation(page, pdfTextAnnotation, annotObject.getPopup());
276319
break;
277320
case XfdfConstants.HIGHLIGHT:
278-
addTextMarkupAnnotationToPdf(PdfName.Highlight, annotObject)
279-
.setColor(new DeviceRgb(1f, 1f, 0));
321+
addTextMarkupAnnotationToPdf(PdfName.Highlight, annotObject, color);
280322
break;
281323
case XfdfConstants.UNDERLINE:
282-
addTextMarkupAnnotationToPdf(PdfName.Underline, annotObject)
283-
.setColor(DeviceRgb.RED);
324+
addTextMarkupAnnotationToPdf(PdfName.Underline, annotObject, color);
284325
break;
285326
case XfdfConstants.STRIKEOUT:
286-
addTextMarkupAnnotationToPdf(PdfName.StrikeOut, annotObject)
287-
.setColor(DeviceRgb.RED);
327+
addTextMarkupAnnotationToPdf(PdfName.StrikeOut, annotObject, color);
288328
break;
289329
case XfdfConstants.SQUIGGLY:
290-
addTextMarkupAnnotationToPdf(PdfName.Squiggly, annotObject)
291-
.setColor(DeviceRgb.RED);
330+
addTextMarkupAnnotationToPdf(PdfName.Squiggly, annotObject, color);
292331
break;
293332
case XfdfConstants.CARET:
294333
PdfCaretAnnotation caretAnnotation = new PdfCaretAnnotation(readAnnotRect(annotObject));
295-
caretAnnotation.setNormalAppearance(this.getCaretAppearance().getPdfObject());
296-
addCommonAnnotationAttributes(caretAnnotation, annotObject);
334+
caretAnnotation.setNormalAppearance(this.getCaretAppearance(color).getPdfObject());
335+
addCommonAnnotationAttributes(caretAnnotation, annotObject, color);
297336
addMarkupAnnotationAttributes(caretAnnotation, annotObject);
298337
page = readAnnotPage(annotObject);
299338
pdfDocument.getPage(page).addAnnotation(caretAnnotation);

0 commit comments

Comments
 (0)