How to position text relative to page using iText? - java

How do I set the position of text so that it is centered vertically relative to its page size? I want to position it say for example x number of points from right and centered vertically. The text of course is rotated 90 degrees.
int n = reader.getNumberOfPages();
PdfImportedPage page;
PdfCopy.PageStamp stamp;
for (int j = 0; j < n; )
{
++j;
page = writer.getImportedPage(reader, j);
stamp = writer.createPageStamp(page);
Rectangle crop = reader.getCropBox(1);
// add overlay text
Phrase phrase = new Phrase("Overlay Text");
ColumnText.showTextAligned(stamp.getOverContent(), Element.ALIGN_CENTER, phrase,
crop.getRight(72f), crop.getHeight() / 2, 90);
stamp.alterContents();
writer.addPage(page);
}
The code above gives me inconsistent position of text, and in some pages, only a portion of the "Overlay text" is visible. Please help, I don't know how to properly use mediabox and cropbox and I'm new to itext.
Thanks!

Regarding the inconsistent position: that should be fixed by adding the vertical offset:
crop.getRight(72f), crop.getBottom() + crop.getHeight() / 2
Do you see? You took the right border with a margin of 1 inch as x coordinate, but you forgot to take into account the y coordinate of the bottom of the page (it's not always 0). Normally, this should fix the positioning problem.
Regarding the fact that only a portion of the overlay text is visible: my first guess was that you're adding content under the existing content, but that guess is wrong (you're using getOverContent()). What exactly do you mean by that second question? Do yo mean the text is clipped by the CropBox? Are you looking for a way to measure the content of phrase to see if it fits the height before you add it?

Related

How to define an offset for a PatternColor fill in iText?

I am trying to add tiled diagonal watermarks to the pdf, but it seems that pattern fills in iText are always tiled from the bottom left of the page, meaning that the tiles at the top and right side of the page can be cut abruptly. Is there an option to tile from the top left or with an offset instead?
Here is a sample of the code:
List<String> watermarkLines = getWatermarkLines();
Rectangle watermarkRect = getWatermarkRect();
PdfContentByte over = stamper.getOverContent(1);
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight();
for (int x = 0; x < watermarkLines.size(); x++) {
AffineTransform trans = getWatermarkTransform(watermarkLines, x);
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
}
over.setColorFill(new PatternColor(painter));
over.rectangle(0, 0, pageSize.getWidth(), pageSize.getHeight());
over.fill();
I tried changing the x and y of the rectangle function to negative or positive values, but it seems that the watermark is still stamped in the pattern as if it was tiled from the bottom left, cutting it in the same place as before.
First of, I cannot fathom which iText version you are using,
List<String> watermarkLines = getWatermarkLines();
...
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
implies that the third parameter of the ColumnText.showTextAligned method you use is typed as String or Object. The iText 5 version I have at hand, though, requires a Phrase there. Below I'll show how to apply an offset with the current iText 5.5.13. You'll have to check whether it also works for your version.
Yes, you can apply an offset... in the pattern definition!
If instead of
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight());
you create the pattern like this
PdfPatternPainter painter = over.createPattern(2 * watermarkRect.getWidth(), 2 * watermarkRect.getHeight(),
watermarkRect.getWidth(), watermarkRect.getHeight());
you have the same step size of pattern application (watermarkRect.getWidth(), watermarkRect.getHeight()) but a canvas twice that width and twice that height to position you text on. By positioning the text with an offset, you effectively move the whole pattern by that offset.
E.g. if you calculate the offsets as
Rectangle pageSize = pdfReader.getCropBox(1);
float xOff = pageSize.getLeft();
float yOff = pageSize.getBottom() + ((int)pageSize.getHeight()) % ((int)watermarkRect.getHeight());
and draw the text using
ColumnText.showTextAligned(painter, 0, new Phrase(watermarkLines.get(x)), (float) trans.getTranslateX() + xOff, (float) trans.getTranslateY() + yOff, 45f);
the pattern should fill the page as if starting at the top left corner of the visible page.
You haven't supplied getWatermarkLines, getWatermarkRect, and getWatermarkTransform. If I use
static AffineTransform getWatermarkTransform(List<String> watermarkLines, int x) {
return AffineTransform.getTranslateInstance(6 + 15*x, 6);
}
static Rectangle getWatermarkRect() {
return new Rectangle(65, 50);
}
static List<String> getWatermarkLines() {
return Arrays.asList("Test line 1", "Test line 2");
}
your original code for me creates a top left corner like this
and the code with the above offset creates one like this

PDFClown: Creating a TextMarkup leads to an inaccurate Box of the TextMarkup

