Using ColumnText results in "The document has no pages" exception - itext

I want to wrap text in a rect which is below (or left or right) of a image as below :
Please see link : http://upanh.in/SLk/
I use ColumnText for wrapping text in my code:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/pdf");
try {
// step 1
Document document = new Document(PageSize.A4.rotate());
// step 2
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, baos);
// step 3
document.open();
// step 4
ColumnText column = new ColumnText(writer.getDirectContent());
column.setSimpleColumn(new Phrase("text is very long ..."), 10, 10, 20, 20, 18, Element.ALIGN_CENTER);
column.go();
// step 5
document.close();
ServletOutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
os.close();
} catch (DocumentException e) {
throw new IOException(e.getMessage());
}
}
Result:
ExceptionConverter: java.io.IOException: The document has no pages.
Do you have any suggestions how to fix that?
Question 2
I try to display text (center and middle) in the rect with below code but it wasn't success. The text was only center in the rect.
ColumnText column = new ColumnText(writer.getDirectContent());
column.setSimpleColumn(RectImg1[0], RectImg1[1], RectImg1[0] + squareHeight, RectImg1[1] + squareHeight
* 1 / 4);
Paragraph p = new Paragraph(imgr.getText(), fontH);
p.setAlignment(Element.ALIGN_CENTER | Element.ALIGN_MIDDLE);
p.setLeading(18);
column.addElement(p);
column.go();
where was my mistake ?

I have edited the title of your question because it was misleading: the exception you encounter will occur in a standalone application too. The fact that you are using the code in a Servlet is irrelevant.
I see that you have the following line:
column.go();
You did not use something like this:
int status = column.go();
If you did, and if you examined status, you would have noticed that the column object still contained some text.
What text? All the text.
There is a serious error in this line:
column.setSimpleColumn(new Phrase("text is very long ..."), 10, 10, 20, 20, 18, Element.ALIGN_CENTER);
You are trying to add the text "text is very long ..." into a rectangle with the following coordinates:
float llx = 10;
float lly = 10;
float urx = 20;
float ury = 20;
You didn't define a font, so the font is Helvetica with font size 12pt and you defined a leading of 18pt.
This means that you are trying to fit text that is 12pt high with an extra 6pt for the leading into a square that measures 10 by 10 pt. Surely you understand that this can't work!
As a result, nothing is added to the PDF and rather than showing an empty page, iText throws an exception saying: there are no pages! You didn't add any content to the document!
You can fix this, for instance by changing the incorrect line into something like this:
column.setSimpleColumn(new Phrase("text is very long ..."), 36, 36, 559, 806, 18, Element.ALIGN_CENTER);
An alternative would be:
column.setSimpleColumn(rect);
column.addElement(paragraph);
In these two lines rect is a Rectangle object. The leading and the alignment are to be defined at the level of the Paragraph object (in this case, you don't use a Phrase).

Related

How to decide the right coordinates in the middle of a PDF page?

I am new to iText and I looked at its many examples. The thing I have hard time to figure it out is the rectangle. On the page
http://developers.itextpdf.com/examples/form-examples-itext5/multiline-fields
there are many examples with hard-coded values for Rectangle objects. For example:
Rectangle rect = new Rectangle(36, 770, 144, 806);
My problem is that I create one Paragraph and I would like to add a fillable text input box (multi-lines) beneath it. How do I know the exact values for creating a Rectangle object that can be nicely put just after the paragraph. The size of the text of a Paragraph can change. So I cannot assume any hard-coded value.
In iText 5, iText keeps track of the coordinates of the content when using document.add(). You could take control yourself by adding content at absolute positions (e.g. by using ColumnText), but that's hard, because then you have to keep track of many things yourself (for instance: you have to introduce page breaks yourself when the content reaches the bottom of the page).
If you leave the control of the coordinates to iText, you can get access to these coordinates by using page events.
Take a look at the example below, where we keep track of the start and the end of a Paragraph in the onParagraph() and onParagraphEnd() method. This code sample is not easy to understand, but it's the only way to get the coordinates of a Paragraph in iText 5 for instance if we want to draw a rectangle around a block of text. As you can read at the bottom of that page, iText 7 makes it much easier to meet this requirement.
If you stick to iText 5, it's much easier to use generic tags to define locations. See the GenericFields example, where we use empty Chunks that result in fields. If you want to see a screen shot of the result, see Add PdfPCell to Paragraph
In your case, I'd create a Paragraph containing a Chunk that spans different lines, and I'd add the field in the onGenericTag() method of a page event.
Suppose that we have the following text file: jekyll_hyde.txt
How do we convert it to a PDF that looks like this:
Note the blue border that is added to the titles, and the page number at the bottom of each page. In iText 5, these elements are added using page events:
class MyPageEvents extends PdfPageEventHelper {
protected float startpos = -1;
protected boolean title = true;
public void setTitle(boolean title) {
this.title = title;
}
#Override
public void onEndPage(PdfWriter writer, Document document) {
Rectangle pagesize = document.getPageSize();
ColumnText.showTextAligned(
writer.getDirectContent(),
Element.ALIGN_CENTER,
new Phrase(String.valueOf(writer.getPageNumber())),
(pagesize.getLeft() + pagesize.getRight()) / 2,
pagesize.getBottom() + 15,
0);
if (startpos != -1)
onParagraphEnd(writer, document,
pagesize.getBottom(document.bottomMargin()));
startpos = pagesize.getTop(document.topMargin());
}
#Override
public void onParagraph(PdfWriter writer, Document document,
float paragraphPosition) {
startpos = paragraphPosition;
}
#Override
public void onParagraphEnd(PdfWriter writer, Document document,
float paragraphPosition) {
if (!title) return;
PdfContentByte canvas = writer.getDirectContentUnder();
Rectangle pagesize = document.getPageSize();
canvas.saveState();
canvas.setColorStroke(BaseColor.BLUE);
canvas.rectangle(
pagesize.getLeft(document.leftMargin()),
paragraphPosition - 3,
pagesize.getWidth() - document.leftMargin() - document.rightMargin(),
startpos - paragraphPosition);
canvas.stroke();
canvas.restoreState();
}
}
We can use the following code to convert a text file to a PDF and introduce the page event to the PdfWriter:
public void createPdf(String dest)
throws DocumentException, IOException {
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest));
MyPageEvents events = new MyPageEvents();
writer.setPageEvent(events);
document.open();
BufferedReader br = new BufferedReader(new FileReader(TEXT));
String line;
Paragraph p;
Font normal = new Font(FontFamily.TIMES_ROMAN, 12);
Font bold = new Font(FontFamily.TIMES_ROMAN, 12, Font.BOLD);
boolean title = true;
while ((line = br.readLine()) != null) {
p = new Paragraph(line, title ? bold : normal);
p.setAlignment(Element.ALIGN_JUSTIFIED);
events.setTitle(title);
document.add(p);
title = line.isEmpty();
}
document.close();
}
Source: developers.itextpdf.com

