Retrieve the coordinates of a Paragraph in iText - java

I'm trying to retrieve the x, y coordinates from a Paragraph created in iText. I followed the approved answer in How to get vertical cursor position when writing document in iText 7? but I'm not getting the expected result.
PdfDocument pdfDoc = new PdfDocument(new PdfWriter("output/ITextSandbox/Coordinates.pdf"));
pdfDoc.setDefaultPageSize(PageSize.LETTER); // 8.5 x 11
Document document = new Document(pdfDoc);
PdfFont font = PdfFontFactory.createFont(StandardFonts.COURIER, PdfEncodings.UTF8);
document.setFont(font);
document.setFontSize(10);
Paragraph paragraph = null;
// Print 5 lines to ensure the y coord is sufficiently moved away from the top of the page.
for (int i = 1; i <= 5; i++)
{
paragraph = new Paragraph();
paragraph.add(new Text("Line " + i));
document.add(paragraph);
}
// Print a new paragraph from which to obtain the x, y coordinates.
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
// Follow the steps from the approved answer in
// https://stackoverflow.com/questions/51953723/how-to-get-vertical-cursor-position-when-writing-document-in-itext-7
IRenderer renderer = paragraph.createRendererSubTree().setParent(document.getRenderer());
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult = renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
float y = layoutResult.getOccupiedArea().getBBox().getY();
float x = layoutResult.getOccupiedArea().getBBox().getX();
System.out.println("x = " + x + ", y = " + y); // y should be approximately 630, not 710.
With standard margins and 10 pt font, the coordinates for the 6th line should approximately be x = 0, y = 630. Instead, I get y = 710.

The code sample in the question simulates rendering on a certain layout area. That approach works to determine the coordinates on the page if the exact same elements are simulated on the exact same layout area.
In the question, only the 6th paragraph is simulated, so the larger y coordinate (higher on the page) is expected. To get the correct y coordinate, the 5 preceding paragraphs would also have to be simulated.
Moreover, the layout area is not the same as page. This code does not take into account the page margins correctly:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
With the default page margins of 36, the correct layout area would be:
float width = document.getPageEffectiveArea(PageSize.LETTER).getWidth();
float height = document.getPageEffectiveArea(PageSize.LETTER).getHeight();
LayoutResult layoutResult =
renderer.layout(new LayoutContext(new LayoutArea(1, new Rectangle(36, 36, width, height))));
A much easier way to get the current coordinates after rendering some elements is:
/*...*/
paragraph = new Paragraph();
paragraph.add(new Text("Line 6"));
document.add(paragraph);
Rectangle remaining = document.getRenderer().getCurrentArea().getBBox();
float y = remaining.getTop();
System.out.println("y = " + y);
Result: y = 631.6011
To illustrate the remaining layout area, let's draw it on the page:
PdfCanvas canvas = new PdfCanvas(pdfDoc.getPage(1));
canvas.setStrokeColor(ColorConstants.RED).rectangle(remaining).stroke();
With some different page margins:
document.setMargins(5, 25, 15, 5);

Related

iText 7 : How to position "page x of y" to the bottom right

