I am using itextsharp v 5.5.13 to create PDF documents.
One scenario where I need help is where the PDF document has a structure (header, body and footer like a template). The idea is to populate data only in the body section. Whenever there is overflow of data to next page, the data goes into the body of the next page maintaining the same structure (i.e. with header and footer).
Currently, I am able to achieve the same by creating pageeventhelper where on page start and page end, I am adding the header and footer. With this approach, I am able to get the desired result but it appears to be little hacky, and the appearance is also not that great (white spaces , gaps etc).
Any suggestions of a better way to handle this use case?
The code looks like as follows,
in the PdfPageEventHelper i have two different headers for page 1 and page 2, and a footer,
protected PdfPTable primaryHeader;
protected PdfPTable secondaryHeader;
protected PdfPTable footer;
public void setPrimaryHeader(PdfPTable header)
{
this.primaryHeader = header;
}
public void setSecondaryHeader(PdfPTable header)
{
this.secondaryHeader = header;
}
public void setFooter(PdfPTable footer)
{
this.footer = footer;
}
public override void OnStartPage(PdfWriter writer, Document document)
{
base.OnStartPage(writer, document);
if (writer.PageNumber == 1)
{
this.primaryHeader.TotalWidth = document.Right - document.Left;
this.primaryHeader.WriteSelectedRows(0, -1, 10, document.Top + 40, writer.DirectContent);
// Adding extra space to remove overlap of text
document.Add(PdfHelper.GetEmptyTable(200f));
}
else
{
this.secondaryHeader.TotalWidth = document.Right - document.Left;
this.secondaryHeader.WriteSelectedRows(0, -1, 10, document.Top + 40, writer.DirectContent);
// Adding extra space to remove overlap of text
document.Add(PdfHelper.GetEmptyTable(60f));
}
}
public override void OnEndPage(PdfWriter writer, Document document)
{
base.OnEndPage(writer, document);
this.footer.TotalWidth = document.Right - document.Left;
this.footer.WriteSelectedRows(0, -1, 10, document.Bottom + 10, writer.DirectContent);
}
The following code creates the byte array pdf,
using (MemoryStream stream = new MemoryStream())
{
Document document = new Document(PageSize.A4, 10, 10, 42, 35);
PdfWriter writer = PdfWriter.GetInstance(document, stream);
CustomPageEventHelper peh = new CustomPageEventHelper(true);
peh.setPrimaryHeader(GetPrimaryHeader(response, identity));
peh.setSecondaryHeader(GetSecondaryHeader(response, identity));
peh.setFooter(GetFooter(response, identity));
writer.PageEvent = peh;
document.Open();
PdfPTable table = null;
PdfPTable tableOuter = null;
PdfPTable tableInner = null;
PdfPTable tableGrid = null;
PdfPCell cell = null;
// Adding content here via PdfPTable's
document.Close();
result = stream.ToArray();
}
The following is the output,
Related
I am using iTextPDF to generate PDFs getting data from some text inputs.
When I run the application and create first PDF, it is generated as expected.
Then I change some values and generate another, this is where problem arises. The last entry displayed on first PDF is printed on top of the first entry of the second generated PDF.
Not sure why this is happening? Is it being saved to a buffer or something, not really sure.
Here is the code for generating PDF:
public class ExportTicket implements Action{
PdfPCell titleCell = new PdfPCell();
PdfPCell contentCell = new PdfPCell();
public String performAction(HttpServletRequest request) throws PewException {
// CREATING DOCUMENT (ITEXTPDF)
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("Ticket_" + ticketNo + ".pdf"));
// Fonts
Font headingFont = new Font(Font.FontFamily.UNDEFINED, 10, Font.BOLD, BaseColor.BLACK);
// Open Document to Write
document.open();
// Table Creation
PdfPTable table = new PdfPTable(2);
table.setTotalWidth(200);
table.setWidths(new int[]{ 5, 10 });
table.setHorizontalAlignment(Element.ALIGN_LEFT);
// Add Ticket Number
contentCell.addElement(new Chunk("Ticket Number: " + ticketNo, headingFont));
contentCell.setColspan(2);
table.addCell(contentCell);
// Add table to Document & Close Document
document.add(table)
document.close();
}
}
Please see attached images for output, First one displays first file generation and second one displays 2nd File generation,
First Generated PDF File for Ticket Number: 20170034
Second Generated PDF File for Ticket Number: 20170035
You have strange priorities. You think you should save processing time by creating a PdfPCell only once (in spite of the fact that you always need a new instance), but you waste processing time by creating the font over and over again (while you could easily reuse it).
This is an improved version of your class (I assumed that you get the ticketNo from the request):
public class ExportTicket implements Action{
// Fonts
Font headingFont = new Font(Font.FontFamily.UNDEFINED, 10, Font.BOLD, BaseColor.BLACK);
public String performAction(HttpServletRequest request) throws PewException {
String ticketNo = request.getParameter("ticketNo");
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream("Ticket_" + ticketNo + ".pdf"));
// Open Document to Write
document.open();
// Table Creation
PdfPTable table = new PdfPTable(2);
table.setTotalWidth(200);
table.setWidths(new int[]{ 5, 10 });
table.setHorizontalAlignment(Element.ALIGN_LEFT);
// Add Ticket Number
PdfPCell contentCell = new PdfPCell()
contentCell.addElement(new Chunk("Ticket Number: " + ticketNo, headingFont));
contentCell.setColspan(2);
table.addCell(contentCell);
// Add table to Document & Close Document
document.add(table)
document.close();
}
}
Could it be because you never reset 'contentCell'?
Or is it a value wich is resetted?
I have written the code to generate the pdf file in server machine, but my requirement is that when user clicks on some button/url on the site in that case he should be able to see that pdf file in new tab of the browser or should be downloaded in his machine directly.
Here is the code which I have written
#RequestMapping(value = "/downloadPDF", method = RequestMethod.POST)
public void downloadPDF()
throws FileNotFoundException, DocumentException {
final String DEST = "column_width_example.pdf";
StringBuilder sb = new StringBuilder();
//Below method returns the data which will be convert in table format for pdf file
String dataForPDFFile = rowForUserCompetency(sb, "name");
Document document = new Document(PageSize.A4.rotate());
PdfWriter.getInstance(document, new FileOutputStream(DEST));
document.open();
float[] columnWidths = { 2.5f, 2, 2, 1, 2, 2, 3 };
String[] lines = dataForPDFFile.toString().split("\\n");
Font fontRow = new Font(Font.FontFamily.TIMES_ROMAN, 10, Font.NORMAL);
PdfPTable table = new PdfPTable(columnWidths);
table.setWidthPercentage(100);
table.getDefaultCell().setUseAscender(true);
table.getDefaultCell().setUseDescender(true);
for (String string : lines) {
String[] cellData = string.toString().split(",");
for (String header : cellData) {
PdfPCell cell = new PdfPCell();
cell.setPhrase(new Phrase(header.toUpperCase(), fontRow));
table.addCell(cell);
}
table.completeRow();
}
document.add(table);
document.close();
}
And this code is generating the pdf file on that server so normal user won't be able to see the file in his machine, so can someone guide me what should be the changes in above code.
You need to manage the HTTP response.
public void downloadPDF(HttpServletResponse response) {
// Set the headers for downloading or opening your document
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=\"user_file_name.pdf\"");
...
// Write directly the output stream.
PdfWriter.getInstance(document, response.getOutputStream());
...
// Build your document as ever.
}
I'm creating a pdf with iText 5 and want to add a footer. I did everything like the book "iText in action" in Chapter 14 says.
There are no errors but the footer doesn't show up.
Can somebody tell me what I'm doing wrong?
My code:
public class PdfBuilder {
private Document document;
public void newDocument(String file) {
document = new Document(PageSize.A4);
writer = PdfWriter.getInstance(document, new FileOutputStream(file));
MyFooter footerEvent = new MyFooter();
writer.setPageEvent(footerEvent);
document.open();
...
document.close();
writer.flush();
writer.close();
}
class MyFooter extends PdfPageEventHelper {
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer(), (document.right() - document.left()) / 2
+ document.leftMargin(), document.top() + 10, 0);
}
private Phrase footer() {
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
Phrase p = new Phrase("this is a footer");
return p;
}
}
The problem you report can not be reproduced. I have taken your example and I create the TextFooter example with this event:
class MyFooter extends PdfPageEventHelper {
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
Phrase header = new Phrase("this is a header", ffont);
Phrase footer = new Phrase("this is a footer", ffont);
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
header,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.top() + 10, 0);
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER,
footer,
(document.right() - document.left()) / 2 + document.leftMargin(),
document.bottom() - 10, 0);
}
}
Note that I improved the performance by creating the Font and Paragraph instance only once. I also introduced a footer and a header. You claimed you wanted to add a footer, but in reality you added a header.
The top() method gives you the top of the page, so maybe you meant to calculate the y position relative to the bottom() of the page.
There was also an error in your footer() method:
private Phrase footer() {
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
Phrase p = new Phrase("this is a footer");
return p;
}
You define a Font named ffont, but you don't use it. I think you meant to write:
private Phrase footer() {
Font ffont = new Font(Font.FontFamily.UNDEFINED, 5, Font.ITALIC);
Phrase p = new Phrase("this is a footer", ffont);
return p;
}
Now when we look at the resulting PDF, we clearly see the text that was added as a header and a footer to each page.
By using showTextAligned method of PdfContentByte We can add footer to our page. Instead of phrase we should pass footer content as string to showTextAligned method as one of the parameter. If you want to format your footer content do before passing it to the method. Below is the sample code.
PdfContentByte cb = writer.getDirectContent();
cb.showTextAligned(Element.ALIGN_CENTER, "this is a footer", (document.right() - document.left()) / 2 + document.leftMargin(), document.bottom() - 10, 0);
Hi When I am generating form using java with itext I want to add form number on top left of the document
above the header.Please let me know the ways to do it.
PdfPTable table = new PdfPTable(3); // 3 columns.
table.setWidthPercentage(100);
PdfPCell cell1 = new PdfPCell(new Paragraph("Cell 1"));
PdfPCell cell2 = new PdfPCell(new Paragraph("Cell 2"));
PdfPCell cell3 = new PdfPCell(new Paragraph("Cell 3"));
cell1.setBorder(0);
cell2.setBorder(0);
cell3.setBorder(0);
table.addCell(cell1);
table.addCell(cell2);
table.addCell(cell3);
How can I set the table alignment to start of the page margin.
Your question is very confusing. You say you are creating a form, but when you say form, you don't seem to be referring to an interactive form, but to an ordinary PDF containing a table.
You say you want to add a number above the header, but you are not telling us what you mean by header. You are assuming that the people reading your question can read your mind.
I guess you want to use a page event to add a String in the top left corner of each page. That would make your question almost a duplicate of itextsharp: How to generate a report with dynamic header in PDF using itextsharp?
You can create a subclass of PdfPageEventHelper like this:
public class Header extends PdfPageEventHelper {
protected Phrase header;
public void setHeader(Phrase header) {
this.header = header;
}
#Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte canvas = writer.getDirectContentUnder();
ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, header, 36, 806, 0);
}
}
You can then use this Header class like this:
public void createPdf(String filename) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
Header event = new Header();
writer.setPageEvent(event);
// step 3
document.open();
// step 4
List<Integer> factors;
for (int i = 2; i < 301; i++) {
factors = getFactors(i);
if (factors.size() == 1) {
document.add(new Paragraph("This is a prime number!"));
}
for (int factor : factors) {
document.add(new Paragraph("Factor: " + factor));
}
event.setHeader(new Phrase(String.format("THE FACTORS OF %s", i)));
document.newPage();
}
// step 5
document.close();
}
In your case, you wouldn't have:
event.setHeader(new Phrase(String.format("THE FACTORS OF %s", i)));
You'd have something like:
event.setHeader(new Phrase(number));
Where number is the number you want to add at the coordinate x = 36, y = 806.
i am creating one pdf output using iText 5.0.5.
I am reading the data in the form of bytes from database and then adding it to the document using HTMLWorker to generate pdf.
BUt i am not able to add header on each paage for that pdf document.
please help.
1) The latest iText is 5.0.6.
2) To create a page header and footer, you need to use the PdfPageEvent interface. This is generally done by deriving from PdfPageEventHelper, and overriding just the methods you need.
In the PdfPageEvent, you must draw to the PDF using a PdfContentByte. The good news is that you can use a ColumnText to add aligned text into a given bounding box, and it'll handle line breaks for you.
public class HeaderFooterPageEvent extends PdfPageEventHelper {
private String headerStr, footerStr;
private Rectangle hBox, fBox;
public HeaderFooterPageEvent(String hStr, Rectangle _hBox, String fString, Rectangle _fBox) {
headerStr = hStr;
hBox = _hBox;
footerStr = fStr;
fBox = _fBox;
}
public onEndPage(PdfWriter writer, Document doc) {
// draw the header text.
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT, headerStr, hBox.getRight(), hBox.getTop, 0);
// draw the footer text.
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_RIGHT, footerStr, fBox.getRight(), fBox.getTop, 0);
}
}
This won't work so well if your header & footer are in HTML. For that you'll have to do some round-about hackery.
1) Create a new Document/PdfWriter with page margins matching the size (height & width) of your header.
2) Render all the header HTML into that page.
3) save the pdf.
4) Import that PDF page into your other document, and draw it thusly:
public onEndPage(PdfWriter writer, Document doc) {
PdfReader reader = new PdfReader(headerPDFPath);
PdfImportedPage headerPageImport = writer.getImportedPage(reader, 1); // 1 -> first page
PdfContentByte cb = writer.getDirectContent();
cb.addTemplate(headerPageImport, hBox.bottom(), hBox.left());
}