Add a PDFPTable to bottom of page in iText - java

I'm trying to add a table as a footer containing all the copyright text, page number etc. But I can't find any supporting method that'll accept a PdfPTable
For a phrase there is code like:
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_CENTER, new Phrase(
String.format("%d", document.getPageNumber())),
(document.getPageSize().getLeft() + document.getPageSize().getRight())/2,
document.getPageSize().getBottom() + 18, 0);

The PdfPTable class has a method writeSelectedRows() that can be used to add (a selection of columns and) rows at an absolute position.
Examples:
http://itextpdf.com/examples/iia.php?id=89 adds rows at an absolute position.
http://itextpdf.com/examples/iia.php?id=90 adds a selection of columns/rows at an absolute position.
http://itextpdf.com/examples/iia.php?id=91 an alternative solution where you wrap a table in a ColumnText object.

The examples posted by Bruno are a good pointer, here's an example without magic numbers:
private void writeFooterTable(PdfWriter writer, Document document, PdfPTable table) {
final int FIRST_ROW = 0;
final int LAST_ROW = -1;
//Table must have absolute width set.
if(table.getTotalWidth()==0)
table.setTotalWidth((document.right()-document.left())*table.getWidthPercentage()/100f);
table.writeSelectedRows(FIRST_ROW, LAST_ROW, document.left(), document.bottom()+table.getTotalHeight(),writer.getDirectContent());
}
This will write the PdfPTable within the document margins at the bottom overlapping any text you have at the bottom. If you wish to write the table in the margin, use: document.bottom() instead of document.bottom()+table.getTotalHeight().
Header/Footer Example
As a relevant note if you're following the example on this link, the "art" box does not appear to be required and the magic numbers 36, 54, 559, 788 correspond to:
document.left(), document.bottom(), document.right(), document.top()

To implement a custom footer you need to implement the PdfPageEventHelper.

Related

iText shrink table column to fit contents

In my iText document, I have a lot of tables scattered around, each with only one row of two columns. I would like to automatically shrink the leftmost column to fit its contents, and expand the rightmost column to fill the remaining space.
The exact contents of these two columns varies greatly, so there's no way to determine ahead of time what the exact width should be.
All of the content in this screenshot is wrapped in one outer table. Each nested table has its two columns highlighted red and blue. I would like to shrink the red columns as narrow as they can get without forcing the text to take up more lines than it has to.
In this case, the contents of the red cells are just a paragraph each, but it's possible they may contain a further-nested table with two cells of its own (which probably faces the same problem).
Is there a simple way to expand one column and shrink another without specifying exact or relative widths?
If you're using iText7 (and ditching the table for layout altogether), you can achieve this look and layout by building on the following example:
Output looks like this:
Code used to generate output above:
public void createPdf(String dest) throws IOException, FileNotFoundException{
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(writer);
Document doc = new Document(pdfDoc);
Paragraph p = new Paragraph();
Text t = new Text("Date:").setBold();
p.add(t);
t= new Text("10/12/17").setUnderline();
p.add(t);
p.add(new Tab());
p.add(createTwoPartBorderedText("Catalog Year: ","2017"));
p.add(new Tab());
p.add(createTwoPartBorderedText("L Number","2019284"));
doc.add(p);
doc.close();
}
public Paragraph createTwoPartBorderedText(String contentOne, String contentTwo){
Paragraph container= new Paragraph();
Text one = new Text(contentOne).setBold();
Border solidRed = new SolidBorder(Color.RED,1f);
one.setBorder(solidRed);
container.add(one);
Text two =new Text(contentTwo);
two.setUnderline();
Border solidBlue = new SolidBorder(Color.BLUE,1f);
two.setBorder(solidBlue);
container.add(two);
return container;
}

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 change the width of a cell?

