How to add overlay text with link annotations to existing pdf? - java

I would like to add a link in my overlay text. I've read that using Anchor will only work for documents made from scratch but not for existing pdfs. My code is adding an overlay text to every page. My goal is to make a portion of that text clickable. I don't know how to make a link annotation that is part of a phrase.
Here's my code:
int n = reader.getNumberOfPages();
// step 4: we add content
PdfImportedPage page;
PdfCopy.PageStamp stamp;
for (int j = 0; j < n; )
{
++j;
page = writer.getImportedPage(reader, j);
if (i == 1) {
stamp = writer.createPageStamp(page);
Rectangle mediabox = reader.getPageSize(j);
Rectangle crop = new Rectangle(mediabox);
writer.setCropBoxSize(crop);
// add overlay text
Paragraph p = new Paragraph();
p.setAlignment(Element.ALIGN_CENTER);
FONT_URL_OVERLAY.setColor(0, 191, 255);
// get current user
EPerson loggedin = context.getCurrentUser();
String eperson = null;
if (loggedin != null)
{
eperson = loggedin.getFullName();
}
else eperson = "Anonymous";
Phrase downloaded = new Phrase();
Chunk site = new Chunk("My Website",FONT_URL_OVERLAY);
site.setAction(new PdfAction("http://www.mywebsite.com"));
downloaded.add(new Chunk("Downloaded by [" + eperson + "] from ", FONT_OVERLAY));
downloaded.add(site);
downloaded.add(new Chunk(" on ", FONT_OVERLAY));
downloaded.add(new Chunk(new SimpleDateFormat("MMMM d, yyyy").format(new Date()), FONT_OVERLAY));
downloaded.add(new Chunk(" at ", FONT_OVERLAY));
downloaded.add(new Chunk(new SimpleDateFormat("h:mm a z").format(new Date()), FONT_OVERLAY));
p.add(downloaded);
ColumnText.showTextAligned(stamp.getOverContent(), Element.ALIGN_CENTER, p,
crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom(), 90);
stamp.alterContents();
}
writer.addPage(page);
}
So my overlay would looked like this:
Downloaded by [Anonymous] from My Website on February 17, 2015 at 1:20 AM CST
How can I convert My Website to a link annotation? Searching here in SO, I found this post, but I don't know how to apply adding link annotation to a portion of my overlay text.
Thanks in advance.
EDIT: How to add a rotated overlay text with link annotations to existing pdf?
Thanks to Bruno Lowagie for going out of his way in answering my question. Although I originally asked how to add link annotations in an overlay text to existing pdfs, he also catered my questions in the comments section of his answer about setting the coordinates properly if the overlay text were rotated.