How to create a multi-line input box (similar to HTML Textarea)?

I am running the MultiLineField example from this link:
http://developers.itextpdf.com/examples/form-examples-itext5/multiline-fields
However, I am not able to see any multi-line input box that allow people to enter text.
How to can I create a multi-line input box (similar to HTML Textarea)?
UPDATE
Here is the code I have now. I am not able to enter any value in the PDF form.
public void createPdf_multilines(String filename) throws DocumentException, IOException {
PdfReader reader = new PdfReader(createForm());
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(filename));
AcroFields form = stamper.getAcroFields();
form.setField("text", "A B C D E F\nG H I J K L M N\nO P Q R S T U\r\nV W X Y Z\n\nAlphabet street");
stamper.setFormFlattening(true);
stamper.close();
}
public byte[] createForm() throws DocumentException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, baos);
document.open();
Rectangle rect = new Rectangle(36, 770, 144, 806);
TextField tf = new TextField(writer, rect, "text");
tf.setOptions(TextField.MULTILINE);
tf.setBorderColor(BaseColor.BLUE);
tf.setBorderWidth(2);
writer.addAnnotation(tf.getTextField());
document.close();
return baos.toByteArray();
}
This creates a text field that will wrap text when it doesn't fit the width of the text area:
Rectangle rect = new Rectangle(36, 770, 144, 806);
TextField tf = new TextField(writer, rect, "text");
tf.setOptions(TextField.MULTILINE);
tf.setBorderColor(BaseColor.BLUE);
tf.setText("A B C D E F\nG H I J K L M N\nO P Q R S T U\r\nV W X Y Z\n\nAlphabet street");
tf.setBorderWidth(2);
writer.addAnnotation(tf.getTextField());
I have added a border width and a border color, so that you clearly see the field on the page. Maybe that's your problem: maybe the field is there, but you just don't see it.
Note that you shouldn't expect people to be able to resize the field as is possible in HTML. In PDF, all fields have fixed coordinates (in this case 36, 770 and 144, 806). You shouldn't expect PDF forms to behave the same way as HTML forms behave.