Using itext 7, I followed Page X of Y tutorial here - https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-building-blocks/chapter-7-handling-events-setting-viewer-preferences-and-printer-properties#Chapter7:Handlingevents;settingviewerpreferencesandprinterproperties-Solvingthe%22PageXofY%22problem
Our requirement is to put page number to the bottom right. I tried variations of x, y position for the placeholder and initial "Page x of", but it isn't getting positioned correctly. And If the total number of pages 3 digits, it overlaps "of". If the total number of pages is 2 digit - there is a space between "of" and total. How do I position it in order to not have additional spaces or overlapping characters.
Here is the code -
...
...
float topMargin = 30;
float bottomMargin = topMargin + 20;
float rightMargin = topMargin;
float leftMargin = topMargin;
Document document = new Document(pdf, PageSize.A4);
document.setMargins(topMargin, rightMargin, bottomMargin, leftMargin);
...
..
PageXofY pageXofYEvent = new PageXofY(pdf, document);
pdf.addEventHandler(PdfDocumentEvent.START_PAGE, pageXofYEvent);
pageXofYEvent.writeTotal(pdf);
public class PageXofY implements IEventHandler
{
protected PdfFormXObject placeholder;
PdfFont font = ITextFonts.getWriterBaseFont();
protected float side = 20;
protected float x = 300;
protected float y = 30;
protected float space = 10f;
protected float descent = 3;
Document document;
float pWidth = 0;
public PageXofY(PdfDocument pdf, Document document)
{
placeholder = new PdfFormXObject(new Rectangle(-20, -15, 100, 100));
this.document = document;
}
#Override
public void handleEvent(Event event)
{
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
int pageNumber = pdf.getPageNumber(page);
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
Paragraph p = new Paragraph().add("Page ").add(String.valueOf(pageNumber)).add(" of ")
.setFont(font).setFontSize(8).setMarginTop(10f);
pWidth = TableColumnSpacer.getParagraphWidth(p, document);
x = pageSize.getRight() - document.getRightMargin() - pWidth - 4f;
y = pageSize.getBottom() + 30 ;//document.getBottomMargin();
canvas.showTextAligned(p, x, y, TextAlignment.LEFT, VerticalAlignment.BOTTOM);
pdfCanvas.addXObjectAt(placeholder, x - space , y - 18);
pdfCanvas.release();
}
public void writeTotal(PdfDocument pdf)
{
Canvas canvas = new Canvas(placeholder, pdf);
Paragraph p = new Paragraph(String.valueOf(pdf.getNumberOfPages())).setFont(font).setFontSize(8).setMarginTop(10f);
canvas.showTextAligned(p, 30, descent, TextAlignment.LEFT, VerticalAlignment.BOTTOM);
}
}
Resolved this issue by adding page footer once the whole document is generated. Iterating over the pages and adding footer text and page x of y. Here is the code -
public void stampPageFooter(Document doc, String name) throws Exception
{
int numberOfPages = doc.getPdfDocument().getNumberOfPages();
PdfDocument pdfDocument = doc.getPdfDocument();
for (int i = 1; i <= numberOfPages; i++)
{
PdfPage page = pdfDocument.getPage(i);
Rectangle pageSize = page.getPageSize();
float pageX = pageSize.getRight() - doc.getRightMargin() - 40;
float pageY = pageSize.getBottom() + 30 ;
// Write x of y to the right bottom
Paragraph p = new Paragraph(String.format("Page %s of %s", i, numberOfPages)).addStyle(PAGE_NUM_STYLE);
doc.showTextAligned(p, pageX, pageY, i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
// write name to the left
pageX = pageSize.getLeft() + doc.getLeftMargin();
pageY = pageSize.getBottom() + 30;
Paragraph para = new Paragraph(name).addStyle(PAGE_NUM_STYLE).setMarginTop(10f);
doc.showTextAligned(para, pageX, pageY , i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
}

PDFBox Page Rotation - Scaled Image Size Decreases

We are using PDFBox 2.0 version in our project. What I needed to add a QRcode image with the rotation of the page. I have taken reference from the below link to scale the content of the page after rotation. (Without this code earlier I was not able to even see the image on page)
To scale content of the page
But, after putting referenced code into place the actual image size showing decreased (By comparing the same image get displayed without rotation). Below are the two PDF page images with and without rotation.
Image_Without_Rotation
Image_With_90_Degree_Rotation
Herewith sharing the code to add image into page with rotation:
PDDocument pdDocument = PDDocument.load(new File(aBarcodeVO.getSourceFilePath()), "");
int total = pdDocument.getNumberOfPages();
PDPage page = pdDocument.getDocumentCatalog().getPages().get(0);
PDRectangle rectangle = getRotatedMediaBox(page);
PDDocument newDocument = new PDDocument();
for (int i = 0; i < total; i++) {
PDPage pdPage = newDocument.importPage(pdDocument.getPage(i));
PDRectangle pageRect = getRotatedMediaBox(pdPage);
int rotation = pdPage.getRotation();
PDRectangle cropBoxRect = page.getCropBox();
//Calculate margin between crop box rectangle and page rectangle.
float[] margins = getCropBoxMargin(pageRect, cropBoxRect, rotation);
if (rotation == 90 || rotation == 270) {
cropBoxRect = new PDRectangle(cropBoxRect.getLowerLeftY(), cropBoxRect.getLowerLeftX(), cropBoxRect.getHeight(),
cropBoxRect.getWidth());
}
//Calculate llx and lly for QR Code image.
float rectLlx = getOffsetX(displacementVO, qrImageSize) + margins[0];
float rectLly = getOffsetY(displacementVO, qrImageSize) + margins[1];
BufferedImage bufferedImage = getImage();
PDPageContentStream pageContentStream = new PDPageContentStream(newDocument, pdPage,
PDPageContentStream.AppendMode.APPEND, true);
PDImageXObject image = JPEGFactory.createFromImage(newDocument, bufferedImage);
//ADDED CODE HERE TO SCALE THE IMAGE
if (rotation == 90 || rotation == 270) {
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(rotation), 0, 0);
PDRectangle cropBox = pdPage.getCropBox();
float tx = (cropBox.getLowerLeftX() + cropBox.getUpperRightX()) / 2;
float ty = (cropBox.getLowerLeftY() + cropBox.getUpperRightY()) / 2;
Rectangle rectang = cropBox.transform(matrix).getBounds();
float scale = Math.min(cropBox.getWidth() / (float)rectang.getWidth(), cropBox.getHeight() / (float)rectang.getHeight());
pageContentStream.transform(Matrix.getTranslateInstance(tx, ty));
pageContentStream.transform(matrix);
pageContentStream.transform(Matrix.getScaleInstance(scale, scale));
pageContentStream.transform(Matrix.getTranslateInstance(-tx, -ty));
}
pageContentStream.drawImage(image, rectLlx, rectLly, qrImageSize, qrImageSize);
pageContentStream.close();
}
newDocument.save(new File(getDestinationFilePath(false)));
newDocument.close();
pdDocument.close();
Please guide me that what causing this image size decreasing or tell me if I have need to change approach for page rotation.

Rotate watermark text at 45 degree angle across the center Apache PDFBox

I want to add a text to the PDF using PDFBox API and rotate it by 45 Degree and place it at the center of the page, The text is dynamic and should be placed in the center always, I got everything else to work except centering piece, I'll appreciate any help.
I have this code:
Point2D.Float pageCenter = getCenter(page);
float stringWidth = getStringWidth(watermarkText, font, fontSize);
float textX = pageCenter.x - stringWidth / 2F + center.x;
System.out.println(textX);
float textY = pageCenter.y + center.y;
//System.out.println("Inside cross"+textX+", "+textY);
fontSize = 110.0f;
cs.transform(Matrix.getRotateInstance(Math.toRadians(45), textX, textY));
cs.moveTo(0, 0);
cs.lineTo(125, 0);
r0.setNonStrokingAlphaConstant(0.20f);
This is the result i want:
Output PDF
What I do is to first rotate based on the calculated angle. In this "rotated world" I do a horizontal offset so that the text is in the middle, and also move the text vertically a bit lower, so that it is in the "vertical" middle of an imagined diagonal line (horizontal in the "rotated world").
try (PDDocument doc = new PDDocument())
{
PDPage page = new PDPage();
doc.addPage(page);
PDFont font = PDType1Font.HELVETICA_BOLD;
try (PDPageContentStream cs =
new PDPageContentStream(doc, page, PDPageContentStream.AppendMode.APPEND, true, true))
// use this long constructor when working on existing PDFs
{
float fontHeight = 110;
String text = "Watermark";
float width = page.getMediaBox().getWidth();
float height = page.getMediaBox().getHeight();
int rotation = page.getRotation();
switch (rotation)
{
case 90:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(90), height, 0));
break;
case 180:
cs.transform(Matrix.getRotateInstance(Math.toRadians(180), width, height));
break;
case 270:
width = page.getMediaBox().getHeight();
height = page.getMediaBox().getWidth();
cs.transform(Matrix.getRotateInstance(Math.toRadians(270), 0, width));
break;
default:
break;
}
float stringWidth = font.getStringWidth(text) / 1000 * fontHeight;
float diagonalLength = (float) Math.sqrt(width * width + height * height);
float angle = (float) Math.atan2(height, width);
float x = (diagonalLength - stringWidth) / 2; // "horizontal" position in rotated world
float y = -fontHeight / 4; // 4 is a trial-and-error thing, this lowers the text a bit
cs.transform(Matrix.getRotateInstance(angle, 0, 0));
cs.setFont(font, fontHeight);
//cs.setRenderingMode(RenderingMode.STROKE); // for "hollow" effect
PDExtendedGraphicsState gs = new PDExtendedGraphicsState();
gs.setNonStrokingAlphaConstant(0.2f);
gs.setStrokingAlphaConstant(0.2f);
gs.setBlendMode(BlendMode.MULTIPLY);
cs.setGraphicsStateParameters(gs);
// some API weirdness here. When int, range is 0..255.
// when float, this would be 0..1f
cs.setNonStrokingColor(255, 0, 0);
cs.setStrokingColor(255, 0, 0);
cs.beginText();
cs.newLineAtOffset(x, y);
cs.showText(text);
cs.endText();
}
doc.save("watermarked.pdf");
}
Note that I've set both stroking and non stroking (= fill). This is useful for people who want to try the (disabled) "hollow" appearance, that one uses stroking only. The default mode is fill, i.e. non-stroking.