You are using ColumnText.showAligned() which is sufficient to add a line of text without any special features, but if you want the anchor to work, you need to use ColumnText differently.
This is shown in the AddLinkAnnotation2 example:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
PdfContentByte canvas = stamper.getOverContent(1);
Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
chunk.setAnchor("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
Phrase p = new Phrase("Download ");
p.add(chunk);
p.add(" and discover more than 200 questions and answers.");
ColumnText ct = new ColumnText(canvas);
ct.setSimpleColumn(36, 700, 559, 750);
ct.addText(p);
ct.go();
stamper.close();
reader.close();
}
In this case, we define a rectangle for a ColumnText object, we add the Phrase to the column, and we go().
If you check the result, link_annotation2.pdf, you'll notice that you can click the words in bold.
There are no plans to support this in ColumnText.showTextAligned(). That is a convenience method that can be used as a short-cut for the handful of lines shown above, but there are some known limitations: lines are not wrapped, interactivity is ignored,...
Update 1: in the comment section, you asked an additional question about rotation the content and the link.
Rotating the content isn't difficult. There's even more than one way to do that. Rotating the link isn't trivial, as a link is a type of annotation, and annotations aren't part of the content.
Let's first take a look at AddLinkAnnotation3:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 6);
stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
PdfContentByte canvas = stamper.getOverContent(1);
Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
Phrase p = new Phrase("Download ");
p.add(chunk);
p.add(" and discover more than 200 questions and answers.");
canvas.saveState();
canvas.concatCTM(transform);
ColumnText ct = new ColumnText(canvas);
ct.setSimpleColumn(300, 0, 800, 400);
ct.addText(p);
ct.go();
canvas.restoreState();
stamper.close();
reader.close();
}
In this example, we define a tranformation of 30 degrees (Math.PI / 6):
AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 6);
We use this transformation when rendering the column:
canvas.saveState();
canvas.concatCTM(transform);
// render column
canvas.restoreState();
This rotates the content, but we didn't add any annotation yet. Instead, we define a page event:
stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
and we introduced a generic tag:
chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
To add the annotation, we use some magic in the page event implementation:
public class AddAnnotation extends PdfPageEventHelper {
protected PdfStamper stamper;
protected AffineTransform transform;
public AddAnnotation(PdfStamper stamper, AffineTransform transform) {
this.stamper = stamper;
this.transform = transform;
}
#Override
public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) {
float[] pts = {rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()};
transform.transform(pts, 0, pts, 0, 2);
float[] dstPts = {pts[0], pts[1], pts[2], pts[3]};
rect = new Rectangle(dstPts[0], dstPts[1], dstPts[2], dstPts[3]);
PdfAnnotation annot = PdfAnnotation.createLink(writer, rect, PdfAnnotation.HIGHLIGHT_INVERT, new PdfAction(text));
stamper.addAnnotation(annot, 1);
}
}
We create an annotation, but before we do so, we perform a transformation on the rectangle. This makes sure that the text fits the rectangle with the text that needs to be clickable, but... this may not be what you expect:
You may have wanted the rectangle to be rotated, and that's possible, but it's more math. For instance: you could create a polygon that is a better fit: ITextShape Clickable Polygon or path
Fortunately, you don't need an angle of 30 degrees, you want to rotate the text with an angle of 90 degrees. In that case, you don't have the strange effect shown in the above screen shot.
Take a look at AddLinkAnnotation4
public class AddAnnotation extends PdfPageEventHelper {
protected PdfStamper stamper;
protected AffineTransform transform;
public AddAnnotation(PdfStamper stamper, AffineTransform transform) {
this.stamper = stamper;
this.transform = transform;
}
#Override
public void onGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) {
float[] pts = {rect.getLeft(), rect.getBottom(), rect.getRight(), rect.getTop()};
transform.transform(pts, 0, pts, 0, 2);
float[] dstPts = {pts[0], pts[1], pts[2], pts[3]};
rect = new Rectangle(dstPts[0], dstPts[1], dstPts[2], dstPts[3]);
PdfAnnotation annot = PdfAnnotation.createLink(writer, rect, PdfAnnotation.HIGHLIGHT_INVERT, new PdfAction(text));
annot.setBorder(new PdfBorderArray(0, 0, 0));
stamper.addAnnotation(annot, 1);
}
}
As you can see, I've added a single line to remove the border (the border is there by default unless you redefine the PdfBorderArray).
The rest of the code is also almost identical. We now define an angle of Math.PI / 2 (90 degrees).
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AffineTransform transform = AffineTransform.getRotateInstance(Math.PI / 2);
stamper.getWriter().setPageEvent(new AddAnnotation(stamper, transform));
PdfContentByte canvas = stamper.getOverContent(1);
Font bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
chunk.setGenericTag("http://pages.itextpdf.com/ebook-stackoverflow-questions.html");
Phrase p = new Phrase("Download ");
p.add(chunk);
p.add(" and discover more than 200 questions and answers.");
canvas.saveState();
canvas.concatCTM(transform);
ColumnText ct = new ColumnText(canvas);
ct.setSimpleColumn(36, -559, 806, -36);
ct.addText(p);
ct.go();
canvas.restoreState();
stamper.close();
reader.close();
}
Note that the lower left corner of the page is the pivot point, hence we need to adapt the coordinates where we add the column, otherwise you'll rotate all the content outside the visible area of the page.
Update 2:
In yet another comment, you are asking about the coordinates you need to use when adding text in a rotated coordinate system.
I made this drawing:
In the top part, you add the word MIDDLE in the middle of a page, but that's not where it will appear: you are rotating everything by 90 degrees, hence the word MIDDLE will rotate outside your page (into the hatched area). The word will be in the PDF, but you'll never see it.
If you look at my code, you see that I use these coordinates:
ct.setSimpleColumn(36, -559, 806, -36);
This is outside the visible area (it's below the actual page dimensions), but as I rotate everything with 90 degrees, it rotates into the visible area.
If you look at my drawing, you can see that the page with coordinates (0, 0), (0, -595), (842, -598) and (842, 0) rotates by 90 degrees and thus gets the coincides with a page with coordinates (0, 0), (595, 0), (595, 842) and (0, 842). That's the type of Math we all learned in high school ;-)
You were adding text at position crop.getLeft(10), crop.getHeight() / 2 + crop.getBottom(). If you know that the text will be rotated by 90 degrees, you should use crop.getHeight() / 2 + crop.getBottom(), -crop.getLeft().
The best way to understand why, is to make a drawing.

Related

How to rotate watermark (text) in PDF using iText?

I'm using iText for stamping a watermark (text: "SuperEasy You Done") on PDF files as described in How to watermark PDFs using text or images? (TransparentWatermark2.java). See project source code on GitHub.
Now an example of the PDF I'm getting is this one (the rest of the document is omitted):
As you can see the watermark is centered and horizontal.
I'd like to keep it centered in the middle of the page, but rotate it "45" degrees, so it rotates anticlockwise. Something like this:
This is the code for stamping the watermark on a given byte array (pdf documents only for me right now)
/**
* Returns the same document with the watermark stamped on it.
* #param documentBytes Byte array of the pdf which is going to be returned with the watermark
* #return byte[] with the same byte array provided but now with the watermark stamped on it.
* #throws IOException If any IO exception occurs while adding the watermark
* #throws DocumentException If any DocumentException exception occurs while adding the watermark
*/
private byte[] getDocumentWithWaterMark(byte[] documentBytes) throws IOException, DocumentException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// pdf
PdfReader reader = new PdfReader(documentBytes);
int n = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, outputStream);
// text watermark
Font font = new Font(Font.HELVETICA, 60);
Phrase phrase = new Phrase("SuperEasy You Done", font);
// transparency
PdfGState gs1 = new PdfGState();
gs1.setFillOpacity(0.06f);
// properties
PdfContentByte over;
Rectangle pagesize;
float x, y;
// loop over every page (in case more than one page)
for (int i = 1; i <= n; i++) {
pagesize = reader.getPageSizeWithRotation(i);
x = (pagesize.getLeft() + pagesize.getRight()) / 2;
y = (pagesize.getTop() + pagesize.getBottom()) / 2;
over = stamper.getOverContent(i);
over.saveState();
over.setGState(gs1);
// add text
ColumnText.showTextAligned(over, Element.ALIGN_CENTER, phrase, x, y, 0);
over.restoreState();
}
stamper.close();
reader.close();
return outputStream.toByteArray();
}
PS: I read this, but it didn't help:
http://itext.2136553.n4.nabble.com/rotate-a-watermark-td2155042.html
You just need to specify the desired rotation angle as the 6th parameter in this line:
ColumnText.showTextAligned(over, Element.ALIGN_CENTER, phrase, x, y, 0); // rotate 0 grades in this case
If the specified value is positive ( > 0) the rotation is anticlockwise, otherwise (< 0) the rotation is clockwise.
In this particular case, for rotating the watermark 45 degrees anticlockwise you just need to write the previous line like this:
ColumnText.showTextAligned(over, Element.ALIGN_CENTER, phrase, x, y, 45f); // 45f means rotate the watermark 45 degrees anticlockwise
By applying this same principle we can achieve any rotation in any direction.
The whole documentation is here: https://itextpdf.com/en/resources/api-documentation under the links for version 5 and version 7.

