I want to dynamically align the iText PdfTable.
How to set the x and y position based alignment in iTextPDF.
PdfPCell cell;
cell = new PdfPCell(testTable);
cell.setFixedHeight(44f);
cell.setColspan(3);
cell.setBorder(0);
table.addCell(cell);
table1.addCell(table);
please look in to this example...
public static void Main() {
// step 1: creation of a document-object
Document document = new Document();
try {
// step 2:
// we create a writer that listens to the document
// and directs a PDF-stream to a file
PdfWriter writer = PdfWriter.getInstance(document, new FileStream("Chap1002.pdf", FileMode.Create));
// step 3: we open the document
document.Open();
// step 4: we grab the ContentByte and do some stuff with it
PdfContentByte cb = writer.DirectContent;
// we tell the ContentByte we're ready to draw text
cb.beginText();
// we draw some text on a certain position
cb.setTextMatrix(100, 400);
cb.showText("Text at position 100,400.");
// we tell the contentByte, we've finished drawing text
cb.endText();
}
catch(DocumentException de) {
Console.Error.WriteLine(de.Message);
}
catch(IOException ioe) {
Console.Error.WriteLine(ioe.Message);
}
// step 5: we close the document
document.Close();
}
}
Please take a look at the C# port of the examples of chapter 4 of my book: http://tinyurl.com/itextsharpIIA2C04
You can add the table to a ColumnText object and add the column at an absolute position:
ColumnText column = new ColumnText(writer.DirectContent);
column.AddElement(table);
column.SetSimpleColumn(llx, lly, urx, ury);
column.Go();
In this snippet llx, lly and urx, ury are the coordinates of the lower-left corner and the upper-right corner of the column on the page (see the ColumnTable example).
In the PdfCalendar example, another method is used:
table.WriteSelectedRows(0, -1, x, y, writer.DirectContent);
The first parameters define which rows need to be drawn (0 to -1 means all rows), x and y define the absolute position.
Related
I am adding a rectangle on top of my page for all pages but I do not want the rectangle on the last page. Here is my code:
#Override
public void onStartPage(PdfWriter writer, Document output) {
Font bold = new Font(Font.FontFamily.HELVETICA, 16, Font.BOLD);
bold.setStyle(Font.UNDERLINE);
bold.setColor(new BaseColor(171, 75, 15));
PdfContentByte cb = writer.getDirectContent();
// Bottom left coordinates x & y, followed by width, height and radius of corners.
cb.roundRectangle(100f, 1180f, 400f, 100f, 5f);//I dont want this on the ;ast page
cb.stroke();
try {
output.add(new Paragraph("STATEMENT OF ACCOUNT", bold));
output.add(new Paragraph(new Phrase(new Chunk(" "))));
output.add(new Paragraph(new Phrase(new Chunk(" "))));
output.add(new Paragraph(new Phrase(new Chunk(" "))));
output.add(new Paragraph(new Phrase(new Chunk(" "))));
Image logo = Image.getInstance(imagepath);
logo.setAbsolutePosition(780, 1230);
logo.scaleAbsolute(200, 180);
writer.getDirectContent().addImage(logo);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
Is there a way to either skip or remove this rectangle from the last Page of the document?
First of all iText developers often have stressed that in onStartPage one MUST NOT add content to the PDF. The reason is that under certain circumstances unused pages are created and onStartPage is called for them but they then are dropped. If you add content to them in onStartPage, though, they are not dropped but remain in your document.
Thus, always use onEndPage to add any content to a page.
In your use case there is yet another reason for using onEndPage: Usually it only becomes clear that a given page is the last page when the last bit of content has been added to the document. This usually occurs after onStartPage has been called for the page but before onEndPage has.
Thus, after you've added the last bit of regular page content to the document, you can simply set a flag in the page event listener that the current page is the final document page. Now the following onEndPage call knows it processes the final page and can add content differently.
So the page event listener would look like this
class MyPageEventListener extends PdfPageEventHelper {
public boolean lastPage = false;
#Override
public void onEndPage(PdfWriter writer, Document output) {
if (!lastPage) {
[add extra content for page before the last one]
} else {
[add extra content for last page]
}
}
...
}
and be used like this
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, TARGET);
MyPageEventListener pageEventListener = new MyPageEventListener();
writer.setPageEvent(pageEventListener);
document.open();
[add all regular content to the document]
pageEventListener.lastPage = true;
document.close();
I'm using iText and create a dynamic table which has a a reoccurring header in the method createTabularHeader:
PdfPTable table = new PdfPTable(6);
// fill it with some basic information
table.setHeaderRows(1);
Yet on the first page I would like to display different information. (but the table structure/size remains the same)
Due to the dynamic content which is obtained in a different method I can't say when a new page starts.
I tried with the most primitive variant - just adding a white rectangle over the text and insert the different text. As it's just on the first page all I have to do is creating that rectangle between both methods.
But the white rectangle doesn't have any opacity and can' cover anything.
Yet by trying around I found the method writer.getDirectContent().setColorStroke(BaseColor.WHITE); which set the text to white. Later I just set the BaseColor of my cells manually to black. But the even though the new text is applied after the calling of my createTabularHeader-method its layer is under the layer of the original text and the letters are covering the new text partly.
Using the answer to How to insert invisible text into a PDF? brought me to the idea of using myPdfContentByte.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_INVISIBLE); was not so helpful as it resets only on the 2nd page regardless what I do and the regular text on the first page stays invisible.
I'm unable to find a proper solution... How can the table-header be modified only on the first page?
The solution is not really nice, but works... and as some sort of bonus I want to add how you can modify the indentions on the first page.
public void createPdf() {
document = new Document();
try {
PdfWriter writer = PDFHead.getWriter(document);
//If it's a letter we have a different indention on the top
if (letterPDF) {
document.setMargins(36, 36, 100, 36);
} else {
document.setMargins(36, 36, 36, 36);
}
document.open();
document.add(createTabularContent());
document.close();
} catch (DocumentException | FileNotFoundException ex) {
try {
document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(FILENAME));
document.open();
document.add(new Phrase(ex.getLocalizedMessage()));
document.close();
Logger.getLogger(Etikette.class.getName()).log(Level.SEVERE, null, ex);
} catch (FileNotFoundException | DocumentException ex1) {
Logger.getLogger(Etikette.class.getName()).log(Level.SEVERE, null, ex1);
}
}
}
The PDFHead is used to create a regular header (the one which appears on every page, not only on pages with the table):
public static PdfWriter getWriter(Document document) throws FileNotFoundException, DocumentException {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
HeaderFooter event = new HeaderFooter("Ing. Mario J. Schwaiger", type + " " + DDMMYYYY.format(new java.util.Date()), 835, isLetterPDF(), customerNumber);
writer.setBoxSize("art", new Rectangle(36, 54, 559, 788));
writer.setPageEvent(event);
return writer;
}
And in that HeaderFooter-Event I use the fact the function is called after the PDF is basically created (for the page number for instance):
#Override
public void onEndPage(PdfWriter writer, Document document) {
if (isLetter) {
//That's only for the first page, apparently 1 is too late
//I'm open for improvements but that works fine for me
if (writer.getPageNumber() == 0) {
//If it's a letter we use the different margins
document.setMargins(36, 36, 100, 36);
}
if (writer.getPageNumber() == 1) {
PdfContentByte canvas = writer.getDirectContent();
float llx = 460;
float lly = 742;
float urx = 36;
float ury = 607;
//As I add the rectangle in the event here it's
//drawn over the table-header. Seems the tableheader
//is rendered afterwards
Rectangle rect1 = new Rectangle(llx, lly, urx, ury);
rect1.setBackgroundColor(BaseColor.WHITE);
rect1.setBorder(Rectangle.NO_BORDER);
rect1.setBorderWidth(1);
canvas.rectangle(rect1);
ColumnText ct = new ColumnText(canvas);
ct.setSimpleColumn(rect1);
PdfPTable minitable = new PdfPTable(1);
PdfPCell cell = PDFKopf.getKundenCol(PDFHeader.getCustomer(customerNumber));
cell.setBorder(Rectangle.NO_BORDER);
minitable.addCell(cell);
//A single cell is not accepted as an "Element"
//But a table including only a single cell is
ct.addElement(minitable);
try {
ct.go();
} catch (DocumentException ex) {
Logger.getLogger(HeaderFooter.class.getName()).log(Level.SEVERE, null, ex);
}
//In any other case we reset the margins back to normal
//This could be solved in a more intelligent way, feel free
} else {
document.setMargins(36, 36, 36, 36);
}
}
//The regular header of any page...
PdfPTable table = new PdfPTable(4);
try {
table.setWidths(new int[]{16, 16, 16, 2});
table.setWidthPercentage(100);
table.setTotalWidth(527);
table.setLockedWidth(true);
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.BOTTOM);
table.addCell(header);
PdfPCell cell;
cell = new PdfPCell(new Phrase(mittelteil));
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
cell.setBorder(Rectangle.BOTTOM);
table.addCell(cell);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
table.addCell(String.format("Page %d of ", writer.getPageNumber()));
cell = new PdfPCell(Image.getInstance(total));
cell.setBorder(Rectangle.BOTTOM);
table.addCell(cell);
table.writeSelectedRows(0, -1, 34, y, writer.getDirectContent());
} catch (DocumentException de) {
throw new ExceptionConverter(de);
}
}
I got a problem I do not really know how to proceed with... When I draw a rectangle on a page (the blue one in the picture) and then draw the same rectangle on a template on the same page (the green one), the rectangle on the template is larger. Has anybody a clue WHY?
Run the following class:
public class RectangleTemplate {
public static void main(String[] args){
try {
File file = new File("rectagnleTemplate_" + System.currentTimeMillis() + ".pdf");
FileOutputStream fileout = new FileOutputStream(file);
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, fileout);
document.open();
Rectangle rectangleOnPage = new Rectangle(20, 20, 100, 100);
rectangleOnPage.setBorderColor(BaseColor.BLUE);
rectangleOnPage.setBorder(Rectangle.BOX);
rectangleOnPage.setBorderWidth(2);
PdfContentByte canvas = writer.getDirectContent();
canvas.rectangle(rectangleOnPage);
canvas.stroke();
PdfTemplate template = canvas.createTemplate(document.getPageSize().getWidth(), document.getPageSize()
.getHeight());
template.rectangle(rectangleOnPage.getLeft(), rectangleOnPage.getBottom(), rectangleOnPage.getRight(),
rectangleOnPage.getTop());
template.setColorFill(BaseColor.GREEN
);
template.fill();
template.stroke();
canvas.addTemplate(template, -10,-10);
canvas.sanityCheck();
canvas.stroke();
document.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
the green rectangle should be as large as the blue one:
This is indeed confusing: iText 1, 2 and 5 grew organically: different people contributed code and as a result, there's a difference between the way a Rectangle object is created and the way you define a rectangle using the rectangle() method.
Take a look at the API docs:
The Rectangle constructor looks like this:
public Rectangle(float llx, float lly, float urx, float ury)
You need the coordinates of the lower-left and the upper-right corner.
The rectangle method looks like this:
public void rectangle(float x, float y, float w, float h)
In this case, you only pass the coordinate of the lower-left corner. The other two parameters are the width and the height.
This line in your code is wrong:
template.rectangle(
rectangleOnPage.getLeft(), rectangleOnPage.getBottom(),
rectangleOnPage.getRight(), rectangleOnPage.getTop());
It should be:
template.rectangle(
rectangleOnPage.getLeft(), rectangleOnPage.getBottom(),
rectangleOnPage.getWidth(), rectangleOnPage.getHeight());
This problem is fixed in iText 7. iText 7 is a complete rewrite of iText by a coordinated team.
I'm using iText to create a PDF with a table. The table headers have 90 degree rotated text, which I'm adding using a CellEvent (code below). This works great, except when the table spans multiple pages the rotated cell header text flows off the top of the page.
I've tried setting cell.setFixedHeight(100) but it doesn't seem to affect the cell. I've tried this solution as well but I can't get the cell to display the resulting image with text at all.
#Override
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
PdfContentByte canvas = canvases[PdfPTable.TEXTCANVAS];
try {
canvas.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, false), this.fontSize);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
if (this.alignment == PdfPCell.ALIGN_CENTER) {
this.left = ((position.getRight() - position.getLeft()) / 2 );
}
else if (this.alignment == PdfPCell.ALIGN_MIDDLE) {
this.top = ((position.getTop() - position.getBottom()) / 2 );
}
canvas.showTextAligned(this.alignment, this.text, position.getLeft() + this.left, position.getTop() - this.top, this.rotation);
}
Here's what the cell header overflow looks like. In this example it should have the month and year (Mar 2016) displayed.
I'd like to have the cell to be arbitrary height dependent on the actual header text being used. Any ideas on how to solve this?
A cell event is triggered after a cell is drawn. You might already have suspected that much as iText passes a Rectangle object with the position to the cellLayout method. A PdfPCell object is passed, but it is to be used for read-only purposes only. As the position is fixed, you can't use the setFixedHeight() on it.
Looking at the screen shot, I am puzzled: why are you using a cell event to add content that is rotated by 90 degrees? The solution to your problem would be to use the setRotation() method:
PdfPCell cell = new PdfPCell(new Phrase("May 16, 2016"));
cell.setRotation(90);
Now the content will be rotated and the size of the cell will be adapted to the content. Please take a look at the RotatedCell example:
public void createPdf(String dest) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
PdfPTable table = new PdfPTable(8);
for (int i = 0; i < 8; i++) {
PdfPCell cell =
new PdfPCell(new Phrase(String.format("May %s, 2016", i + 15)));
cell.setRotation(90);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
table.addCell(cell);
}
for(int i = 0; i < 16; i++){
table.addCell("hi");
}
document.add(table);
document.close();
}
The result looks like this: rotated_cell.pdf
Note that the concepts horizontal and vertical are rotated to. If you want to center the rotated content horizontally, you have to center the vertical alignment for the content and the rotate the aligned content.
I'm using iText to create barcodes on a PDF with the same format as this one:
The problem is the the left number, the first zero digits must be smaller, while the rest of the numbers must also be bold. "T.T.C." also has to be even smaller (it doesn't have to be on another line).
I was able to rotate the number with the following code:
String price = "23000 T.T.C.";
PdfContentByte cb = docWriter.getDirectContent();
PdfTemplate textTemplate = cb.createTemplate(50, 50);
ColumnText columnText = new ColumnText(textTemplate);
columnText.setSimpleColumn(0, 0, 50, 50);
columnText.addElement(new Paragraph(price));
columnText.go();
Image image;
image = Image.getInstance(textTemplate);
image.setAlignment(Image.MIDDLE);
image.setRotationDegrees(90);
doc.add(image);
The problem is that I cannot find a way online to change the font of certain characters of the String price when it is printed on the PDF.
I have created a small Proof of Concept that results in a PDF that looks like this:
As you can see, it has text in different sizes and styles. It also has a bar code that is rotated.
Take a look at the RotatedText example:
public void createPdf(String dest) throws IOException, DocumentException {
// step 1
Document document = new Document(new Rectangle(60, 120), 5, 5, 5, 5);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
// step 3
document.open();
// step 4
PdfContentByte canvas = writer.getDirectContent();
Font big_bold = new Font(FontFamily.HELVETICA, 12, Font.BOLD);
Font small_bold = new Font(FontFamily.HELVETICA, 6, Font.BOLD);
Font regular = new Font(FontFamily.HELVETICA, 6);
Paragraph p1 = new Paragraph();
p1.add(new Chunk("23", big_bold));
p1.add(new Chunk("000", small_bold));
document.add(p1);
Paragraph p2 = new Paragraph("T.T.C.", regular);
p2.setAlignment(Element.ALIGN_RIGHT);
document.add(p2);
BarcodeEAN barcode = new BarcodeEAN();
barcode.setCodeType(Barcode.EAN8);
barcode.setCode("12345678");
Rectangle rect = barcode.getBarcodeSize();
PdfTemplate template = canvas.createTemplate(rect.getWidth(), rect.getHeight() + 10);
ColumnText.showTextAligned(template, Element.ALIGN_LEFT,
new Phrase("DARK GRAY", regular), 0, rect.getHeight() + 2, 0);
barcode.placeBarcode(template, BaseColor.BLACK, BaseColor.BLACK);
Image image = Image.getInstance(template);
image.setRotationDegrees(90);
document.add(image);
Paragraph p3 = new Paragraph("SMALL", regular);
p3.setAlignment(Element.ALIGN_CENTER);
document.add(p3);
// step 5
document.close();
}
This example solves all of your issues:
You want a Paragraph to use different fonts: compose a Paragraph using different Chunk objects.
You want to add extra text on top of a bar code: add the bar code to a PdfTemplate and add the extra text using ColumnText.showTextAligned() (not that you can also compose a Phrase using different Chunk objects if you need more than one font in that extra text).
You want to rotate the bar code: wrap the PdfTemplate inside an Image object and rotate the image.
You can check the result: rotated_text.pdf
I hope this helps.