Here is the context:
We add two empty pages to an existing pdf, each containing an empty pushbutton field
We apply a PAdES B-B seal with all modification rights on the document
We modify a pushbutton to insert an image in it
When we try to modify the pushbutton appearance to set an image, the seal validity breaks with "unauthorized modification" no matter what we try.
Here is a code sample:
PdfReader pdfReader = new PdfReader("test.pdf");
PdfStamper pdfStamper = new PdfStamper(pdfReader, output, pdfReader.getPdfVersion(), true);
AcroFields acroFields = pdfStamper.getAcroFields();
String imageFieldId = "imageField1";
acroFields.setField(imageFieldId, Base64.encodeBytes(consentImage));
pdfStamper.close();
pdfReader.close();
We also tried with the recommanded way in documentation without success:
PushbuttonField pbField = acroFields.getNewPushbuttonFromField(imageFieldId);
pbField.setImage(Image.getInstance("image1.jpg"));
acroFields.replacePushbuttonField(imageFieldId, pbField.getField());
Problem is: i don't know if that type of modification is supported by iText or if it's our way of modifying the button which is wrong?
Update:
If the certification is replaced by a simple signature, we can set the pushbutton appearance without breaking it.
Why the certification signature is broken
You say
We apply a PAdES B-B seal with all modification rights on the document
which does not mean that all imaginable modifications of the document are allowed but instead that all allowable modifications are allowed. According to the PDF specification the choices are:
No changes to the document shall be permitted; any change to the document shall invalidate the signature.
Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature.
Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
Thus, in case of your document the allowed changes include form fill-ins and arbitrary annotation manipulation.
Unfortunately iText 5, when setting "the value" of an AcroForm push button, does not merely set the button appearance to the button but instead
PushbuttonField pb = getNewPushbuttonFromField(name);
pb.setImage(img);
replacePushbuttonField(name, pb.getField());
I.e. it essentially replaces the former push button with a similar one. This as such is not allowed.
Why a mere approval signature is not broken
The PDF specification does not restrict the changes allowed to a document signed by a mere approval signature (unless restrictions explicitly are given in a FieldMDP transform).
Adobe once claimed that they do restrict changes allowed to signed but not certified documents like those to a certified document with restriction value 3 plus "Adding signature fields", cf. this answer, but apparently they are a bit laxer in other respects, too. In particular current Adobe Reader versions only warn about "Form Fields with Property Changes" in the case at hand.
An additional complication
The PDF in question actually does not have only the AcroForm form definition, instead it has a similar XFA form definition, it is a hybrid form document. Thus, to change the image in both form definitions, one has to consider the filling of the XFA form, too.
Fortunately, the way iText 5 fills in the image into the XFA form does not make Adobe Reader assume the seal broken.
How to set the button image instead to not break the seal
To not break the seal, we have to set the button image without changing the underlying form, merely the widget. Thus, the following code attempts to only change the appearance of the button:
PdfReader pdfReader = new PdfReader(SOURCE);
PdfStamper pdfStamper = new PdfStamper(pdfReader, TARGET, pdfReader.getPdfVersion(), true);
byte[] bytes = IMAGE_BYTES;
AcroFields acroFields = pdfStamper.getAcroFields();
String name = "mainform[0].subform_0[0].image_0_0[0]";
String value = Base64.getEncoder().encodeToString(bytes);
Image image = Image.getInstance(bytes);
XfaForm xfa = acroFields.getXfa();
if (xfa.isXfaPresent()) {
name = xfa.findFieldName(name, acroFields);
if (name != null) {
String shortName = XfaForm.Xml2Som.getShortName(name);
Node xn = xfa.findDatasetsNode(shortName);
if (xn == null) {
xn = xfa.getDatasetsSom().insertNode(xfa.getDatasetsNode(), shortName);
}
xfa.setNodeText(xn, value);
}
}
PdfDictionary widget = acroFields.getFieldItem(name).getWidget(0);
PdfArray boxArray = widget.getAsArray(PdfName.RECT);
Rectangle box = new Rectangle(boxArray.getAsNumber(0).floatValue(), boxArray.getAsNumber(1).floatValue(), boxArray.getAsNumber(2).floatValue(), boxArray.getAsNumber(3).floatValue());
float ratioImage = image.getWidth() / image.getHeight();
float ratioBox = box.getWidth() / box.getHeight();
boolean fillHorizontally = ratioImage > ratioBox;
float width = fillHorizontally ? 1 : ratioBox / ratioImage;
float height = fillHorizontally ? ratioImage / ratioBox : 1;
float xOffset = 0; // centered: (width - 1) / 2;
float yOffset = height - 1; // centered: (height - 1) / 2;
PdfAppearance app = PdfAppearance.createAppearance(pdfStamper.getWriter(), width, height);
app.addImage(image, 1, 0, 0, 1, xOffset, yOffset);
PdfDictionary dic = (PdfDictionary)widget.get(PdfName.AP);
if (dic == null)
dic = new PdfDictionary();
dic.put(PdfAnnotation.APPEARANCE_NORMAL, app.getIndirectReference());
widget.put(PdfName.AP, dic);
pdfStamper.markUsed(widget);
pdfStamper.close();
pdfReader.close();
(SetImageInSignedPdf test testSetInXfaAndAppearanceSampleCert)
In my tests this results in the image being visible both in viewers that support XFA forms and those that don't, and the seal not being considered broken by Adobe Reader.
Beware, though, I only developed and tested this with your sample document; chances are that some border conditions might not be considered.
Related
the following szenario is given: party 1 signs pdf, party 2 fills out AcroForm field and then also signs the pdf.The problem is, after the second signature, the form field value only appears if I click in the field. Both the form filling process and the signature process happen in append mode.signing pdf code:
using (PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0', tempFolder, true))
{
// Creating the signature
SigPadSignature sig = new SigPadSignature(sigInfo, DigestAlgorithms.SHA256);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = sigInfo.Reason;
appearance.Location = sigInfo.Location;
appearance.Contact = sigInfo.ContactInfo;
appearance.Certificate = chain[0];
appearance.SignatureCreator = sigInfo.SignatureCreator;
appearance.SetVisibleSignature(area.CreateRectangle(), area.PageNumber, fieldName);
appearance.Layer2Text = string.Format("\n{0}\n{1}, {2:g}", sigInfo.SignerName, sigInfo.Location, DateTime.Now);
appearance.Layer2Font = new Font(Font.FontFamily.COURIER, 7.0f);
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
appearance.SignatureGraphic = Image.GetInstance(sigInfo.SignatureImage, System.Drawing.Imaging.ImageFormat.Bmp);
appearance.SignatureEvent = sig;
MakeSignature.SignDetached(appearance, sig, chain, /*crlList*/null, /*ocspClient*/null, /*tsaClient*/null, estimatedSize, CryptoStandard.CMS);
}
fill form field code:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '\0', true))
{
stamper.AcroFields.SetField("fieldname", "test1234");
}
Here is a example PDF. If you open it in AdobeReader you can see the szenario described. The form field value only appears when clicking the field.
What am I doing wrong? Any suggestions?
Greetings
In short
You actually discovered a bug in iTextSharp v5.x (also in iText v5.x and OpenPdf). When working in append mode in this library, one must mark each indirect object from the original file one manipulated as 'used' but iText(Sharp) forgets to do so for one object in the context of changing the field appearance.
This object needs not be indirect, actually it more often is direct or absent than indirect; thus, this bug does not have a visible effect very often.
You can work around this by removing that object in its previous form before setting setting the field value:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '\0', true))
{
stamper.AcroFields.GetFieldItem("AcroFormField_0").GetWidget(0).Remove(PdfName.AP);
stamper.AcroFields.SetField("AcroFormField_0", "test1234");
}
(Actually some null checks wouldn't hurt here either...)
In detail
When setting the field value, eventually AcroFields.SetField(String name, String value, String display, bool saveAppearance) is called. In case of a text field, the new appearance of that field is updated in that method by this code:
PdfAppearance app = GetAppearance(merged, display, name);
...
PdfDictionary appDic = widget.GetAsDict(PdfName.AP);
if (appDic == null) {
appDic = new PdfDictionary();
widget.Put(PdfName.AP, appDic);
merged.Put(PdfName.AP, appDic);
}
appDic.Put(PdfName.N, app.IndirectReference);
writer.ReleaseTemplate(app);
If there was no appearance dictionary before, a new one is created as direct object and everything works as desired. If there was an appearance dictionary as direct object, it's just the same as the widget is marked used further down. But if there was an appearance dictionary appDict as indirect object, this dictionary is not marked used and, therefore, the change (appDic.Put(PdfName.N, app.IndirectReference)) is not eventually saved, i.e. the new appearance is not associated with the field widget.
A fix would be to mark a previously existing appearance dictionary as used, e.g. like this:
if (appDic == null) {
...
} else {
MarkUsed(appDic);
}
By removing it first as proposed above one makes iText create a new appearance dictionary as direct object, so the bug is circumvented.
And what about the signatures...?
It looks like Adobe Reader recognizes that the previous processor forgot to update the appearance and so does that itself under the hood in case of the PDF in which as last action the field value was set.
After adding another signature to that PDF, though, Adobe Reader apparently does not want to do this under the hood anymore. This would after all change what the signer had seen when signing, and Adobe tries to prevent such situations in which its software could be blamed for changing previously signed data...
This is the code I use to draw a line.
double[] lineArray = annotation.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
cs.moveTo(x1, y1);
cs.lineTo(x2, y2);
Where cs is PdfAppearance, annotation is PdfAnnotation and rect is
PdfArray rect = annotation.getAsArray(PdfName.RECT);
This works ok in portrait. but come, landscape mode e.g. 270 rotation, the coordinates get misplaced. I also did a rotate via cs.transform() so my 0,0 would be rotated but it does nothing.
Any idea what could be lacking?
The source
This answer covers the updated source code provided by the OP via a google drive link in comments:
public static void main(String[] args) throws Exception {
PdfReader reader = new PdfReader("src");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("dest"));
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
stamper.close();
reader.close();
}
private static void addAppearance(PdfStamper stamper, PdfAnnotation stamp, Rectangle location) {
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(location.getWidth(), location.getHeight());
PdfArray rect = stamp.getAsArray(PdfName.RECT);
Rectangle bbox = app.getBoundingBox();
double[] lineArray = stamp.getAsArray(PdfName.L).asDoubleArray();
double x1 = lineArray[0] - rect.getAsNumber(0).doubleValue();
double y1 = lineArray[1] - rect.getAsNumber(1).doubleValue();
double x2 = lineArray[2] - rect.getAsNumber(0).doubleValue();
double y2 = lineArray[3] - rect.getAsNumber(1).doubleValue();
app.moveTo(x1, y1);
app.lineTo(x2, y2);
app.stroke();
stamp.setAppearance(PdfName.N, app);
}
No appearance
The first observation when viewing the resulting PDF in Chrome is, as the OP put it in a comment:
nothing shows up
Inspecting the PDF the cause is clear: The annotation has no appearance stream. Thus, limited PDF viewers which only can show annotations by their appearance stream, not by their descriptive values, like the integrated viewer in Chrome don't show it.
This is due to the order in which the OP calls iText functionalities in his code:
[... create annotation object stamp ...]
stamper.addAnnotation(stamp, 1);
addAppearance(stamper, stamp, location);
So he first adds the annotation to the PDF by means of stamper.addAnnotation and thereafter creates an appearance and attaches it to the stamp object.
This order is wrong. In context with iText one has to be aware that the library attempts to write additions as early as possible to the output stream to reduce its memory footprint. (This by the way is one of the important features of iText in the context of server applications in which multiple PDFs may have to be processed in parallel.)
So already during stamper.addAnnotation(stamp, 1) the annotation is written to the output stream, and as it has no appearance yet, the annotation in the output stream is without appearance. The later addAppearance call only adds an appearance to the in-memory representation of the annotation which won't be serialized anymore.
Changing the order to
[... create annotation object stamp ...]
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
results in a PDF with a line drawn. Unfortunately not at the desired position, but that is another problem.
Wrong position
The reason why the line is both in the wrong location and has the wrong direction, is based in a feature of iText which has already been a topic in this answer and in this answer:
For rotated pages iText attempts to lift the burden of adding the rotation and translation to page content required to draw upright text and have the coordinate system origin in the lower left of the page of the users' shoulders, so that the users don't have to deal with page rotation at all. Consequently, it also does so for annotations.
As you already have the actual coordinates to use, this "help" by iText damages your annotation. As discussed in those other answers, there unfortunately is no explicit switch to turn off that mechanism; there is an easy work-around, though: before your manipulation simply remove the page rotation entry, and afterwards add it back again:
PdfReader reader = ...;
PdfStamper stamper = ...;
// hide the page rotation
PdfDictionary pageDict = reader.getPageN(1);
PdfNumber rotation = pageDict.getAsNumber(PdfName.ROTATE);
pageDict.remove(PdfName.ROTATE);
Rectangle location = new Rectangle(544.8f, 517.65f, 663f, 373.35f);
PdfArray lineEndings = new PdfArray();
lineEndings.add(new PdfName("None"));
lineEndings.add(new PdfName("None"));
PdfAnnotation stamp = PdfAnnotation.createLine(stamper.getWriter(), location,
"comment", 550.05f, 510.9f, 656.25f, 378.6f);
stamp.put(new PdfName("LE"), lineEndings);
stamp.put(new PdfName("IT"), new PdfName("Line"));
stamp.setBorderStyle(new PdfBorderDictionary(1, PdfBorderDictionary.STYLE_SOLID));
stamp.setColor(PdfGraphics2D.prepareColor(Color.RED));
stamp.put(PdfName.ROTATE, new PdfNumber(270));
addAppearance(stamper, stamp, location);
stamper.addAnnotation(stamp, 1);
// add page rotation again if required
if (rotation != null)
pageDict.put(PdfName.ROTATE, rotation);
stamper.close();
reader.close();
This appears to create the annotation appearance as required.
I am using pdfbox to add a line to pdf file. but the text i am adding is reversed.
File file = new File(filePath);
PDDocument document = PDDocument.load(file);
PDPage page = document.getPage(0);
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true);
int stampFontSize = grailsApplication.config.pdfStamp.stampFontSize ? grailsApplication.config.pdfStamp.stampFontSize : 20
contentStream.beginText();
contentStream.setFont(PDType1Font.TIMES_ROMAN, stampFontSize);
int leftOffset = grailsApplication.config.pdfStamp.leftOffset ? grailsApplication.config.pdfStamp.leftOffset : 10
int bottomOffset = grailsApplication.config.pdfStamp.bottomOffset ? grailsApplication.config.pdfStamp.bottomOffset : 20
contentStream.moveTextPositionByAmount(grailsApplication.config.xMove,grailsApplication.config.yMove)
contentStream.newLineAtOffset(leftOffset, bottomOffset)
String text = "i have added this line...!!!!";
contentStream.showText(text);
contentStream.endText();
contentStream.close();
document.save(new File(filePath));
document.close();
byte[] pdfData;
pdfData = Files.readAllBytes(file.toPath());
return pdfData;
i tried using moveTextPositionByAmount method but this does not seem to have any effect on text. why is my text reversed and how can i set it to correct orientation.
Your code is not causing the mirrored output by itself, so the cause must be inside the PDF you are stamping. Unfortunately you did not provide the PDF in question, so we have to guess here.
Most likely the issue is caused by the pre-existing page content having set the current transformation matrix to a mirroring affine transformation without resetting it at the end.
If that indeed is the case, PDFBox provides an easy work-around:
You construct your PDPageContentStream like this:
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true);
There is another constructor accepting an additional boolean argument. If you use that constructor setting the additional argument to true, PDFBox attempts to reset the graphics state of the content:
PDPageContentStream contentStream = new PDPageContentStream(document, page,PDPageContentStream.AppendMode.APPEND,true,true);
Beware: If this indeed fixes the issue, the coordinates and offsets you currently use rely on the transformation matrix being changed as it is. In that case you will have to update them accordingly.
Alternatively introducing a counter-mirroring may help, e.g. by setting the text matrix like this at the start of each of your text objects:
contentStream.beginText();
contentStream.setTextMatrix(new Matrix(1f, 0f, 0f, -1f, 0f, 0f));
Thereafter all y coordinate changes need to be negated, in particular the second argument of contentStream.moveTextPositionByAmount and contentStream.newLineAtOffset.
(By the way, moveTextPositionByAmount and newLineAtOffset do the same, the former merely is the deprecated variant, so you might want to use the latter in both cases.)
My web application signs PDF documents. I would like to let users download the original PDF document (not signed) but adding an image and the signers in the left margin of the pdf document.
I've seen this idea in another web application, and I would like to do the same. Of course I would like to do it using itext library.
I have attached two images, the original PDF document (not signed) and the modified PDF document.
First this: it is important to change the document before you digitally sign it. Once digitally signed, these changes will break the signature.
I will break up the question in two parts and I'll skip the part about the actual watermarking as this is already explained here: How to watermark PDFs using text or images?
This question is not a duplicate of that question, because of the extra requirement to add an extra margin to the right.
Take a look at the primes.pdf document. This is the source file we are going to use in the AddExtraMargin example with the following result: primes_extra_margin.pdf. As you can see, a half an inch margin was added to the left of each page.
This is how it's done:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
// properties
PdfContentByte over;
PdfDictionary pageDict;
PdfArray mediabox;
float llx, lly, ury;
// loop over every page
for (int i = 1; i <= n; i++) {
pageDict = reader.getPageN(i);
mediabox = pageDict.getAsArray(PdfName.MEDIABOX);
llx = mediabox.getAsNumber(0).floatValue();
lly = mediabox.getAsNumber(1).floatValue();
ury = mediabox.getAsNumber(3).floatValue();
mediabox.set(0, new PdfNumber(llx - 36));
over = stamper.getOverContent(i);
over.saveState();
over.setColorFill(new GrayColor(0.5f));
over.rectangle(llx - 36, lly, 36, ury - llx);
over.fill();
over.restoreState();
}
stamper.close();
reader.close();
}
The PdfDictionary we get with the getPageN() method is called the page dictionary. It has plenty of information about a specific page in the PDF. We are only looking at one entry: the /MediaBox. This is only a proof of concept. If you want to write a more robust application, you should also look at the /CropBox and the /Rotate entry. Incidentally, I know that these entries don't exist in primes.pdf, so I am omitting them here.
The media box of a page is an array with four values that represent a rectangle defined by the coordinates of its lower-left and upper-right corner (usually, I refer to them as llx, lly, urx and ury).
In my code sample, I change the value of llx by subtracting 36 user units. If you compare the page size of both PDFs, you'll see that we've added half an inch.
We also use these coordinates to draw a rectangle that covers the extra half inch. Now switch to the other watermark examples to find out how to add text or other content to each page.
Update:
if you need to scale down the existing pages, please read Fix the orientation of a PDF in order to scale it
Our company using iText to stamp some watermark text (not image) on some pdf forms. I noticed 95% forms shows watermark correctly, about 5% does not. I tested, copy 2 original pdf files, one was marked ok, other one does not ok, then tested in via a small program, same result: one got marked, the other does not. I then tried the latest version of iText jar file (version 5.0.6), same thing. I checked pdf file properties, security settings etc, seems nothing shows any hint. The result file does changed size and markd "changed by iText version...." after executed program.
Here is the sample watermark code (using itext jar version 2.1.7), note topText, mainText, bottonText parameters passed in, make 3 lines of watermarks show in the pdf as watermark.
Any help appreciated !!
public class WatermarkGenerator {
private static int TEXT_TILT_ANGLE = 25;
private static Color MEDIUM_GRAY = new Color(160, 160, 160);
private static int SUPPORT_FONT_SIZE = 42;
private static int PRIMARY_FONT_SIZE = 54;
public static void addWaterMark(InputStream pdfInputStream,
OutputStream outputStream, String topText,
String mainText, String bottomText) throws Exception {
PdfReader reader = new PdfReader(pdfInputStream);
int numPages = reader.getNumberOfPages();
// Create a stamper that will copy the document to the output
// stream.
PdfStamper stamp = new PdfStamper(reader, outputStream);
int page=1;
BaseFont baseFont =
BaseFont.createFont(BaseFont.HELVETICA_BOLDOBLIQUE,
BaseFont.WINANSI, BaseFont.EMBEDDED);
float width;
float height;
while (page <= numPages) {
PdfContentByte cb = stamp.getOverContent(page);
height = reader.getPageSizeWithRotation(page).getHeight() / 2;
width = reader.getPageSizeWithRotation(page).getWidth() / 2;
cb = stamp.getUnderContent(page);
cb.saveState();
cb.setColorFill(MEDIUM_GRAY);
// Top Text
cb.beginText();
cb.setFontAndSize(baseFont, SUPPORT_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, topText, width,
height+PRIMARY_FONT_SIZE+16, TEXT_TILT_ANGLE);
cb.endText();
// Primary Text
cb.beginText();
cb.setFontAndSize(baseFont, PRIMARY_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, mainText, width,
height, TEXT_TILT_ANGLE);
cb.endText();
// Bottom Text
cb.beginText();
cb.setFontAndSize(baseFont, SUPPORT_FONT_SIZE);
cb.showTextAligned(Element.ALIGN_CENTER, bottomText, width,
height-PRIMARY_FONT_SIZE-6, TEXT_TILT_ANGLE);
cb.endText();
cb.restoreState();
page++;
}
stamp.close();
}
}
We solved problem by change Adobe LifecycleSave file option. File->Save->properties->Save as, then look at Save as type, default is Acrobat 7.0.5 Dynamic PDF Form File, we changed to use 7.0.5 Static PDF Form File (actually any static one will work). File saved in static one do not have this watermark disappear problem. Thanks Mark for pointing to the right direction.
You're using the underContent rather than the overContent. Don't do that. It leaves you at the mercy of big, white-filled rectangles that some folks insist on drawing first thing. It's a hold over from less-than-good PostScript interpreters and hasn't been necessary for Many Years.
Okay, having viewed your PDF, I can see the problem is that this is an XFA-based form (from LiveCycle Designer). Acrobat can (and often does) rebuild the entire file based on the XFA (a type of xml) it contains. That's how your changes are lost. When Acrobat rebuilds the PDF from the XFA, all the existing PDF information is pitched, including your watermark.
The only way to get this to work would be to define the watermark as part of the XFA file contained in the PDF.
Detecting these forms isn't all that hard:
PdfReader reader = new PdfReader(...);
AcroFields acFields = reader.getAcroFields();
XfaForm xfaForm = acFields.getXfaForm();
if (xfaForm != null && xfaForm.isXfaPresent()) {
// Ohs nose.
throw new ItsATrapException("We can't repel XML of that magnitude!");
}
Modifying them on the other hand could be Quite Challenging, but here's the specs.
Once you've figured out what needs to be changed, it's a simple matter of XML manipulation... but that "figure it out" part could be interesting.
Good hunting.