Moving text on a page with iText7 retaining font, color, style, ... but changing size of the text

I want to move text with iText7. I have a source bounding box, that can be somewhere on the page and I have a target bounding box, that has a fixed position (incl. width and height). I'll stay on the same page. The source and target boxes can overlap. The source bounding box can also be larger than the target box. In this case I have to reduce the font size. The text should retain font, color and so on.
There is a cut and paste example on the iText website . But in the result pdf file you can select the text at the new and old position (tried it only with a normal pdf reader). I don't want the text to be selectable at the old position.
I thought, that maybe I could select the text and just place it at the new position and remove it from the old position. For the latter i would need pdfSweep, but this is ok. Adding the text at the new position should be no problem. Even if the text has different fonts, sizes and so on. There are plenty of examples on the iText website. The only way I know to select the text is like shown in this example. This gives me only the text. But to place it at the target position with the same font, color and so on, I need all those informations, too.
I know, that pdf is not meant for editing. This is often mention in answers on StackOverflow.
Is there a way to do this with iText7?
There is no high level API in iText allowing you to move page content, in particular not all content from some rectangle. One reason may be that in general this is no mere moving. PDFs often contain structures influencing larger areas, and such structures would not simply have to be moved but instead copied, and each copy restricted to its area.
It is indeed possible, though, to combine the cut and paste example the OP found with the pdfSweep module already considered by the OP to a solution which prevents the text from being selectable at the old position, e.g. like this:
public void moveCleanSection(PdfReader pdfReader, String targetFile, int page, Rectangle from, Rectangle to) throws IOException
{
LicenseKey.loadLicenseFile("itextkey-multiple-products.xml");
ByteArrayOutputStream interimMain = new ByteArrayOutputStream();
ByteArrayOutputStream interimPage = new ByteArrayOutputStream();
ByteArrayOutputStream interimSection = new ByteArrayOutputStream();
try ( PdfDocument pdfMainDocument = new PdfDocument(pdfReader);
PdfDocument pdfPageDocument = new PdfDocument(new PdfWriter(interimPage)) )
{
pdfMainDocument.setCloseReader(false);
pdfMainDocument.copyPagesTo(page, page, pdfPageDocument);
}
try ( PdfDocument pdfMainDocument = new PdfDocument(pdfReader, new PdfWriter(interimMain));
PdfDocument pdfSectionDocument = new PdfDocument(new PdfReader(new ByteArrayInputStream(interimPage.toByteArray())),
new PdfWriter(interimSection)) )
{
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
cleanUpLocations.add(new PdfCleanUpLocation(page, from, null));
cleanUpLocations.add(new PdfCleanUpLocation(page, to, null));
PdfCleanUpTool cleaner = new PdfCleanUpTool(pdfMainDocument, cleanUpLocations);
cleaner.cleanUp();
cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
Rectangle mediaBox = pdfSectionDocument.getPage(1).getMediaBox();
if (from.getTop() < mediaBox.getTop())
cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(mediaBox.getLeft(), from.getTop(), mediaBox.getWidth(), mediaBox.getTop() - from.getTop()), null));
if (from.getBottom() > mediaBox.getBottom())
cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(mediaBox.getLeft(), mediaBox.getBottom(), mediaBox.getWidth(), from.getBottom() - mediaBox.getBottom()), null));
if (from.getLeft() > mediaBox.getLeft())
cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(mediaBox.getLeft(), mediaBox.getBottom(), from.getLeft() - mediaBox.getLeft(), mediaBox.getHeight()), null));
if (from.getRight() < mediaBox.getRight())
cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(from.getRight(), mediaBox.getBottom(), mediaBox.getRight() - from.getRight(), mediaBox.getHeight()), null));
cleaner = new PdfCleanUpTool(pdfSectionDocument, cleanUpLocations);
cleaner.cleanUp();
}
try ( PdfDocument pdfSectionDocument = new PdfDocument(new PdfReader(new ByteArrayInputStream(interimSection.toByteArray())));
PdfDocument pdfMainDocument = new PdfDocument(new PdfReader(new ByteArrayInputStream(interimMain.toByteArray())), new PdfWriter(targetFile)) )
{
float scale = Math.min(to.getHeight() / from.getHeight(), to.getWidth() / from.getWidth());
pdfSectionDocument.getPage(1).setMediaBox(from);
PdfFormXObject pageXObject = pdfSectionDocument.getFirstPage().copyAsFormXObject(pdfMainDocument);
PdfPage pdfPage = pdfMainDocument.getPage(page);
PdfCanvas pdfCanvas = new PdfCanvas(pdfPage);
pdfCanvas.addXObject(pageXObject, scale, 0, 0, scale, (to.getLeft() - from.getLeft() * scale), (to.getBottom() - from.getBottom() * scale));
}
}
(From MoveSectionCleanly.java)
Beware: Due to the nature of pdfSweep, text being on the border of the source area is removed both from the source and the copy of it.

