Creating PDF with overflow layout via iTextSharp - c#

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...

Related

Remove page numbers from PDFsharp page merge when merging with another document

I used PDFsharp to merge 2 PDF files. My code adds page numbering to the bottom of each page. Now I need to merge the created document with another PDF the same way. The issue I get is the page numbers on the part created from the last document are fine. The document created from the first merge has the new page number added over the top of the first set so there are now two page numbers on top of each other in the first set of pages. So in one spot I have both of these "Page 1 of 40" and Page "1 of 110" on top of each other.
Here is the code I used to merge the PDFs
using PdfSharp.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
namespace SomeProject.Helpers
{
public static class PDFHelper
{
public static void MergePDFFiles(string[] pdfFiles, string outputFilePath)
{
PdfDocument document = new PdfDocument();
foreach (string pdfFile in pdfFiles)
{
PdfDocument inputPDFDocument = PdfReader.Open(pdfFile, PdfDocumentOpenMode.Import);
document.Version = inputPDFDocument.Version;
foreach (PdfPage page in inputPDFDocument.Pages)
{
document.AddPage(page);
}
}
// Set font for paging
XFont font = new XFont("Verdana", 9);
XBrush brush = XBrushes.Black;
// Create variable that store page count
string noPages = document.Pages.Count.ToString();
// Set for loop of document page count and set page number using DrawString function of PdfSharp
for (int i = 0; i < document.Pages.Count; ++i)
{
PdfPage page = document.Pages[i];
// Make a layout rectangle.
XRect layoutRectangle = new XRect(240 /*X*/ , page.Height - font.Height - 10 /*Y*/ , page.Width /*Width*/ , font.Height /*Height*/ );
using (XGraphics gfx = XGraphics.FromPdfPage(page))
{
gfx.DrawString("Page " + (i + 1).ToString() + " of " + noPages, font, brush, layoutRectangle, XStringFormats.Center);
}
}
document.Save(outputFilePath);
}
}
}
A clean solution: only add the page numbers to the final documents. Keep copies of intermediate files without page numbers or create the files twice as needed.
A hack: draw a white rectangle below the new page numbers to hide anything that is already in that area. The PDF will have both page numbers, but only the most recent and current page number will be visible.
Removing page numbers is a bit complicated.

C# itextsharp table over multiple pages [duplicate]

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?

PdfSmartCopy is copying the contents at the bottom (footer) of the new PDF file

I have a function which is cropping the specific part of the pdf file and adding it into the new Pdf file but the main problem that i am getting is that it is showing the cropped part of the page into the bottom (footer) of the newly created pdf file.
Here is the code..
public static void CropPdfFile(string sourceFilePath, string outputFilePath)
{
// Allows PdfReader to read a pdf document without the owner's password
PdfReader.unethicalreading = true;
// Reads the PDF document
using (PdfReader pdfReader = new PdfReader(sourceFilePath))
{
// Set which part of the source document will be copied.
// PdfRectangel(bottom-left-x, bottom-left-y, upper-right-x, upper-right-y)
PdfRectangle rect = new PdfRectangle(0f, 9049.172f, 594.0195f, 700.3f);
using (var output = new FileStream(outputFilePath, FileMode.CreateNew, FileAccess.Write))
{
// Create a new document
using (Document doc = new Document())
{
// Make a copy of the document
PdfSmartCopy smartCopy = new PdfSmartCopy(doc, output);
// Open the newly created document
doc.Open();
// Loop through all pages of the source document
for (int i = 4; i <= pdfReader.NumberOfPages; i++)
{
// Get a page
var page = pdfReader.GetPageN(i);
// Apply the rectangle filter we created
page.Put(PdfName.CROPBOX, rect);
page.Put(PdfName.MEDIABOX, rect);
// Copy the content and insert into the new document
smartCopy.SetLinearPageMode();
var copiedPage = smartCopy.GetImportedPage(pdfReader, i);
smartCopy.AddPage(copiedPage);
}
// Close the output document
doc.Close();
}
}
}
Please help me to solve this..
The size of a PDF page (expressed in user units) depends on the value of the mediabox. For instance: The media box of an A4 page is usually defined like this [0 0 595 842]
In this case, the origin of the coordinate system (0, 0) coincides with the lower-left corner. The coordinate of the upper right corner is (595, 842).
Another possible value for an A4 page would be [0 842 595 1684]. Same width (595 user units), same height (1684 - 842 = 842 user units), but the lower-left corner now has the coordinate (0, 842) and the coordinate of the upper-right corner is (595, 1684).
You write that you create a PdfRectangle using these parameters: (bottom-left-x, bottom-left-y, upper-right-x, upper-right-y). However, you're using these hard-coded values: 0f, 9049.172f, 594.0195f, 700.3f.
Your lower-left-y (9049.172) is at a higher position than your upper-right-y (700.3). This doesn't really make sense. Hence: you should consider changing that value to something that does make sense. What value that should be, is a question only you can answer since only you know the value of the MediaBox of the file you want to crop.
In your comment, you explain that your PDF is an A4 page. You can check this by using the PdfReader method named getPageSize(). If you want to crop the page so that you only see the header of your document, you need to use something like this:
PdfRectangle rect = new PdfRectangle(0f, 842 - x, 595, 842);
Where x is the height of the header. For instance, if the header is 100 user units, then you'd need:
PdfRectangle rect = new PdfRectangle(0f, 742, 595, 842);
It is unclear why you're always talking about 100px. If you want to convert pixels to points, please read Convert Pixels to Points
Using the formula mentioned there 100 pixels equals 75 points.

Why doesn't an Image show up when I add it to a Document using iTextSharp?

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.

itextsharp : Adding multiple pages

I'm using the DirectContent method of absolutely positioning elements on my PDF.
I need to iterate over a list of records and build one page per record in my PDF.
How do I tell itextsharp to insert a new page and "draw" to that page?
// 72point per inch
// we want 7x10
iTextSharp.text.Rectangle pageSize = new iTextSharp.text.Rectangle(504, 720);
Document doc = new Document(pageSize);
PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(#"C:\temp\backPages.pdf", FileMode.Create));
doc.Open();
PdfContentByte cb = writer.DirectContent;
// "DRAW" IMAGES AND TEXT
...
//various .Add's called here
...
// Done with drawing images & text
doc.Close();
Easily enough its the Document.NewPage() function.
I saw some really odd "solutions" on other sites, hope this helps someone else.

Categories