Im working with PDFClown to analyze and work with PDFDocuments. My aim is to highlight all numbers within a table. For all numbers which belong together (For example: All numbers in one column of a table) I will create one TextMarkup with a List of Quads. First of all it looks like everythink work well: All highlights on the left belong to one TextMarkup and all Highlights on the right belong to another TextMarkup.
But when analyzing the size of the TextMarkup the size is bigger than it looks at the picture. So when drawing for example a rectangle arround the left TextMarkup box the rectangle intersects the other column despite no highlight of the left TextMarkup intersects the other column. Is there a way to optimize the Box of the TextMarkup? I think there is a bulbous ending of the box so that the box is intersecting the other TextMarkup
This is the code which creates the TextMarkup:
List<Quad> highlightQuads = new ArrayList<Quad>();
for (TextMarkup textMarkup : textMarkupsForOneAnnotation) {
Rectangle2D textBox = textMarkup.getBox();
Rectangle2D.Double rectangle = new Rectangle2D.Double(textBox.getX(), textBox.getY(), textBox.getWidth(), textBox.getHeight());
highlightQuads.add(Quad.get(rectangle));
}
if (highlightQuads.size() > 0) {
TextMarkup _textMarkup = new TextMarkup(pagesOfNewFile.get(lastFoundNewFilePage).getPage(), highlightQuads,"", MarkupTypeEnum.Highlight);
_textMarkup.setColor(DeviceRGBColor.get(Color.GREEN));
_textMarkup.setVisible(true);
allTextMarkUps.add(_textMarkup);
}
Here is an example file Example
Thank You !!
Your code is not really self contained (I cannot run it as it in particular misses the input data), so I could only do a bit of PDF Clown code analysis. That code analysis, though, did indeed turn up a PDF Clown implementation detail that would explain your observation.
How does PDF Clown calculate the dimensions of the markup annotation?
The markup annotation rectangle must be big enough to include all quads plus start and end decorations (rounded left and right caps on markup rectangle).
PDF Clown calculates this rectangle as follows in TextMarkup:
public void setMarkupBoxes(
List<Quad> value
)
{
PdfArray quadPointsObject = new PdfArray();
double pageHeight = getPage().getBox().getHeight();
Rectangle2D box = null;
for(Quad markupBox : value)
{
/*
NOTE: Despite the spec prescription, Point 3 and Point 4 MUST be inverted.
*/
Point2D[] markupBoxPoints = markupBox.getPoints();
quadPointsObject.add(PdfReal.get(markupBoxPoints[0].getX())); // x1.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[0].getY())); // y1.
quadPointsObject.add(PdfReal.get(markupBoxPoints[1].getX())); // x2.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[1].getY())); // y2.
quadPointsObject.add(PdfReal.get(markupBoxPoints[3].getX())); // x4.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[3].getY())); // y4.
quadPointsObject.add(PdfReal.get(markupBoxPoints[2].getX())); // x3.
quadPointsObject.add(PdfReal.get(pageHeight - markupBoxPoints[2].getY())); // y3.
if(box == null)
{box = markupBox.getBounds2D();}
else
{box.add(markupBox.getBounds2D());}
}
getBaseDataObject().put(PdfName.QuadPoints, quadPointsObject);
/*
NOTE: Box width is expanded to make room for end decorations (e.g. rounded highlight caps).
*/
double markupBoxMargin = getMarkupBoxMargin(box.getHeight());
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
refreshAppearance();
}
private static double getMarkupBoxMargin(
double boxHeight
)
{return boxHeight * .25;}
So it takes the bounding box of all the quads and adds left and right margins each as wide as a quarter of the height of this whole bounding box.
What is the result in your case?
While this added margin width is sensible if there is only a single quad, in case of your markup annotation which includes many quads on top of one another, this results in a giant, unnecessary margin.
How to improve the code?
As the added caps depend on the individual caps and not their combined bounding box, one can improve the code by using the maximum height of the individual quads instead of the height of the bounding box of all quads, e.g. like this:
Rectangle2D box = null;
double maxQuadHeight = 0;
for(Quad markupBox : value)
{
double quadHeight = markupBox.getBounds2D().getHeight();
if (quadHeight > maxQuadHeight)
maxQuadHeight = quadHeight;
...
}
...
double markupBoxMargin = getMarkupBoxMargin(maxQuadHeight);
box.setRect(box.getX() - markupBoxMargin, box.getY(), box.getWidth() + markupBoxMargin * 2, box.getHeight());
setBox(box);
If you don't want to patch PDF Clown for this, you can also execute this code (with minor adaptations) after constructing the TextMarkup _textMarkup to correct the precalculated annotation rectangle.
Is this fixing a PDF Clown error?
It is not an error as there is no need for the text markup annotation rectangle to be minimal; PDF Clown could also always use the whole crop box for each such annotation.
I would assume, though, that the author of the code wanted to calculate a somewhat minimal rectangle but only optimized for single line and so in a way did not live up to his own expectations...
Are there other problems in this code?
Yes. The text a markup annotation marks needs not be horizontal, it may be there at an angle, it could even be vertical. In such a case some margin would also be needed at the top and the bottom of the annotation rectangle, not (only) at the left and the right.