I need your help in setting the length of a cell's bottom border. Currently, it is showing the bottom border of a cell too long and I need to shorten it. I tried to change the width of the table, but it is not coming properly.
Below is the code:
Paragraph tableParagraph = new Paragraph();
tableParagraph.setAlignment(Element.ALIGN_LEFT);
PdfPTable table55 = new PdfPTable(2);
table55 = new PdfPTable(new float[] { 6, 6 });
table55.setWidthPercentage(90f);
table55.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
PdfPCell cel2a = new PdfPCell(new Paragraph("Total of Net Profit ", font));
PdfPCell cel2b = new PdfPCell(new Paragraph("100.000" + " USD ", font));
cel2a.setBorder(Rectangle.NO_BORDER);
cel2b.setBorder(Rectangle.BOTTOM);
cel2a.setLeading(1f, 1.5f);
cel2b.setLeading(1f, 1.5f);
table55.addCell(cel2a);
table55.addCell(cel2b);
There are different ways to define the width of a cell. To explain the different options, we have to talk about defining the width of the table (all columns) first, and then talk about defining the width of the separate columns.
Width of a table:
Option 1: You don't define an absolute width.
Instead you ask iText to calculate the width based on the available space. The available space is the width of the page minus the size of the left and the right margin.
If you create a document like this:
Document document = new Document();
Then the width of the page is 595 user units (this is the width of an A4 page) and the width of the margins is 36 user units (these are default values if you don't define the margins explicitly). Hence the available width is 523 user units.
When you define your table like this:
PdfPTable table = new PdfPTable(2);
then the table will take up 80% of the available width when you add this table to a page. So if there's an available width of 523, the width of your table will be 418.4 user units.
You can change this by changing the width percentage. For instance if you add:
table.setWidthPercentage(100);
then 100% of the available width will be used and your table will be 523 user units wide.
Option 2: You define an absolute width.
Suppose that you are asked to create a table with a width of 4 inches. By default 1 inch is 72 user units, so you need a table of 288 user units.
This can be achieved like this:
PdfPTable table = new PdfPTable(2);
table.setTotalWidth(288);
table.setLockedWidth(true);
If you forget the line table.setLockedWidth(true); then iText will assume that you want iText to calculate the width based on the (default) width percentage and the available width. Locking the width, switches iText to use the total width as an absolute width.
Width of columns:
Case 1: relative widths
When you don't define any widths, each column will have the same width. This width will be calculated by dividing the width of the table by the number of columns. E.g. if the width of the table is 288 user units, and if the table has two columns, each column will be 144 user units.
Suppose that you want the second column to be three times as wide as the first column, then you can change the relative widths of the columns like this:
PdfPTable table = new PdfPTable(2);
table.setWidths(new float[] { 1, 3 });
or, if you want to use only one line:
PdfPTable table = new PdfPTable(new float[] { 1, 3 });
If the width of table is 288, the width of the first column will now be 72 and the width of the second column will be 216.
You'll get the same result if you do this:
PdfPTable table = new PdfPTable(new float[] { 25, 75 });
The widths are relative values. { 1, 3 } is the equivalent of { 25, 75 }.
You did something strange:
PdfPTable table55 = new PdfPTable(2);
table55 = new PdfPTable(new float[] { 6, 6 });
In the first line you create a table with two columns of identical width new PdfPTable(2), but you never use that PdfPTable(2), because in the second row you redefine the table55 variable as new PdfPTable(new float[]{ 6, 6 } ).
As { 6, 6 } are relative values, this is equivalent to { 50, 50 } or { 1, 1 }... You're creating a table with two columns with the same width.
Case 2: absolute widths
Suppose that you don't want to use relative widths (for instance because the Math of calculating 1/4 of 288 and 3/4 of 288 is too hard) and you want to create a table width two columns of which the first one is 1 inch wide and the second one is 3 inches wide, then you can once again use the principle of the locked width of a table. Take a look at:
PdfPTable table = new PdfPTable(2);
table.setTotalWidth(new float[]{ 72, 216 });
table.setLockedWidth(true);
Now we pass an array to the setTotalWidth() method. Widths are no longer relative, and we tell iText to use these absolute widths by locking the width.
Now the first column (and all the cells in the first column) will be 72 user units (1 inch) wide and the second column will be 216 user units (3 inch) wide.
Width of the cells:
The width of the cells follow the widths of the columns.
All of this has, of course, been explained in large detail in the documentation that is available online. See for instance examples such as ColumnWidths or the entire chapter that is dedicated to tables in the free ebook The Best iText Questions on StackOverflow. One wonders why no one ever reads that documentation. Does it make sense to keep on answering questions if no one is interested in the answers? (Sorry for this philosophical note, it has been a long day.)

How to add text to an image?

In my project I use iText to generate a PDF document.
Suppose that the height of a page measures 500pt (1 user unit = 1 point), and that I write some text to the page, followed by an image.
If the content and the image require less than 450pt, the text preceded the image.
If the content and the image exceed 450pt, the text is forwarded to the next page.
My question is: how can I obtain the remaining available space before writing an image?
First things first: when adding text and images to a page, iText sometimes changes the order of the textual content and the image. You can avoid this by using:
writer.setStrictImageSequence(true);
If you want to know the current position of the "cursor", you can use the method getVerticalPosition(). Unfortunately, this method isn't very elegant: it requires a Boolean parameter that will add a newline (if true) or give you the position at the current line (if false).
I do not understand why you want to get the vertical position. Is it because you want to have a caption followed by an image, and you want the caption and the image to be at the same page?
In that case, you could put your text and images inside a table cell and instruct iText not to split rows. In this case, iText will forward both text and image, in the correct order to the next page if the content doesn't fit the current page.
Update:
Based on the extra information added in the comments, it is now clear that the OP wants to add images that are watermarked.
There are two approaches to achieve this, depending on the actual requirement.
Approach 1:
The first approach is explained in the WatermarkedImages1 example. In this example, we create a PdfTemplate to which we add an image as well as some text written on top of that image. We can then wrap this PdfTemplate inside an image and add that image together with its watermark using a single document.add() statement.
This is the method that performs all the magic:
public Image getWatermarkedImage(PdfContentByte cb, Image img, String watermark) throws DocumentException {
float width = img.getScaledWidth();
float height = img.getScaledHeight();
PdfTemplate template = cb.createTemplate(width, height);
template.addImage(img, width, 0, 0, height, 0, 0);
ColumnText.showTextAligned(template, Element.ALIGN_CENTER,
new Phrase(watermark, FONT), width / 2, height / 2, 30);
return Image.getInstance(template);
}
This is how we add the images:
PdfContentByte cb = writer.getDirectContentUnder();
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE1), "Bruno"));
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE2), "Dog"));
document.add(getWatermarkedImage(cb, Image.getInstance(IMAGE3), "Fox"));
Image img = Image.getInstance(IMAGE4);
img.scaleToFit(400, 700);
document.add(getWatermarkedImage(cb, img, "Bruno and Ingeborg"));
As you can see, we have one very large image (a picture of my wife and me). We need to scale this image so that it fits the page. If you want to avoid this, take a look at the second approach.
Approach 2:
The second approach is explained in the WatermarkedImages2 example. In this case, we add each image to a PdfPCell. This PdfPCell will scale the image so that it fits the width of the page. To add the watermark, we use a cell event:
class WatermarkedCell implements PdfPCellEvent {
String watermark;
public WatermarkedCell(String watermark) {
this.watermark = watermark;
}
public void cellLayout(PdfPCell cell, Rectangle position,
PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER,
new Phrase(watermark, FONT),
(position.getLeft() + position.getRight()) / 2,
(position.getBottom() + position.getTop()) / 2, 30);
}
}
This cell event can be used like this:
PdfPCell cell;
cell = new PdfPCell(Image.getInstance(IMAGE1), true);
cell.setCellEvent(new WatermarkedCell("Bruno"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE2), true);
cell.setCellEvent(new WatermarkedCell("Dog"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE3), true);
cell.setCellEvent(new WatermarkedCell("Fox"));
table.addCell(cell);
cell = new PdfPCell(Image.getInstance(IMAGE4), true);
cell.setCellEvent(new WatermarkedCell("Bruno and Ingeborg"));
table.addCell(cell);
You will use this approach if all images have more or less the same size, and if you don't want to worry about fitting the images on the page.
Consideration:
Obviously, both approaches have a different result because of the design choice that is made. Please compare the resulting PDFs to see the difference: watermark_template.pdf versus watermark_table.pdf

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