Page Header overlaps with Table

In itext, I have a table in my pdf. When the table gets full for a page the entries continues on the next page but it overlaps with my pdf page header. How do I avoid that?
Please take a look at the TableHeader example.
In this example, I create a document with some "Hello World" content:
public void createPdf(String filename) throws IOException, DocumentException {
TableHeader.HeaderTable event = new TableHeader.HeaderTable();
// step 1
Document document = new Document(PageSize.A4, 36, 36, 20 + event.getTableHeight(), 36);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
writer.setPageEvent(event);
// step 3
document.open();
// step 4
for (int i = 0; i < 50; i++)
document.add(new Paragraph("Hello World!"));
document.newPage();
document.add(new Paragraph("Hello World!"));
document.newPage();
document.add(new Paragraph("Hello World!"));
// step 5
document.close();
}
As you can see, I also define a TableHeader event. I use this event as a page event, but I also need this event when I define the Document. I use the following value as top margin:
20 + event.getTableHeight()
What does this mean? Let's take a look at the implementation of this event:
public class HeaderTable extends PdfPageEventHelper {
protected PdfPTable table;
protected float tableHeight;
public HeaderTable() {
table = new PdfPTable(1);
table.setTotalWidth(523);
table.setLockedWidth(true);
table.addCell("Header row 1");
table.addCell("Header row 2");
table.addCell("Header row 3");
tableHeight = table.getTotalHeight();
}
public float getTableHeight() {
return tableHeight;
}
public void onEndPage(PdfWriter writer, Document document) {
table.writeSelectedRows(0, -1,
document.left(),
document.top() + ((document.topMargin() + tableHeight) / 2),
writer.getDirectContent());
}
}
When I create the event, a PdfPTable is constructed. I store this table as a member variable, along with the height of this table: tableHeight.
I use this tableHeight when I define the top margin, so that I am 100% sure that the table will fit the margin. I add an additional 20 user units because I don't want the table to stick to the top border of the page:
20 + event.getTableHeight()
When I add the table in the onEndPage() method, I use the following coordinates:
x = document.left()
y = document.top() + ((document.topMargin() + tableHeight) / 2),
The value of document.top() is the value of the top of the page minus the top margin. I add some extra space, more specifically the difference of the top margin and the table height divided by two, added to the table height:
tableHeight + ((document.topMargin() - tableHeight) / 2)
This formula can be simplified to:
((document.topMargin() + tableHeight) / 2)
As you can see, all of this is simple Math, the kind of Math you are taught in primary school.
The resulting PDF looks like this:
This proves that your allegation that "it doesn't work" is wrong. Please understand that it is not polite to say "it doesn't work" after people have explained in great detail how to do something. By showing that it does work, people reading this could interpret your allegation as a lie (and that is bad for your karma).

Total page number with itextpdf