Adding footer to existing PDF

I am trying to add footer to my existing PDF. I did add one footer to the PDF.
Is there anyway to add 2 lines of footer? This is my code below:
Document document = new Document();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(new File("D:/TestDestination/Merge Output1.pdf")));
document.open();
PdfReader reader1 = new PdfReader("D:/TestDestination/Merge Output.pdf");
int n1 = reader1.getNumberOfPages();
PdfImportedPage page;
PdfCopy.PageStamp stamp;
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
for (int i = 0; i < n1; ) {
page = copy.getImportedPage(reader1, ++i);
stamp = copy.createPageStamp(page);
ColumnText.showTextAligned(stamp.getUnderContent(), Element.ALIGN_CENTER,new Phrase(String.format("page %d of %d", i, n1)),297.5f, 28, 0);
stamp.alterContents();
copy.addPage(page);
}
document.close();
reader1.close();
Please go to the official documentation and click Q&A to go to the Frequently Asked Questions. Select Absolute positioning of text.
You are currently using ColumnText in a way that allows you to add a single line of text. You are using ColumnText.showTextAligned(...) as explained in my answer to the question How to rotate a single line of text?
You should read the answers to questions such as:
How to add text at an absolute position on the top of the first page?
How to add text inside a rectangle?
How to truncate text within a bounding box?
How to fit a String inside a rectangle?
How to reduce redundant code when adding content at absolute positions?
Assuming that you don't have access to the official web site (otherwise you wouldn't have posted your question), I'm adding a short code snippet:
ColumnText ct = new ColumnText(stamp.getUnderContent());
ct.setSimpleColumn(rectangle);
ct.addElement(new Paragraph("Whatever text needs to fit inside the rectangle"));
ct.go();
In this snippet, stamp is the object you created in your code. The rectangle object is of type Rectangle. Its parameters are the coordinates of the lower-left and upper-right corner of the rectangle in which you want to render the multi-line text.
Caveat: all text that doesn't fit the rectangle will be dropped. You can avoid this by adding the text in simulation mode first. If the text fits, add it for real. If it doesn't fit, try anew using a smaller font or a bigger rectangle.

How to add page number in pdf on to left corner

I tried with the following code , Form number is added left side of the document but not at the top left corner.
document.open();
Chunk number = new Chunk(formNo);
number.setHorizontalScaling(1f);
Paragraph pp = new Paragraph();
pp.add(number);
document.add(pp);
My proble is resolved when I added this simple alignment
document.open();
Chunk number = new Chunk(formNo);
number.setHorizontalScaling(1f);
Paragraph pp = new Paragraph();
pp.add(number);
pp.setAlignment(Element.ALIGN_LEFT);
document.add(pp);
The top-left corner of the document has the following coordinates:
float x = document.getLeft();
float y = document.getTop();
If you want to add a number in the top-left corner, you need to do something like this:
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT,
new Phrase(String.format("%d", writer.getPageNumber())),
x - 10, y - 22, 0);
Not that I introduced a small horizontal margin of 10 user units.
I also subtracted 22 from the y value because the default font is 12 pt, and I want to make sure that there's sufficient space vertically to show the text.

Using itext for printing at absolute positions

What is the recommended way of printing a text document as a pdf using absolute positioning ?
I am having a table that I have to print. I am also having the data type lengths and starting positions of the columns.
Since the existing table was a character based, there was no problem in its positioning. But even after using a monotype font (Courier, 10) I am not able to properly position the data and last column(s) of each row erroneously skip to the next line.
In order to present my data as close as the character one, I divided the page into different columns(based on its page size) and then add the contents at the desired place. I am adding chunks of data into the paragraph.
paragraph.add(new Chunk(new VerticalPositionMark(), columnNo*ptUnit, false));
I have tried to tweak the page size, font size and margin lengths, but the data is not properly displayed. Have you encountered any such problems ? please do share your thoughts.
Have you tried ColumnText
When i want to write a paragraph and I do know the amount of lines...I do a cycle incrementing (even it says incrementing and is minus is because the pdf is from "south" to "north" (0 - height) the y in a proportion of the fontsize, something like this
//_valueArray is my string[]
//fontSize is the value of the Size of the font...
//1.5 it's just a magic number :) that give me the space line that i need
//cbLocal is the PdfContentByte of the pdf
for (i = 0; i < _valueArray.Length; i++)
{
var p = new Phrase(_valueArray[i], font);
ColumnText.ShowTextAligned(cbLocal, align, p, x, y, 0);
if (i + 1 != _valueArray.Length)
{
y = y - (fontSize*1.5f);
}
}

Categories