java code to highlight the color of text in pdf

I am using com.lowagie.text in my pdf. While generating it, I need to highlight a few text.This is the code i have.I need the code for Highlighting text in a chunk.
PdfContentByte cb = writer.getDirectContent();
Chunk chunck = new Chunk(arraySpec[k],font);
Phrase phrase = new Phrase(8.0f+ 1);
phrase.add(chunck);
ColumnText columnText = new ColumnText(cb);
columnText.addText(phrase);
//verify the bounding box size
if (attributeBoundingBoxTextWidth == 0){
attributeBoundingBoxTextWidth = (int) boardWidth;
}
float llx = xpos;
float lly = 0;
float urx = llx + width;
float ury = refYPos - sizeOftext;
refYPos = ury;
float leading = 8.0f+ 1;
columnText.setSimpleColumn(llx, lly, urx, ury, leading, Element.ALIGN_LEFT);
columnText.go();
I got a solution for the above post.
columnText.getCanvas().setColorFill(Color.decode("#FFA500"));
columnText.getCanvas().rectangle(llx, ury-(leading) ,columnText.getWidth(phrase) , sizeOftext);
columnText.getCanvas().fill();
columnText.go();

PDFbox overlaped links at index document

I'm completely newbie to PDFBox and I'm having an issue I can't find the way to solve by the moment.
I get from my database a list of folder and documents located in those folders, I iterate over all these data to generate an index with active links to the correct folder/document path. (Imagine I have a root folder and I want to have a pdf index in that root with relative links to all folders and documents contained in it)
The main code looks like follows:
try {
PDDocument document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDFont font = PDType1Font.HELVETICA;
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(100, 700);
contentStream.drawString("Índice " + expediente.getNombre());
contentStream.endText();
int i = 0;
for (Folder currFol : root.getFolders()) {
for (Document currDoc : currFol.getDocuments()) {
i++;
float x = 50;
float y = 250;
String text = currFol.getName() + "/" + currDoc.getName();
float textWidth = font.getStringWidth(text) / 1000 * 12;
PDAnnotationLink link = new PDAnnotationLink();
PDGamma colourBlue = new PDGamma();
colourBlue.setB(1);
link.setColour(colourBlue);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(currFol.getName() + "/" + currDoc.getName());
link.setAction(action);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y -(i* 5));
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 50);
link.setRectangle(position);
page.getAnnotations().add(link);
}
}
// Make sure that the content stream is closed:
contentStream.close();
document.save(output);
document.close();
} catch (Exception e) {
e.printStackTrace();
}
My problem here is that all elements are printed ovelaped, text and boxes are over each other and by the moment can't find out how to print correctly all links in a list formatted style to create the index.
Any idea or suggestion will be very apreciated.
I tried following some tutorials, with no succes by the moment, like http://www.programcreek.com/java-api-examples/index.php?api=org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationLink, http://www.massapi.com/class/pd/PDAnnotationLink.html .
I tried without PDRectangle, just the text link (as PDRectangle is present in all examples I found but I don't need it really)
Thank you,
In your inner loop you always set x and y to the same values
float x = 50;
float y = 250;
which you don't change thereafter. Then you draw the respective text starting at x, y
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
Thus, you draw each entry starting at the same coordinates. So it is not surprising all the text overlaps.
Furthermore you set position and size of the links like this:
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y -(i* 5));
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 50);
link.setRectangle(position);
Thus, the upper y coordinate of each link is always y + 50, i.e. 300, and the lower y coordinate of the links moves down by 5 per iteration. So the vertical extent of your first link is contained in that of the second which in turn is contained in that of the third etc etc etc. Again no surprise that these annotations overlap.
Thus, this has nothing to do with being a PDFBox newbie but merely with getting one's coordinates right... ;)
How about something like this instead:
float x = 50;
float y = 650;
for (Folder currFol : root.getFolders()) {
for (Document currDoc : currFol.getDocuments()) {
String text = currFol.getName() + "/" + currDoc.getName();
float textWidth = font.getStringWidth(text) / 1000.0 * 12;
PDAnnotationLink link = new PDAnnotationLink();
PDGamma colourBlue = new PDGamma();
colourBlue.setB(1);
link.setColour(colourBlue);
// add an action
PDActionURI action = new PDActionURI();
action.setURI(currFol.getName() + "/" + currDoc.getName());
link.setAction(action);
contentStream.beginText();
contentStream.setFont(font, 12);
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(text);
contentStream.endText();
PDRectangle position = new PDRectangle();
position.setLowerLeftX(x);
position.setLowerLeftY(y - 3);
position.setUpperRightX(x + textWidth);
position.setUpperRightY(y + 12);
link.setRectangle(position);
page.getAnnotations().add(link);
y -= 15;
}
}

Categories