I have two parts to my java project.
I need to populate the fields of a pdf
I need to add a table below the populated section on the blank area of the page (and this table needs to be able to rollover to the next page).
I am able to do these things separately (populate the pdf and create a table). But I cannot effectively merge them. I have tried doing a doc.add(table) which will result in the table being on the next page of the pdf, which I don't want.
I essentially just need to be able to specify where the table starts on the page (so it wouldn't overlap the existing content) and then stamp the table onto the existing pdf.
My other option if this doesn't work is trying to add fields to the original pdf that will be filled by the table contents (so it will instead be a field-based table).
Any suggestions?
EDIT:
I'm new to iText and have not used columntext before, but I'm trying to test it out in the following code but the table is not being displayed. I looked at other columntext examples and I have not seen exactly where the columntext is added back into the pdf.
//CREATE FILLED FORM PDF
PdfReader reader = new PdfReader(sourcePath);
PdfStamper pdfStamper = new PdfStamper(reader, new FileOutputStream(destPath));
pdfStamper.setFormFlattening(true);
AcroFields form = pdfStamper.getAcroFields();
form.setField("ID", "99999");
form.setField("ADDR1", "425 Test Street");
form.setField("ADDR2", "Test, WA 91334");
form.setField("PHNBR", "(999)999-9999");
form.setField("NAME", "John Smith");
//CREATE TABLE
PdfPTable table = new PdfPTable(3);
Font bfBold12 = new Font(FontFamily.HELVETICA, 12, Font.BOLD, new BaseColor(0, 0, 0));
insertCell(table, "Table", Element.ALIGN_CENTER, 1, bfBold12);
table.completeRow();
ColumnText column = new ColumnText(pdfStamper.getOverContent(1));
column.addElement(table);
pdfStamper.close();
reader.close();
Please take a look at the AddExtraTable example. It's a simplification of the AddExtraPage example written in answer to the question How to continue field output on a second page?
That question is almost an exact duplicate of your question, with as only difference the fact that your requirement is easier to achieve.
I simplified the code like this:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
Rectangle pagesize = reader.getPageSize(1);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
form.setField("Name", "Jennifer");
form.setField("Company", "iText's next customer");
form.setField("Country", "No Man's Land");
PdfPTable table = new PdfPTable(2);
table.addCell("#");
table.addCell("description");
table.setHeaderRows(1);
table.setWidths(new int[]{ 1, 15 });
for (int i = 1; i <= 150; i++) {
table.addCell(String.valueOf(i));
table.addCell("test " + i);
}
ColumnText column = new ColumnText(stamper.getOverContent(1));
Rectangle rectPage1 = new Rectangle(36, 36, 559, 540);
column.setSimpleColumn(rectPage1);
column.addElement(table);
int pagecount = 1;
Rectangle rectPage2 = new Rectangle(36, 36, 559, 806);
int status = column.go();
while (ColumnText.hasMoreText(status)) {
status = triggerNewPage(stamper, pagesize, column, rectPage2, ++pagecount);
}
stamper.setFormFlattening(true);
stamper.close();
reader.close();
}
public int triggerNewPage(PdfStamper stamper, Rectangle pagesize, ColumnText column, Rectangle rect, int pagecount) throws DocumentException {
stamper.insertPage(pagecount, pagesize);
PdfContentByte canvas = stamper.getOverContent(pagecount);
column.setCanvas(canvas);
column.setSimpleColumn(rect);
return column.go();
}
As you can see, the main differences are:
We create a rectPage1 for the first page and a rectPage2 for page 2 and all pages that follow. That's because we don't need a full page on the first page.
We don't need to load a PdfImportedPage, instead we're just adding blank pages of the same size as the first page.
Possible improvements: I hardcoded the Rectangle instances. It goes without saying that rect1Page depends on the location of your original form. I also hardcoded rect2Page. If I had more time, I would calculate rect2Page based on the pagesize value.
See the following questions and answers of the official FAQ:
How to add a table on a form (and maybe insert a new page)?
How to continue field output on a second page?
Related
I am facing the following problem for which i haven't found any solution yet. I am implementing a platform for a medical laboratory. They want for every incident to write the report to the system and then generate and print it from the system. I am using itext 7 to accomplish this. However i am facing the following problem.
They have a very strange template. On the first page in the beginning they want to print a specific table, while in the beginning of every other page they want to print something else. So i need to know when pages change in order to print in the beginning of the page the corresponding table.
After reading various sources i ended up creating the first page normally and then adding a header event handler that checks the page number and gets executed always except page 1.
public class VariableHeaderEventHandler implements IEventHandler {
#Override
public void handleEvent(Event event) {
System.out.println("THIS IS ME: HEADER EVENT HANDLER STARTED.....");
PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = documentEvent.getDocument();
PdfPage page = documentEvent.getPage();
Rectangle pageSize = page.getPageSize();
int pageNumber = pdfDoc.getPageNumber(page);
if (pageNumber == 1) return; //Do nothing in the first page...
System.out.println("Page size: " + pageSize.getHeight());
Rectangle rectangle = new Rectangle(pageSize.getLeft() + 30, pageSize.getHeight()-234, pageSize.getWidth() - 60, 200);
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
pdfCanvas.rectangle(rectangle);
pdfCanvas.setFontAndSize(FontsAndStyles.getRegularFont(), 10);
Canvas canvas = new Canvas(pdfCanvas, pdfDoc, rectangle);
Div header = new Div();
Paragraph paragraph = new Paragraph();
Text text = new Text("Διαγνωστικό Εργαστήριο Ιστοπαθολογίας και Μοριακής Παθολογοανατομικής").addStyle(FontsAndStyles.getBoldStyle());
paragraph.add(text);
paragraph.add(new Text("\n"));
text = new Text("Μοριακή Διάγνωση σε Συνεργασία με").addStyle(FontsAndStyles.getBoldStyle());
paragraph.add(text);
paragraph.add(new Text("\n"));
text = new Text("Γκούρβας Βίκτωρας, M.D., Ph.D.").addStyle(FontsAndStyles.getBoldStyle());
paragraph.add(text);
paragraph.add(new Text("\n"));
text = new Text("Τσιμισκή 33, Τ.Κ. 54624, ΘΕΣΣΑΛΟΝΙΚΗ").addStyle(FontsAndStyles.getNormalStyle());
paragraph.add(text);
paragraph.add(new Text("\n"));
text = new Text("Τήλ/Φάξ: 2311292924 Κιν.: 6932104909 e-mail: vgourvas#gmail.com").addStyle(FontsAndStyles.getNormalStyle());
paragraph.add(text);
header.add(paragraph);
// =============Horizontal Line BOLD============
SolidLine solidLine = new SolidLine((float) 1.5);
header.add(new LineSeparator(solidLine));
// ========Horizontal Line BOLD End==========
text = new Text("ΠΑΘΟΛΟΓΟΑΝΑΤΟΜΙΚΗ ΕΞΕΤΑΣΗ").addStyle(FontsAndStyles.getBoldStyle());
paragraph = new Paragraph().add(text);
header.add(paragraph);
header.setTextAlignment(TextAlignment.CENTER);
canvas.add(header);
canvas.close();
}
However the problem i am facing now is that header overlaps content and i can't figure out how to set different margins per page. For example form page 2 and beyond i would like different topMargin.
Has anyone faced these problems before and have found a working solution? Am I implementing correct? Is there a better way of accomplishing the same result?
Thanks in advance,
Toutoudakis Michail
You should create your own custom document renderer and decrease the area which would be used to place content for each page except for the first one.
Please look at the snippet below and updateCurrentArea method in particular.
class CustomDocumentRenderer extends DocumentRenderer {
public CustomDocumentRenderer(Document document) {
super(document);
}
#Override
public IRenderer getNextRenderer() {
return new CustomDocumentRenderer(this.document);
}
#Override
protected LayoutArea updateCurrentArea(LayoutResult overflowResult) {
LayoutArea area = super.updateCurrentArea(overflowResult);
if (currentPageNumber > 1) {
area.setBBox(area.getBBox().decreaseHeight(200));
}
return area;
}
}
Then just set the renderer on your document:
Document doc = new Document(pdfDoc);
doc.setRenderer(new CustomDocumentRenderer(doc));
The resultant pdf which I get for your document looks as follows:
There is another solution however. Once you've added at least one element to your document, you can change the default document's margins. The change will be applied on all pages created afterwards (and in your case these are pages 2, 3, ...)
doc.add(new Paragraph("At least one element should be added. Otherwise the first page wouldn't be created and changing of the default margins would affect it."));
doc.setMargins(200, 36, 36, 36);
// now you can be sure that all the next pages would have new margins
I have a pdf which is half static and half dynamic which can grow multiple pages. I created the static part in Adobe LiveCycle and using itext to create the dynamic part. The dynamic part of the form is a table which has to expand based on the input across multiple pages. The form has acrofields in both the parts.
I have used columntext and pdfstamper to add the content to the existing pdf and the table grows dynamically which is working fine. The problems are
In every table cell, an acrofield needs to get added. I used pdfcell event to create it but after some googling, I could only find code using pdfwriter but not using pdf stamper.
on the first page, how to restrict the table content so that it doesn't go till the end of the page and I can insert page numbers at the bottom ?
I need to add a signature field at the end of the table. how do I know the coordinates of the end of the dynamic table?
My code snippet for dynamic table part:
ColumnText column = new ColumnText(stamper.getOverContent(1));
Rectangle rectPage1 = new Rectangle(792, 270);
column.setSimpleColumn(rectPage1);
column.addElement(table);
int pagecount = 1;
Rectangle rectPage2 = new Rectangle(792, 540);
int status = column.go();
while (ColumnText.hasMoreText(status) ) {
status = triggerNewPage(stamper, pagesize, column, rectPage2, ++pagecount);
}
public int triggerNewPage(PdfStamper stamper, Rectangle pagesize, ColumnText column, Rectangle rect, int pagecount) throws DocumentException {
stamper.insertPage(pagecount, pagesize);
PdfContentByte canvas = stamper.getOverContent(pagecount);
column.setCanvas(canvas);
column.setSimpleColumn(rect);
return column.go();
}
I need to make a PDF page that looks something like this:
I'm having problems to make two columns that fit on a small sized page.
This is my code:
public void createSizedPdf(String dest) throws IOException, DocumentException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
document.setMargins(5,5,5,5);
Rectangle one = new Rectangle(290,100);
one.setBackgroundColor(Color.YELLOW);
document.setPageSize(one);
document.open();
Paragraph consigneeName = new Paragraph("Ahmed");
Paragraph address = new Paragraph("Casa ST 121");
String codeBL = "14785236987541";
PdfContentByte cb = writer.getDirectContent();
Barcode128 code128 = new Barcode128();
code128.setBaseline(9);
code128.setSize(9);
code128.setCode(codeBL);
code128.setCodeType(Barcode128.CODE128);
Image code128Image = code128.createImageWithBarcode(cb, null, null);
Paragraph right = new Paragraph();
right.add(consigneeName);
right.add(address);
right.add(code128Image);
Chunk glue = new Chunk(new VerticalPositionMark());
Paragraph p = new Paragraph();
p.add(right);
p.add(new Chunk(glue));
p.add(code128Image);
document.add(p);
document.close();
}
One way to solve your problem, would be to create a template PDF with AcroForm fields. You could create a nice design manually, and then fill out the form programmatically by putting data (text, bar codes) at the appropriate places defined by the fields that act as placeholders.
Another way, is to create the PDF from scratch, which is the approach you seem to have taken, looking at your code.
Your question isn't entirely clear, in the sense that you share your code, but you don't explain the problem you are experiencing. As I already commented:
are you unable to scale images? are you unable to define a smaller
font size? are you unable to create a table with specific dimension?
You say I'm having problems to make two columns that fits in a small
sized page but you forgot to describe the problems.
You didn't give an answer to those questions, and that makes it very hard for someone to answer your question. The only thing a Stack Overflow reader could do, is to do your work in your place. That's not what Stack Overflow is for.
Moreover, the answer to your question is so trivial that it is hard for a Stack Overflow reader to understand why you posted a question.
You say you need to add data (text and bar codes) in two columns, you are actually saying that you want to create a table. This is an example of such a table:
If you look at the SmallTable example, you can see how it's built.
You want a PDF that measures 290 by 100 user units, with a margin of 5 user units on each side. This means that you have space for a table measuring 280 by 90 user units. Looking at your screen shot, I'd say that you have a column of 160 user units with and a column of 120 user units. I'd also say that you have three rows of 30 user units high each.
OK, then why don't you create a table based on those dimensions?
public void createPdf(String dest) throws IOException, DocumentException {
Rectangle small = new Rectangle(290,100);
Font smallfont = new Font(FontFamily.HELVETICA, 10);
Document document = new Document(small, 5, 5, 5, 5);
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
document.open();
PdfPTable table = new PdfPTable(2);
table.setTotalWidth(new float[]{ 160, 120 });
table.setLockedWidth(true);
PdfContentByte cb = writer.getDirectContent();
// first row
PdfPCell cell = new PdfPCell(new Phrase("Some text here"));
cell.setFixedHeight(30);
cell.setBorder(Rectangle.NO_BORDER);
cell.setColspan(2);
table.addCell(cell);
// second row
cell = new PdfPCell(new Phrase("Some more text", smallfont));
cell.setFixedHeight(30);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setBorder(Rectangle.NO_BORDER);
table.addCell(cell);
Barcode128 code128 = new Barcode128();
code128.setCode("14785236987541");
code128.setCodeType(Barcode128.CODE128);
Image code128Image = code128.createImageWithBarcode(cb, null, null);
cell = new PdfPCell(code128Image, true);
cell.setBorder(Rectangle.NO_BORDER);
cell.setFixedHeight(30);
table.addCell(cell);
// third row
table.addCell(cell);
cell = new PdfPCell(new Phrase("and something else here", smallfont));
cell.setBorder(Rectangle.NO_BORDER);
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
table.addCell(cell);
document.add(table);
document.close();
}
In this example,
you learn how to change the font of the content in a cell,
you learn how to change horizontal and vertical alignment,
you learn how to scale a bar code so that it fits into a cell,
...
All of this functionality is explained in the official documentation. As I said before: you didn't explain the nature of your problem. What wasn't clear in the documentation for you? What is your question?
This question already has an answer here:
How to add overlay text with link annotations to existing pdf?
(1 answer)
Closed 7 years ago.
Two questions,
How to hide or set color white to Rectangle box border
How to add text to Rectangle
public void addLinkedtoTOC(String src, String dest,String IMG,int linkedPageNumber,int linkDisplayTOCPageNumber) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
Rectangle linkLocation = new Rectangle(320, 695, 560, 741);
linkLocation.setBorderColorLeft(BaseColor.WHITE);
linkLocation.setBorderColorRight(BaseColor.RED);
linkLocation.setBorderColorTop(BaseColor.RED);
PdfDestination destination = new PdfDestination(PdfDestination.FIT);
PdfAnnotation link = PdfAnnotation.createLink(stamper.getWriter(), linkLocation, PdfAnnotation.HIGHLIGHT_INVERT,linkedPageNumber, destination);
stamper.addAnnotation(link, linkDisplayTOCPageNumber);
stamper.close();
reader.close();
}
Your first question is easy. It's a duplicate of iText - How to stamp image on existing PDF and create an anchor
When you create a PdfAnnotation object that represents a link annotation, a border is defined by default. You can remove this border using the setBorder() method at the level of the annotation as is done in the AddImageLink example:
link.setBorder(new PdfBorderArray(0, 0, 0));
Your second question has also been answered before. See for instance:
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?
I have combined both in the AddLinkAnnotation5 example:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
// Here we define the location:
Rectangle linkLocation = new Rectangle(320, 695, 560, 741);
// here we add the actual content at this location:
ColumnText ct = new ColumnText(stamper.getOverContent(1));
ct.setSimpleColumn(linkLocation);
ct.addElement(new Paragraph("This is a link. Click it and you'll be forwarded to another page in this document."));
ct.go();
// now we create the link that will jump to a specific destination:
PdfDestination destination = new PdfDestination(PdfDestination.FIT);
PdfAnnotation link = PdfAnnotation.createLink(stamper.getWriter(),
linkLocation, PdfAnnotation.HIGHLIGHT_INVERT,
3, destination);
// If you don't want a border, here's where you remove it:
link.setBorder(new PdfBorderArray(0, 0, 0));
// We add the link (that is the clickable area, not the text!)
stamper.addAnnotation(link, 1);
stamper.close();
reader.close();
}
However, there's also another way to add the text and the link annotation at the same time. That's explained in my answer to the duplicate question: How to add overlay text with link annotations to existing pdf?
In this answer, I refer to the AddLinkAnnotation2 example, where we add the content using ColumnText as described above, but we introduce a clickable Chunk:
Chunk chunk = new Chunk("The Best iText Questions on StackOverflow", bold);
chunk.setAnchor("http://developers.itextpdf.com/frequently-asked-developer-questions");
Wrap the chunk object inside a Paragraph, add the Paragraph using ColumnText and you have your borderless link.
I've been trying to create a table of contents which dynamically links to other pages in the PDF using anchors. I've ran into an issue with using stamper.getOverContent() as the canvas parameter in the method table.writeSelectedRows().
table.writeSelectedRows(0, -1, 36, 700, stamper.getOverContent(4));
The example below will write a table to page 4 containing an anchor destination, and a table to page 8 containing an anchor hyperlink which links back to page 4. (Note that I'm using tables for this because it is just a part of what ultimately needs to be achieved).
String strFileName = "C:\\link\\to\\existing\\PDF\\document.pdf";
PdfReader reader = new PdfReader(strFileName);
FileOutputStream out = new FileOutputStream("results/tables/link_in_positioned_table.pdf");
PdfStamper stamper = new PdfStamper(reader, out);
stamper.setRotateContents(true);
//Page 4 table containing destination
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell();
Paragraph p = new Paragraph();
table.setTotalWidth(500);
Anchor target = new Anchor("page 4");
target.setName("page4");
p.add(target);
cell.addElement(p);
table.addCell(cell);
table.writeSelectedRows(0, -1, 36, 700, stamper.getOverContent(4));
//Page 8 table containing link
PdfPTable table1 = new PdfPTable(1);
PdfPCell cell1 = new PdfPCell();
Paragraph p1 = new Paragraph();
table1.setTotalWidth(500);
Anchor anchor = new Anchor("page4 link");
anchor.setReference("#page4");
p1.add(anchor);
cell1.addElement(p1);
table1.addCell(cell1);
table1.writeSelectedRows(0, -1, 36, 700, stamper.getOverContent(8));
stamper.close();
reader.close();
out.close();
This works fine if anchor.setReference(); is an external hyperlink, but if it is internal it returns with the following error:
at
com.itextpdf.text.pdf.internal.PdfAnnotationsImp.addPlainAnnotation(PdfAnnotationsImp.java:126)
at com.itextpdf.text.pdf.PdfDocument.localGoto(PdfDocument.java:2178)
at
com.itextpdf.text.pdf.PdfDocument.writeLineToContent(PdfDocument.java:1643)
at com.itextpdf.text.pdf.ColumnText.go(ColumnText.java:1162) at
com.itextpdf.text.pdf.ColumnText.go(ColumnText.java:994) at
com.itextpdf.text.pdf.ColumnText.goComposite(ColumnText.java:1541) at
com.itextpdf.text.pdf.ColumnText.go(ColumnText.java:1000) at
com.itextpdf.text.pdf.ColumnText.go(ColumnText.java:994) at
com.itextpdf.text.pdf.ColumnText.go(ColumnText.java:982) at
com.itextpdf.text.pdf.PdfPRow.writeCells(PdfPRow.java:583) at
com.itextpdf.text.pdf.PdfPTable.writeSelectedRows(PdfPTable.java:833)
at
com.itextpdf.text.pdf.PdfPTable.writeSelectedRows(PdfPTable.java:966)
at
com.itextpdf.text.pdf.PdfPTable.writeSelectedRows(PdfPTable.java:912)
at
com.itextpdf.text.pdf.PdfPTable.writeSelectedRows(PdfPTable.java:891)
at
com.ems.rendition.cts.plugin.StamperPDFPlugin.createPdf(StamperPDFPlugin.java:1727)
at
com.ems.rendition.cts.plugin.StamperPDFPlugin.main(StamperPDFPlugin.java:1684)
Am I doing something wrong? Or is this an issue?
Note: I asked a similar question here - iTextPDF - Cannot use writeSelectedRows() on a table where an anchor has been inserted which didn't identify that getOverContent() was the issue and instead focussed on the actual implementation of the anchors.