2D barcode generation issue in Java

I am generating barcodes using iText API, everything looks good when it is linear barcodes when it is 2D barcodes then barcodes are placed into pdf document as images, hence reducing the quality of the barcode on low resolution printers and unable to scan the barcode. Below is the code
BarcodePDF417 pdf417 = new BarcodePDF417();
String text = "BarcodePDF417 barcode";
pdf417.setText(text);
Image img = pdf417.getImage();
document.add(img);
Now i am look for an alternative to draw barcode and i found palceBarcode method which might favor to the requirement.
I have seen the below code in BarcodePDF417 class in itext source and could not able to find out the way how to use it
public void placeBarcode(PdfContentByte cb, BaseColor foreground, float moduleHeight, float moduleWidth) {
paintCode();
int stride = (bitColumns + 7) / 8;
cb.setColorFill(foreground);
for (int k = 0; k < codeRows; ++k) {
int p = k * stride;
for (int j = 0; j < bitColumns; ++j) {
int b = outBits[p + j / 8] & 0xff;
b <<= j % 8;
if ((b & 0x80) != 0) {
cb.rectangle(j * moduleWidth, (codeRows - k - 1) * moduleHeight, moduleWidth, moduleHeight);
}
}
}
cb.fill();
}
Can anyone suggest the way to use the above method?
I have written code like below but getting dark page as a whole.
Rectangle pageSize = new Rectangle(w * 72, h * 72);
Document doc = new Document(pageSize, 1f, 1f, 1f, 1f);
PdfWriter writer = PdfWriter.getInstance(doc, getOutputStream());
doc.open();
PdfContentByte cb = writer.getDirectContent();
BarcodePDF417 pf = new BarcodePDF417();
pf.setText("BarcodePDF417 barcode");
pf.getImage();
Rectangle rc = pf.getBarcodeSize();
pf.placeBarcode(cb, BaseColor.BLACK, rc.getHeight(), rc.getWidth());
doc.close();
Please take a look at the BarcodePlacement example. In this example, we create three PDF417 barcodes:
PdfContentByte cb = writer.getDirectContent();
Image img = createBarcode(cb, "This is a 2D barcode", 1, 1);
document.add(new Paragraph(
String.format("This barcode measures %s by %s user units",
img.getScaledWidth(), img.getScaledHeight())));
document.add(img);
img = createBarcode(cb, "This is NOT a raster image", 3, 3);
document.add(new Paragraph(
String.format("This barcode measures %s by %s user units",
img.getScaledWidth(), img.getScaledHeight())));
document.add(img);
img = createBarcode(cb, "This is vector data drawn on a PDF page", 1, 3);
document.add(new Paragraph(
String.format("This barcode measures %s by %s user units",
img.getScaledWidth(), img.getScaledHeight())));
The result looks like this on the outside:
One particular barcode looks like this on the inside:
I'm adding this inside view to show that the 2D barcode is not added as a raster image (as was the case with the initial approach you've tried). It is a vector image consisting of a series of small rectangles. You can check this for yourself by taking a look at the barcode_placement.pdf file.
Please don't be confused because I use an Image object. If you look at the createBarcode() method, you can see that the Image is, in fact, a vector image:
public Image createBarcode(PdfContentByte cb, String text,
float mh, float mw) throws BadElementException {
BarcodePDF417 pf = new BarcodePDF417();
pf.setText("BarcodePDF417 barcode");
Rectangle size = pf.getBarcodeSize();
PdfTemplate template = cb.createTemplate(
mw * size.getWidth(), mh * size.getHeight());
pf.placeBarcode(template, BaseColor.BLACK, mh, mw);
return Image.getInstance(template);
}
The height and the width passed to the placeBarcode() method, define the height and the width of the small rectangles that are drawn. If you look at the inside view, you can see for instance:
0 21 3 1 re
This is a rectangle with x = 0, y = 21, width 3 and height 1.
When you ask the barcode for its size, you get the number of rectangles that will be drawn. Hence the dimensions of the barcode is:
Rectangle size = pf.getBarcodeSize();
float width = mw * size.getWidth();
float height = mh * size.getHeight();
Your assumption that size is a size in user units is only correct if mw and mh are equal to 1.
I use these values to create a PdfTemplate instance and I draw the barcode to this Form XObject. Most of the times, it's easier to work with the Image class than working with PdfTemplate, so I wrap the PdfTemplate inside an Image.
I can then add this Image to the document just like any other image. The main difference with ordinary images, is that this image is a vector image.

