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.)
Related
I'm using iText 7 to construct reusable PDF components that I reuse across multiple pages within a document. I'm using iText-dotnet for this task (v7), using F# as the language. (This shouldn't be hard to follow for non-F# people as it's just iText calls :D)
I know how to add annotations to a Page, that isn't the issue. Adding the annotation to the page is as simple as page.AddAnnotation(newAnnotation).
Where I'm having difficulty, is that there is no "Page" associated with a Canvas when you are using a PdfFormXObject() to render a Pdf fragment.
let template = new PdfFormXObject(rect)
let templateCanvas = PdfCanvas(template, pageContext.Canvas.GetPdfDocument())
let newCanvas = new Canvas(templateCanvas, rect)
Once I have the new Canvas, I try to write to the Canvas and add the Annotation via Page.AddAnnotation(). The problem is that there is no Page attached to the PdfFormXObject!
// Create the destination and annotation (destPage is the pageNumber)
let dest = PdfExplicitDestination.CreateFitB(destPage)
let action = PdfAction.CreateGoTo(dest)
let annotation = PdfLinkAnnotation(rect)
let border = iText.Kernel.Pdf.PdfAnnotationBorder(0f, 0f, 0f)
// set up the Annotation with action and display information
annotation
.SetHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH)
.SetAction(action)
.SetBorder(border)
|> ignore
// Try adding the annotation to the page BOOM! (There is *NO* page (null) associated with newCanvas)
newCanvas.GetPage().AddAnnotation(annotation) |> ignore // HELP HERE: Is there another way to do this?
The issue is that I do not know of a different way to set the Annotation on the canvas. Is there a way to render the annotation and just add the annotation directly to the canvas as raw PDF instructions?
Alternatively, is there a way create a different reusable PDF fragment in iText so I can also reuse the GoTo annotation.
N.B. I could split off the annotations and then apply them every time I use the PdfFormXObject() on a new page, but that sort of defeats the purpose of reusing Pdf fragments (template) in my final PDF to reduce it's size.
If you can point me in the right direction, that would be great.
Again, this is not how to add an annotation to a Page(), that's easy. It's how to add an annotation to a PdfFormXObject (or similar mechanism that I'm unaware of for constructing rusable Pdf fragments).
-- As per John's comments below:
I cannot seem to find any reference to single use annotations.
I'm aware of the following example link, so I modified it to look like this:
private static void Main(string[] args)
{
try
{
PdfDocument pdfDocument = new PdfDocument(new PdfWriter("TestMultiLink.pdf"));
Document document = new Document(pdfDocument);
string destinationName = "MyForwardDestination";
// Create a PdfStringDestination to use more than once.
var stringDestination = new PdfStringDestination(destinationName);
for (int page = 1; page <= 50; page++)
{
document.Add(new Paragraph().SetFontSize(100).Add($"{page}"));
switch (page)
{
case 1: // First use of PdfStringDestination
document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(20)));
break;
case 3: // Re-use the stringDestination
document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(10)));
break;
case 42:
pdfDocument.AddNamedDestination(destinationName, PdfExplicitDestination.CreateFit(pdfDocument.GetLastPage()).GetPdfObject());
break;
}
if (page < 50)
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
document.Close();
}
catch (Exception e)
{
Console.WriteLine($"Ouch: {e.Message}");
}
}
If you dig into the iText source for iText.Layout.Link, you'll see that the String Destination is added as an Annotation. Therefore, I'm not sure if John's answer is true anymore.
Does anyone know how I can convert the Annotation to a Dictionary and how I would go about adding the PdfDictionary (raw) info into the PftFormXObject?
Thanks
#johnwhitington is correct.
Per PDF specification, annotations can only be added to a page, they cannot be added to a form XObject. It is not a limitation of iText or any other PDF library.
Annotations cannot be reused, each annotation is a distinct object.
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.
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 able to insert an Image inside an existing pdf document, but the problem is,
The image is placed at the bottom of the page
The page becomes white with the newly added text showing on it.
I am using following code.
List<PDPage> pages = pdDoc.getDocumentCatalog().getAllPages();
if(pages.size() > 0){
PDJpeg img = new PDJpeg(pdDoc, in);
PDPageContentStream stream = new PDPageContentStream(pdDoc,pages.get(0));
stream.drawImage(img, 60, 60);
stream.close();
}
I want the image on the first page.
PDFBox is a low-level library to work with PDF files. You are responsible for more high-level features. So in this example, you are placing your image at (60, 60) starting from lower-left corner of your document. That is what stream.drawImage(img, 60, 60); does.
If you want to move your image somewhere else, you have to calculate and provide the wanted location (perhaps from dimensions obtained with page.findCropBox(), or manually input your location).
As for the text, PDF document elements are absolutely positioned. There are no low-level capabilities for re-flowing text, floating or something similar. If you write your text on top of your image, it will be written on top of your image.
Finally, for your page becoming white -- you are creating a new content stream and so overwriting the original one for your page. You should be appending to the already available stream.
The relevant line is:
PDPageContentStream stream = new PDPageContentStream( pdDoc, pages.get(0));
What you should do is call it like this:
PDPageContentStream stream = new PDPageContentStream( pdDoc, pages.get(0), true, true);
The first true is whether to append content, and the final true (not critical here) is whether to compress the stream.
Take a look at AddImageToPDF sample available from PDFBox sources.
Try this
doc = PDDocument.load( inputFileName );
PDXObjectImage ximage = null;
ximage = new PDJpeg(doc, new FileInputStream( image )
PDPage page = (PDPage)doc.getDocumentCatalog().getAllPages().get(0);
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true);
contentStream.drawImage( ximage, 425, 675 );
contentStream.close();
This prints the image in first page. If u want to print in all pages just put on a for loop with a condition of number of pages as the limit.
This worked for me well!
So late answer but this is for who works on it in 2020 with Kotlin: drawImage() is getting float values inside itself so try this:
val file = File(getPdfFile(FILE_NAME))
val document = PDDocument.load(file)
val page = document.getPage(0)
val contentStream: PDPageContentStream
contentStream = PDPageContentStream(document, page, true, true)
// Define a content stream for adding to the PDF
val bitmap: Bitmap? = ImageSaver(this).setFileName("sign.png").setDirectoryName("signature").load()
val mediaBox: PDRectangle = page.mediaBox
val ximage: PDImageXObject = JPEGFactory.createFromImage(document, bitmap)
contentStream.drawImage(ximage, mediaBox.width - 4 * 65, 26f)
// Make sure that the content stream is closed:
contentStream.close()
// Save the final pdf document to a file
pdfSaveLocation = "$directoryPDF/$UPDATED_FILE_NAME"
val pathSave = pdfSaveLocation
document.save(pathSave)
document.close()
I am creating a new PDF and running below code in a loop - to add one image per page and below co-ordinates and height and width values work well for me.
where out is BufferedImage reference variable
PDPage page = new PDPage();
outputdocument.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(outputdocument, page, AppendMode.APPEND, true);
PDImageXObject pdImageXObject = JPEGFactory.createFromImage(outputdocument, out);
contentStream.drawImage(pdImageXObject, 5, 2, 600, 750);
contentStream.close();
This link gives you details about Class PrintImageLocations.
This PrintImageLocations will give you the x and y coordinates of the images.
Usage: java org.apache.pdfbox.examples.util.PrintImageLocations input-pdf
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.