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 have a one page pdf template and need to create a new document with several pages. Each page needs to be as the first page of the template. Then i need to add text to each page. The pages are copied but the text is not added.
This is my code:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(stream));
PdfDocument cover = new PdfDocument(new PdfReader(templatePath));
//First copy the pages
var totalPages=5;
var coverPage = cover.GetPage(1);
for (int i = 0; i < totalPages; i++)
{
//If i do it to a blank page the text is visible
//pdfDoc.AddNewPage();
//I have tried both methods:
pdfDoc.AddPage(coverPage.CopyTo(pdfDoc));
//cover.CopyPagesTo(1, 1, pdfDoc);
}
//Now i try to add text
Document doc = new Document(pdfDoc);
var font = PdfFontFactory.CreateFont(fontPath);
for (int i = 1; i <= totalPages; i++)
{
//Edited
Rectangle pagesize = pdfDoc.GetPage(i).GetPageSize();
doc.ShowTextAligned(new Paragraph("HEADER").SetFont(font).SetFontSize(22), pagesize.GetLeft(), pagesize.GetBottom(), i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
//doc.ShowTextAligned(new Paragraph("HEADER").SetFont(font), 100, 700, i, TextAlignment.CENTER, VerticalAlignment.TOP, 0);
}
doc.Close();
cover.Close();
pdfDoc.Close();
I have tried this options:
Canvas instead of document with no result (see code below)
If i use the AddNewPage() and not the cover page, then the text is added to the blank page (both document and canvas methods).
If i open and write directly to the template document the text is visible but the size is very small and position of the text is different compared to 2)
This is the canvas code inside the for instruction:
var canvas = new PdfCanvas(pdfDoc.GetPage(i));
canvas.BeginText()
.SetFontAndSize(font, 22) //Edited
.MoveText(100, 700)
.ShowText("HEADER")
.EndText();
//UPDATED
Following the solution contributed by #mkl, i have changed the way i add the pages:
var coverPage = cover.GetPage(1);
Rectangle coverSize = coverPage.GetPageSize();
for (int i = 0; i < totalPaginas; i++)
{
//Taken from this example: https://kb.itextpdf.com/home/it7kb/ebooks/itext-7-jump-start-tutorial-for-java/chapter-6-reusing-existing-pdf-documents
PdfPage page = pdfDoc.AddNewPage(PageSize.A4);
PdfCanvas canvas = new PdfCanvas(page);
AffineTransform transformationMatrix = AffineTransform.GetScaleInstance(
page.GetPageSize().GetWidth() / coverSize.GetWidth(),
page.GetPageSize().GetHeight() / coverSize.GetHeight());
canvas.ConcatMatrix(transformationMatrix);
var pageCopy = coverPage.CopyAsFormXObject(pdfDoc);
canvas.AddXObjectAt(pageCopy, 0, 0);
//pdfDoc.AddNewPage();
//pdfDoc.AddPage(coverPage.CopyTo(pdfDoc));
//cover.CopyPagesTo(1, 1, pdfDoc);
}
Now i can see the text added, but the font size is much smaller than if instead of copying i do "pdfDoc.AddNewPage()", why is it? i would like it to be the correct font size.
Your code works in my tests. Maybe (100,700) is outside of the visual page area, which would typically be the case if your template page does not have its lower left corner at (0,0).
This should put the text in the lower left corner:
Rectangle pagesize = pdfDoc.GetPage(i).GetPageSize();
doc.ShowTextAligned(new Paragraph("HEADER"), pagesize.GetLeft(),
pagesize.GetBottom(), i, TextAlignment.LEFT, VerticalAlignment.BOTTOM, 0);
If that works, you can work from the pagesize rectangle to calculate the appropriate position for the text.
The solution to why the text dont appear is updated in my question at the bottom.
The reason why the coordinates dont match and the size is so small is the source template pdf that was exported with a very high px/inch for high level printing. Reducing it to 72ppp was the fix.
How do I place text at a specific location on the pdf? I did a little bit of searching but didn't find anything too good. I have document.Add(new Paragraph("Date:" + DateTime.Now)); and I wanted to place that on a specific area on the pdf file.
My code:
private void savePDF_Click(object sender, EventArgs e)
{
FileStream fileStream = new FileStream(nameTxtB.Text + "Repair.pdf", FileMode.Create, FileAccess.Write, FileShare.None);
Document document = new Document();
document.Open();
iTextSharp.text.Rectangle rectangle = new iTextSharp.text.Rectangle(PageSize.LETTER);
PdfWriter pdfWriter = PdfWriter.GetInstance(document, fileStream);
iTextSharp.text.Image r3tsLogo = iTextSharp.text.Image.GetInstance("rt3slogo.PNG"); //creates r3ts logo
iTextSharp.text.Image r3Info = iTextSharp.text.Image.GetInstance("R3 Information.PNG"); //creates r3 information text below r3ts logo
r3tsLogo.SetAbsolutePosition(document.PageSize.Width - 375 - 0f, document.PageSize.Height - 130 - 0f);
r3Info.SetAbsolutePosition(document.PageSize.Width - 365 - 0f, document.PageSize.Height - 170 - 0f); //higher the number in height the lower the place of text on paper
//less number will result in text more to right in width
//increase size of picture
r3tsLogo.ScalePercent(120);
r3Info.ScalePercent(65);
//---------------adds all images to pdf file ---------------------------------
document.Add(r3tsLogo);
document.Add(r3Info);
document.Add(new Paragraph("Date:" + DateTime.Now));
document.Close();
}
Assuming that you know how to add images at an absolute position (see Joris' answer), but looking at how to add text, then the answer to your question is: use ColumnText.
If you only need to add a single line that doesn't need to be wrapped, you can use the ShowTextAligned() method:
ColumnText.showTextAligned(writer.DirectContent,
Element.ALIGN_CENTER, new Phrase("single line"), x, y, rotation);
In this line of code, x and y are the coordinates for the middle of the text (other possible alignment values are ALIGN_LEFT and ALIGN_RIGHT). The rotation parameter defines a rotation in degrees. Note that the text "single line" won't be wrapped. You can add text that "falls off the page" this way if the text you're adding is too long.
If you want to add text inside a specific rectangle, then you need to define the column using a Rectangle object:
ColumnText ct = new ColumnText(writer.DirectContent);
ct.setSimpleColumn(new Rectangle(0, 0, 523, 50));
ct.addElement(new Paragraph("This could be a very long sentence that needs to be wrapped"));
ct.go();
If you provide more text than fits the rectangle, that text will not be rendered. However, it will still be available in the ct object so that you can add that remaining text at another position.
All of this has been asked and answered before:
Single line:
http://stackoverflow.com/questions/16370428/how-to-write-in-a-specific-location-the-zapfdingbatslist-in-a-pdf-document-using
http://stackoverflow.com/questions/17998306/rotating-text-using-center-in-itext
Multiple lines:
http://stackoverflow.com/questions/33609447
http://stackoverflow.com/questions/31152874/how-to-add-text-in-pdfcontentbyte-rectangle-using-itextsharp
http://stackoverflow.com/questions/15414923/rotate-paragraphs-or-cells-some-arbitrary-number-of-degrees-itext
Did I have to search long for these examples? No, I found them on the official web site under Absolute Positioning of text.
Wisdom is there for those who search...
This concept is thoroughly explained in the book 'iText in action'. Which can be found on the website.
http://developers.itextpdf.com/examples/itext-action-second-edition/chapter-3
Short code sample (check the site for other examples):
// step 1
Document document = new Document(PageSize.POSTCARD, 30, 30, 30, 30);
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT));
// step 3
document.open();
// step 4
// Create and add a Paragraph
Paragraph p = new Paragraph("Foobar Film Festival", new Font(FontFamily.HELVETICA, 22));
p.setAlignment(Element.ALIGN_CENTER);
document.add(p);
// Create and add an Image
Image img = Image.getInstance(RESOURCE);
img.setAbsolutePosition(
(PageSize.POSTCARD.getWidth() - img.getScaledWidth()) / 2,
(PageSize.POSTCARD.getHeight() - img.getScaledHeight()) / 2);
document.add(img);
I have struggled a bit with the official Syncfusion Xamarin Forms PDF Documentation. I have managed to create a table, bind it to values and append it to a PdfDocument.
However odd enough, I cannot seem to draw text strings on separate lines. Each text that I try to draw using graphics.DrawString() or TextElement gets drawn on top of each other in the first line of the document.
All the string are AppResources, concatenating each other to form a report from resx files and class objects that represent my data. Many of them need to be rendered as separate phrases and paragraphs.
Is there a working sample on how to append text line after line and in between them to have tables?
Thank you!
Drawing text one after another is possible by using Essential PDF. We can draw text by one after another by using PdfTextElement with PdfLayoutResult or specifying its position in PdfGraphics.DrawString method. Please refer the below code snippet and sample for more details.
//Creates a new PDF document.
PdfDocument doc = new PdfDocument();
//Adds a page.
PdfPage page = doc.Pages.Add();
//create a new PDF string format
PdfStringFormat drawFormat = new PdfStringFormat();
drawFormat.WordWrap = PdfWordWrapType.Word;
drawFormat.Alignment = PdfTextAlignment.Justify;
drawFormat.LineAlignment = PdfVerticalAlignment.Top;
//Set the font.
PdfFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 10f);
//Create a brush.
PdfBrush brush = PdfBrushes.Red;
//bounds
RectangleF bounds = new RectangleF(new PointF(10, 10), new SizeF(page.Graphics.ClientSize.Width - 30, page.Graphics.ClientSize.Height - 20));
//Create a new text elememt
PdfTextElement element = new PdfTextElement(text, font, brush);
//Set the string format
element.StringFormat = drawFormat;
//Draw the text element
PdfLayoutResult result = element.Draw(page, bounds);
// Draw the string one after another.
result = element.Draw(result.Page, new RectangleF(result.Bounds.X, result.Bounds.Bottom + 10, result.Bounds.Width, result.Bounds.Height));
// Creates a PdfLightTable.
PdfLightTable pdfLightTable = new PdfLightTable();
//Add colums to light table
pdfLightTable.Columns.Add(new PdfColumn("Name"));
pdfLightTable.Columns.Add(new PdfColumn("Age"));
pdfLightTable.Columns.Add(new PdfColumn("Sex"));
//Add row
pdfLightTable.Rows.Add(new string[] { "abc", "21", "Male" });
//Includes the style to display the header of the light table.
pdfLightTable.Style.ShowHeader = true;
//Draws PdfLightTable and returns the rendered bounds.
result = pdfLightTable.Draw(page, new PointF(result.Bounds.Left,result.Bounds.Bottom+20));
//draw string with returned bounds from table
result = element.Draw(result.Page, result.Bounds.X, result.Bounds.Bottom + 10);
//draw string with returned bounds from table
element.Draw(result.Page, result.Bounds.X, result.Bounds.Bottom + 10);
MemoryStream stream = new MemoryStream();
//Saves the document.
doc.Save(stream);
doc.Close(true);
Sample link: http://www.syncfusion.com/downloads/support/directtrac/171058/ze/App2938608856
Please let us know if you need any further assistance.
Contextt: I am opening an existing, interactive PDF form containing AcroForm fields. I tried to add an image to a rectangle field in the PDF form like this:
string path = HttpContext.Current.Server.MapPath("includes");
string newFile = HttpContext.Current.Server.MapPath("Tmp") + "/completed_gray" +".pdf";
string imagepath = HttpContext.Current.Server.MapPath("Tmp");
Document doc = new Document();
try {
PdfWriter.GetInstance(doc, new FileStream(newFile, FileMode.Open));
doc.Open();
iTextSharp.text.Image gif = iTextSharp.text.Image.GetInstance(imagepath + "/CUstomRep_Eng_Col_1_V1.png");
iTextSharp.text.Rectangle rect = pdfStamper.AcroFields.GetFieldPositions("img_1_space")[0].position;
gif.ScaleAbsolute(rect.Width, rect.Height);
gif.SetAbsolutePosition(rect.Left, rect.Bottom);
doc.Add(gif);
}
catch (Exception ex) {
//Log error;
}
finally {
doc.Close();
}
The image doesn't show up in the resulting PDF.
You're creating a document using the "5 steps to create a PDF document" as documented in my books.
create a Document object.
create a PdfWriter instance.
open the document.
add content to the document.
close the document.
This contradicts with what you actually want to do: I want to add an Image in a placeholder defined by an AcroForm field.
Why are you saying you want one thing, and doing something else? Beats me. Probably because you didn't want to read the documentation.
You need something like this:
Create a PdfReader instance.
Create a PdfStamper instance.
Ask the stamper for information about the fields.
Add content to a page using the stamper instance.
Close the stamper.
In answer to your question: why doesn't my image show up in my document?
Support the coordinates of the field in the existing document are lower-left corner x = 600, y = 600 and upper-right corner x = 700, y = 700, then you are adding the image outside the visible area of the page you're creating. When you use new Document();, you're creating a document where the lower-left corner is x = 0, y = 0 and the upper-right corner is x = 595, y = 842.
In that case, you're adding the image to the document, but it's not visible because you've added it outside the rectangle that defines the page.
I'm successfully creating a PDF document from a DataTable via iTextSharp, but I can't get the layout in a desireable format.
Though unlikely, the DataTable has the potential for dozens of columns. Imagine the following DataTable - each number represents the area that can fit on a page:
|------------|
| 1 : 2 : 3 |
|------------|
| 4 : 5 : 6 |
|------------|
This should export as a 6 page PDF document in the order I've numbered the sections. Instead, it's currently generating as a two page document with 40-some columns squished on each page with a font so small it literally has no detail.
The simplest way to explain what I want is: When a generated PDF prints, I want it to print like a very wide Excel sheet rather than squishing all the content together.
My code is as follows:
public static void ExportAsPDF(DataTable Table, IList<string> Columns, string filename)
{
int ColumnCount = Table.Columns.Count;
int RowCount = Table.Rows.Count;
iTextSharp.text.Table BodyTable = new iTextSharp.text.Table(ColumnCount, RowCount);
BodyTable.AutoFillEmptyCells = true;
foreach (string s in Columns)
{
BodyTable.AddCell(s);
}
foreach (object o in from DataRow row in Table.Rows from o in row.ItemArray select o)
{
BodyTable.AddCell(o.ToString());
}
Document doc = new Document();
PdfWriter.GetInstance(doc, HttpContext.Current.Response.OutputStream);
doc.Open();
doc.Add(BodyTable);
doc.Close();
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.pdf", filename));
HttpContext.Current.Response.End();
}
All input and help is much appreciated. Thanks
Okay, two options.
Draw everything yourself by hand with PdfContentByte and ColumnText for the text layout part.
Convince the normal iText Layout code that your page is wide enough to hold an entire row, then split those pages into pieces later. Not Pretty, but probably easier than the alternative.
First you need to define a page that is 3 times wider than normal.
Rectangle triplePageRect = new Rectangle(PageSize.LETTER);
float origWidth = triplePageRect.getWidth();
triplePageRect.setWidth(origWidth * 3f);
Document doc = new Document(triplePageRect);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(doc, baos);
Then you draw your table pretty much the way you are now... BUT you must be sure that a column edge lines up with your two page edges so you can easily break up the pages later. Bonus points if you can create an empty row centered on where the page break will be so you have a margin.
//Your Code Here
Finally, you need to save your PDF, open it again, and chop it to ribbons. I'm thinking PdfStamper.
// write everything out to the baos.
doc.close();
// and suck it right back up again. Hurray for efficiency. Or something.
PdfReader reader = new PdfReader(baos.toByteArrayOrWhateverItsCalled());
PdfStamper stamper = new PdfStamper( reader, new FileOutputStream(outputPath));
// duplicate the pages. I'll assume only one page, but you'll get the idea.
PdfDictionary origPage = reader.getPageN(1);
for (int i = 0; i < 2; ++i) {
// initial size is irrelevant, we're going to change it, but !null
stamper.insertPage(2+i, PageSize.LETTER);
PdfDictionary newPageDict = reader.getPage(2 + i);
// copy the original page... note that this is a shallow copy
newPageDict.putAll(origPageDict);
// duplicate the page rect so each page will have its own copy
PdfArray pageRect = newPageDict.getAsArray(PdfName.MEDIABOX);
// also a shallow copy, but changes to this array will be localized to the page.
PdfArray newRect = new PdfArray(pageRect);
// page rects are defined as [llx lly urx ury], so we need to change 0 and 2.
newRect.set(0, new PdfNumber(origWidth * (i+1));
newRect.set(2, new PdfNumber(origWidth * (i+2));
}
//Smoke'em if you've got 'em folks, we're done.
stamper.close();
Damn I'm good. Oooh ooh! And modest! Let's not forget good looking, brave, courteous, witty...