Finding co ordinates of selected rectangle in pdf

I am using pdf box for converting pdf pages to images and adding these images to the jframe .I draw the rectangle in jframe using paint method and got the co ordinates of rectangle.My requirement is using these co ordinates i have to clean up the text in pdf .For that i written below logic but it is not working properly. I think the co ordinates what i selected are different than the itext rectangle co ordinates.please give a solution.
public void manipulatePdf(String src, String dest,
java.awt.Rectangle selectRect) throws IOException,
DocumentException {
PdfReader reader = new PdfReader(src);
Rectangle page = reader.getBoxSize(1, "media");
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
List<PdfCleanUpLocation> cleanUpLocations = new ArrayList<PdfCleanUpLocation>();
float lly = page.getHeight() - selectRect.height;
float ury = page.getHeight() - selectRect.y;
System.out.println("new pagesize" + lly + " " + ury);
cleanUpLocations.add(new PdfCleanUpLocation(1, new Rectangle(0f, lly,
page.getWidth(), ury), BaseColor.GRAY));
PdfCleanUpProcessor cleaner = new PdfCleanUpProcessor(cleanUpLocations,
stamper);
cleaner.cleanUp();
stamper.close();
reader.close();
}
not much familiar with these objects, however at first glance there seems some problem at line "float ury = page.getHeight() - selectRect.y;" . Do you want to to use 'page.y' instead !!