I am using itextpdf-5.5.6 for publish my data.
I'm trying to set page numbers in PdfPCell of my pdf using following format : page_num/total_page_num
For this I use PdfTemplate object filling inside total page number before close document.
It warks, but PdfTemplate exceeds PdfPCell border.
Is it possible that all content of cell stay inside of cell properly ?
PdfTemplate pageNumTemplate;
#Test
public void quick_test() throws FileNotFoundException, DocumentException {
String filename = "C:\\test.pdf";
File file = new File(filename);
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
PageNumberEvent pageNumberEvent = new PageNumberEvent();
writer.setPageEvent(pageNumberEvent);
document.open();
for(int i = 0; i < 10000; i++){
document.add(new Paragraph("This is my paragraph"));
document.newPage();
}
document.close();
}
public class PageNumberEvent extends PdfPageEventHelper {
#Override
public void onEndPage(PdfWriter writer, Document document) {
if(pageNumTemplate == null){
pageNumTemplate = writer.getDirectContent().createTemplate(0.1f, 0.1f);
}
PdfPTable table = new PdfPTable(1);
table.setSpacingBefore(50);
Paragraph para = new Paragraph();
Chunk pageNum = new Chunk(writer.getPageNumber() + "/");
para.add(pageNum);
Image totalPageNumImg = null;
try {
totalPageNumImg = Image.getInstance(pageNumTemplate);
} catch (BadElementException e) {
e.printStackTrace();
}
Chunk totalPageNumImgChunk = new Chunk(totalPageNumImg, 0, -1, true);
para.add(totalPageNumImgChunk);
para.setIndentationLeft(370);
PdfPCell cell = new PdfPCell(para);
cell.addElement(para);
table.addCell(cell);
try {
document.add(table);
} catch (DocumentException e) {
e.printStackTrace();
}
}
#Override
public void onCloseDocument(PdfWriter writer,Document document) {
String totalPageNumString = String.valueOf(writer.getPageNumber() - 1);
float widthPoint = totalPageNumString.length() * 10;
float heightPoint = totalPageNumString.length() * 20;
Phrase totalPageNumPhrase = new Phrase(totalPageNumString);
Rectangle templRect = pageNumTemplate.getBoundingBox();
Rectangle rectangle = new Rectangle(templRect.getLeft(), templRect.getBottom(), widthPoint, heightPoint + 2);
pageNumTemplate.setBoundingBox(rectangle);
ColumnText.showTextAligned(pageNumTemplate, Element.ALIGN_LEFT, totalPageNumPhrase, 0, 1, 0);
}
}
Early, in the first onEndPage call, you create a minute template (0.1x0.1) to start with
pageNumTemplate = writer.getDirectContent().createTemplate(0.1f, 0.1f);
which fits into your cell. At the end though, in onCloseDocument, after everything has been layout'ed to a fixed position, you resize this template which makes it grow to the left:
Rectangle templRect = pageNumTemplate.getBoundingBox();
Rectangle rectangle = new Rectangle(templRect.getLeft(), templRect.getBottom(), widthPoint, heightPoint + 2);
pageNumTemplate.setBoundingBox(rectangle);
If you want your template to remain in your table cell, you have to initialize it with a width which most likely will be enough to hold the number of pages, e.g.
pageNumTemplate = writer.getDirectContent().createTemplate(30f, 0.1f);
Here you may have to play around a bit...
As the OP indicated in a comment, not only keeping all content of cell stay inside of cell is required but the content also is expected to be right aligned in the cell.
This second requirement obviously cannot be fulfilled by the OP's code which draws the number of pages into the template using ALIGN_LEFT. Furthermore it does not mix and match with resizing the template on the right size because it has already been positioned on the pages.
To fulfill it, therefore, the OP should
horizontally initialize the template with a size large enough for any expected number of pages (onEndPage);
fill the table cell with right alignment (instead of some pseudo right alignment by means of setIndentationLeft) (onEndPage);
leave the horizontal size of the template as is (onCloseDocument); and
show the number of pages right aligned to the right template border (onCloseDocument).

iTextSharp ColumnText.SetSimpleColumn Addtext causes Error in Adobe Reader X 10.1.5

The code below illustrates a problem I have with iTextSharp. Everything works perfectly. The pdf file is created and appears correct on the screen. When I print the pdf from Adobe Reader X, it looks exactly right but Adobe reports "An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem."
Unfortunately, the file has to be attached to an email and sent to clients. The error message is not a good look and I want to fix it. It happens in all versions of Reader that I have tried, including 10.1.15 installed today.
I have iTextSharp 5.3.4.0 under Windows 7 Pro SP1
private void writeTestDoc()
{
string fname = "test.pdf";
float textWidth = 500;
float leftMgn = 60;
float rubricTop = 720;
float leftPad = 5;
float topPad = 12;
float leading = 0;
BaseFont baseCalibri = BaseFont.CreateFont("c:/windows/fonts/calibri.ttf", BaseFont.WINANSI, true);
Font plainFont = new Font(baseCalibri, 11, Font.NORMAL);
Document document = new Document();
try
{
PdfWriter writer = PdfWriter.GetInstance(document, new FileStream(fname, FileMode.Create));
document.Open();
PdfContentByte cb = writer.DirectContent;
cb.BeginText();
ColumnText ct = new ColumnText(cb);
float boxTop = rubricTop;
ct.SetSimpleColumn(leftMgn + leftPad, boxTop - topPad, leftMgn + textWidth, boxTop, leading, Element.ALIGN_CENTER);
ct.AddText(new Phrase("A test message", plainFont));
ct.Go();
cb.EndText();
document.Close();
}
catch (Exception ex)
{
writeFile("ERROR in writeTestDoc " + ex.Message);
}
}
Remove cb.BeginText(); and cb.EndText();. It's illegal for BT/ET text objects to be nested. Report the place where you've found the documentation that told you to use BeginText()/EndText in combination with ColumnText, so that we can ask the author to correct it from his or her documentation.