iText PDFDocument page size inaccurate

I am trying to add a header to existing pdf documents in Java with iText. I can add the header at a fixed place on the document, but all the documents are different page sizes, so it is not always at the top of the page. I have tried getting the page size so that I could calculate the position of the header, but it seems as if the page size is not actually what I want. On some documents, calling reader.getPageSize(i).getTop(20) will place the text in the right place at the top of the page, however, on some different documents it will place it half way down the page. Most of the pages have been scanned be a Xerox copier, if that makes a difference. Here is the code I am using:
PdfReader reader = new PdfReader(readFilePath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(writeFilePath));
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
PdfContentByte cb = stamper.getOverContent(i);
cb.beginText();
cb.setFontAndSize(bf, 14);
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
cb.showTextAligned(PdfContentByte.ALIGN_CENTER, "Copy", x, y, 0);
cb.endText();
}
stamper.close();
PDF that works correctly
PDF that works incorrectly
Take a look at the StampHeader1 example. I adapted your code, introducing ColumnText.showTextAligned() and using a Phrase for the sake of simplicity (maybe you can change that part of your code too):
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Phrase header = new Phrase("Copy", new Font(FontFamily.HELVETICA, 14));
for (int i = 1; i <= reader.getNumberOfPages(); i++) {
float x = reader.getPageSize(i).getWidth() / 2;
float y = reader.getPageSize(i).getTop(20);
ColumnText.showTextAligned(
stamper.getOverContent(i), Element.ALIGN_CENTER,
header, x, y, 0);
}
stamper.close();
reader.close();
}
As you have found out, this code assumes that no rotation was defined.
Now take a look at the StampHeader2 example. I'm using your "Wrong" file and I've added one extra line:
stamper.setRotateContents(false);
By telling the stamper not to rotate the content I'm adding, I'm adding the content using the coordinates as if the page isn't rotated. Please take a look at the result: stamped_header2.pdf. We added "Copy" at the top of the page, but as the page is rotated, we see the word appear on the side. The word is rotated because the page is rotated.
Maybe that's what you want, maybe it isn't. If it isn't, please take a look at StampHeader3 in which I calculate x and y differently, based on the rotation of the page:
if (reader.getPageRotation(i) % 180 == 0) {
x = reader.getPageSize(i).getWidth() / 2;
y = reader.getPageSize(i).getTop(20);
}
else {
x = reader.getPageSize(i).getHeight() / 2;
y = reader.getPageSize(i).getRight(20);
}
Now the word "Copy" appears on what is perceived as the "top of the page" (but in reality, it could be the side of the page): stamped_header3.pdf

Categories