I am generating pdf file for payslip using PdfWriter in C#. And I'm downloading the pdf file from html code, every user it will create a table (<table>...</table>) and every table display in new page.
But all table are displayed in same page.
eg
Page 1
Employee 1 Details
may the details will come to the next page.
Page 2
Employee 2 Details
Page 3
Employee 2 details
Page 4
Employee 3 details
.....
.....
....
But Now my output will come
Page 1
Employee 1
Employee 2
Page 2
Employee 3
Employee 4
Employee 5
.....
My code is
StringBuilder stb = new StringBuilder();
stb.Append(All.ToString());
EXP.InnerHtml = stb.ToString();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=" + filename + ".pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
StringWriter stringWriter = new StringWriter();
HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
string resHtml = "";
for(int i=0;i<10;i++)
{
resHtml+="<table width='100%'><tr><td align='center'>payslip"+ i+"</td></tr></table>";
}
StringReader stringReader = new StringReader(resHtml);
Doc = new Document(PageSize.A2, 10f, 10f, 50f, 20f);
HTMLWorker htmlparser = new HTMLWorker(Doc);
PdfWriter.GetInstance(Doc, Response.OutputStream);
Doc.Open();
htmlparser.Open();
htmlparser.Parse(stringReader);
htmlparser.Close();
Doc.Close();
Response.Write(Doc);
Response.End();
You are using HTMLWorker. That class is deprecated: it is no longer supported as it has been abandoned in favor of XML Worker. There are different ways to solve your problem.
Create multiple small HTML files instead of one big HTML
I wouldn't create one long table for every employee, but a single table for every employee, and introduce document.NewPage() after adding every table.
See Answer #2 to the question How to parse multiple HTML files into a single PDF?
This is some Java code (you can read it as if it were pseudo code):
public void createPdf(Employees employees) throws IOException, DocumentException {
Document document = new Document();
PdfWriter.getInstance(document, new FileOutputStream(file));
document.open();
String css = readCSS();
for (Employee employee : employees) {
String html = createHtml(employee);
ElementList list = XMLWorkerHelper.parseToElementList(html, css);
for (Element e : list) {
document.add(e);
}
document.newPage();
}
document.close();
}
This solution is the best solution from the point of view of memory and CPU use.
Create one big HTML and introduce page breaks
Another option is to introduce a page break before every employee table. See set new page in HTML using iTextSharp HTMLWorker (html to pdf)
This isn't a good idea as you build up a large chunk of data in memory and that memory can only be released after the PDF is rendered. iTextSharp tries to flush pages to the OutputStream as soon as possible. If you create small HTML files, and add them to the PDF immediately, you can discard the HTML bytes from memory sooner rather than later and iTextSharp will also be able to flush content streams to the output, releasing memory that is needed to store that content.
Important notice:
Obviously, these answers imply that you do the right thing. That is: throw away your code that relies on the abandoned HTMLWorker and start using XML Worker.
You can append pagebreak after every tag and append before tag.
This will give you a string like,
.................
following is the code to split the html string.
Dim myString As String = sb.ToString()
Dim mySplit As String = "pagebreak"
Dim myResult() As String = myString.Split(New String() {mySplit}, StringSplitOptions.None)
To render each html string on new page,
Dim pdfDoc As New Document(PageSize.A4, 10.0F, 10.0F, 10.0F, 0.0F)
Dim htmlparser As New HTMLWorker(pdfDoc)
Using memoryStream As New MemoryStream()
Dim writer As PdfWriter = PdfWriter.GetInstance(pdfDoc, memoryStream)
pdfDoc.Open()
For Each r As String In myResult
Dim sr As New StringReader(r)
htmlparser.Parse(sr)
pdfDoc.NewPage()
sr.Dispose()
Next
pdfDoc.Close()
Dim bytes As Byte() = memoryStream.ToArray()
memoryStream.Close()
Response.Clear()
Response.ContentType = "application/pdf"
Response.AddHeader("Content-Disposition", "attachment;filename=Report.pdf")
Response.Buffer = True
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.BinaryWrite(bytes)
Response.[End]()
Response.Close()
End Using
If your html content is fixed then you can with page break but if your HTML content is variable then it will be different to predict when page is start and finish.
Related
I'm trying to create a CV builder that saves the CV edited by the user to a folder in my project for further processing of sending it through email, I have reached as far as using itext to create a PDF of an HTML div, but has no CSS or any of the text values I have returned from my database. Through some research i find that my problem could be solved by using itext 7 and an add-on pdfHTML but can not find any proper examples of how to use it with my ASP.NET code. Would really appreciate any help.
Bellow is the code for the on-click button event I use to generate the PDF
protected void ButtonDownload_Click(object sender, EventArgs e)
{
Response.ContentType = "application/pdf";
//Response.AddHeader("content-disposition", "attachment;filename=Panel.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
contentdiv.RenderControl(hw); //convert the div to PDF
StringReader sr = new StringReader(sw.ToString());
Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f);
HTMLWorker htmlparser = new HTMLWorker(pdfDoc);
PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
pdfDoc.Open();
htmlparser.Parse(sr);
pdfDoc.Close();
string filename = base.Server.MapPath("~/PDF/" + "UserCV.pdf");
HttpContext.Current.Request.SaveAs(filename, false);
Response.End();
}
This picture shows the pdf result i get when i click the download button
And this is html page it is trying to convert
The text bellow the headings on the HTML page are Labels whose values are being set by retrieving values form a database
This is an example on how to use pdfHTML
This example is quite extensive, as it also sets document properties, and registers a custom Font.
public void createPdf(String src, String dest, String resources) throws IOException {
try {
FileOutputStream outputStream = new FileOutputStream(dest);
WriterProperties writerProperties = new WriterProperties();
//Add metadata
writerProperties.addXmpMetadata();
PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
pdfDoc.getCatalog().setLang(new PdfString("en-US"));
//Set the document to be tagged
pdfDoc.setTagged();
pdfDoc.getCatalog().setViewerPreferences(new PdfViewerPreferences().setDisplayDocTitle(true));
//Set meta tags
PdfDocumentInfo pdfMetaData = pdfDoc.getDocumentInfo();
pdfMetaData.setAuthor("Joris Schellekens");
pdfMetaData.addCreationDate();
pdfMetaData.getProducer();
pdfMetaData.setCreator("JS");
pdfMetaData.setKeywords("example, accessibility");
pdfMetaData.setSubject("PDF accessibility");
//Title is derived from html
// pdf conversion
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
fp.addDirectory(resources);//The noto-nashk font file (.ttf extension) is placed in the resources
props.setFontProvider(fp);
props.setBaseUri(resources);
//Setup custom tagworker factory for better tagging of headers
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.setTagWorkerFactory(tagWorkerFactory);
HtmlConverter.convertToPdf(new FileInputStream(src), pdfDoc, props);
pdfDoc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
The most relevant line here is
HtmlConverter.convertToPdf(new FileInputStream(src), pdfDoc, props);
Which essentially tells pdfHTML to perform the conversion of the inputstream (specified by src), put the content in pdfDoc and use the given ConverterProperties (specified by props).
I'm getting only the first two page. I have a generated list of elements in the third page. When there are too many elements in my collection, all pages from there become blank in my pdf output
using (FileStream fs = new FileStream(filePath, FileMode.Create))
{
Document document = new Document(PageSize.A4, 25, 25, 30, 30);
WebClient wc = new WebClient();
string htmlText = wc.DownloadString(textUrl);
PdfWriter pdfWriter = PdfWriter.GetInstance(document, fs);
document.Open();
// register all fonts in current computer
FontFactory.RegisterDirectories();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider();
using (var msHtml = new MemoryStream(System.Text.Encoding.Default.GetBytes(htmlText)))
{
//Set factories
var cssAppliers = new CssAppliersImpl(fontProvider);
var htmlContext = new HtmlPipelineContext(cssAppliers);
//HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(Tags.GetHtmlTagProcessorFactory());
//FontFactory.Register(arialuniTff);
string gishaTff = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "GISHA.TTF");
FontFactory.Register(gishaTff);
var worker = XMLWorkerHelper.GetInstance();
var cssStream = new FileStream(FolderMapPath("/css/style.css"), FileMode.Open);
worker.ParseXHtml(pdfWriter, document, msHtml, cssStream, new UnicodeFontFactory());
}
// Close the document
document.Close();
// Close the writer instance
pdfWriter.Close();
}
Here is my cshtml code
I only have experience working with iText in Java, but is it possible that the MemoryStream object you are using has a byte limit that gets filled up when the table on page 3 has too many elements for it to store? If that's the case, then the closing tags on that long table may not be written to the MemoryStream, thus that table and everything after doesn't get rendered; i.e. get's truncated by the PDF converter engine.
Can you try using a different Stream object?
Here is how i solved my problem. The problem wasn't with the C# backend code. It's seems like the XMLWorkerHelper at the moment doesn't deal well with loop in view. I had to display list of items in my PDF file. The result was well if the collection contains no so many items, But the page break at the level when the the collection contains more than 50 items because this could not be display in a single page. What i did is that i started to count the number of items and at some number like 40, i just include a break element <li style="list-style:none; list-style-type:none; page-break-before:always">#item</li>. And reset the counter and continue display my items. And it was great and my problem was resolved.
May be this can be helpful for someone.
I am doing html to pdf file . Its Downloading instantly . I dont want download instantly. i want to save the file in my project folder once converted.
My C# Code
string html ="<table><tr><td>some contents</td></tr></table>";
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=WelcomeLetter.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
StringReader sr = new StringReader(table);
Document ResultPDF = new Document(iTextSharp.text.PageSize.A4, 25, 10, 20, 30);
PdfPTable Headtable = new PdfPTable(7);
Headtable.TotalWidth = 525f;
Headtable.LockedWidth = true;
Headtable.HeaderRows = 5;
Headtable.FooterRows = 2;
Headtable.KeepTogether = true;
HTMLWorker htmlparser = new HTMLWorker(ResultPDF);
PdfWriter.GetInstance(ResultPDF, Response.OutputStream);
ResultPDF.Open();
htmlparser.Parse(sr);
ResultPDF.Close();
Response.Write(ResultPDF);
Response.End();
For saving pdf file locally in your project folder you can use FileStream class like this.
FileStream stream = new FileStream(filePath, FileMode.Create);//Here filePath is path of your project folder.
Now use this stream instead of using Response.OutputStream when you create instance of PdfWriter object.
PdfWriter.GetInstance(ResultPDF, stream);
Now do not use Responce.Write as you don't want to download your file.And close your stream at end.
stream.Close();
I'm going to combine everyone's answer into one that you should be able to drop in and use. If this works, I would accept Manish Parakhiya's answer because that had the most important part.
First, I'm going to assume you are using a recent version of iTextSharp. I think 5.5.5 is the most recent version. Second, because of this, I'm going to restructure your code a bit in order to use the using pattern. If you're stuck on an older obsolete unsupported version like 4.1.6 you'll need to re-adjust.
Almost every tutorial out there shows you that you can bind directly the Response.OutputStream. This is 100% valid but I would argue that it is also a really bad idea. Instead, bind to a more generic MemoryStream. This makes debugging much easier and your code will port and adapt that much easier.
The below code includes comments about each of the changes and what things are actually doing. The top section is all about creating a PDF from a string of HTML. The bottom actually does something with it, including writing it to disk and/or streaming it to a browser.
//Will hold our PDF eventually
Byte[] bytes;
//HTML that we want to parse
string html = "<table><tr><td>some contents</td></tr></table>";
//Create a MemoryStream to write our PDF to
using (var ms = new MemoryStream()) {
//Create our document abstraction
using (var ResultPDF = new Document(iTextSharp.text.PageSize.A4, 25, 10, 20, 30)) {
//Bind a writer to our Document abstraction and our stream
using (var writer = PdfWriter.GetInstance(ResultPDF, ms)) {
//Open the PDF for writing
ResultPDF.Open();
//Parse our HTML using the old, obsolete, not support parser
using (var sw = new StringWriter()) {
using (var hw = new HtmlTextWriter(sw)) {
using (var sr = new StringReader(html)) {
using (var htmlparser = new HTMLWorker(ResultPDF)) {
htmlparser.Parse(sr);
}
}
}
}
//Close the PDF
ResultPDF.Close();
}
}
//Grab the raw bytes of the PDF
bytes = ms.ToArray();
}
//At this point, the bytes variable holds a valid PDF file.
//You can write it disk:
System.IO.File.WriteAllBytes("your file path here", bytes);
//You can also send it to a browser:
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=WelcomeLetter.pdf");
Response.BinaryWrite(bytes);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
//Never do the next line, it doesn't do what you think it does and actually produces corrupt PDFs
//Response.Write(ResultPDF); //BAD!!!!!!
Response.End();
string tempDirectory = Session.SessionID.ToString();
string location = Path.Combine(Server.MapPath(
WebConfigurationManager.AppSettings["PathSet"].ToString()), tempDirectory);
if (!Directory.Exists(location))
{
Directory.CreateDirectory(location);
}
string fileName="abc.pdf";
filePath = Path.Combine(location, fileName);
I am using the following code for generating a PDF file.
It is working good, but now i want to generate 4 PDF's at the same time.
I tried by again initiating Document & repeating the whole code for generating 2nd PDF report, But it generates only 1 PDF.
var document = new Document(PageSize.A4, 50, 50, 25, 25);
// Create a new PdfWrite object, writing the output to a MemoryStream
var output = new MemoryStream();
var writer = PdfWriter.GetInstance(document, output);
// Open the Document for writing
document.Open();
string contents = System.IO.File.ReadAllText(Server.MapPath("~/Reports/Original.html"));
var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(contents), null);
foreach (var htmlElement in parsedHtmlElements)
document.Add(htmlElement as IElement);
document.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", string.Format("attachment;filename=Receipt-{0}.pdf", "Report"));
Response.BinaryWrite(output.ToArray());
return View();
How to generate multiple PDF's?
You are outputting the bytes as a response, so you would never be able of generating 2 different files in one response. Only one response per request.
If you want the user to download 2 different PDFs at the same time you could call the controller using javascript from the view.
I'm working on asp.net application where it takes input from user in multiple form and save in pdf. Now I'm stuck where I don't know how to save the generated pdf in the SQL Server database.
With this, I'm attaching code for saving in pdf.
protected void btnExportPDF_Click(object sender, EventArgs e)
{
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=Samba.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
StringWriter st = new StringWriter();
HtmlTextWriter ht = new HtmlTextWriter(st);
gvExperience.AllowPaging = false;
gvExperience.DataBind();
gvExperience.RenderControl(ht);
StringReader sr1 = new StringReader(st.ToString());
Document pdfDoc = new Document(PageSize.A4, 10f,10f,10f,0f);
HTMLWorker htmlparser = new HTMLWorker(pdfDoc);
PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
pdfDoc.Open();
htmlparser.Parse(sr1);
pdfDoc.Close();
Response.Write(pdfDoc);
Response.End();
}
Create a table with at least the name of the file (varchar(100), the content (varbinary(max)), and the length of the content (int).
Convert pdf to byte array (byte[]). I would swap our your Reponse.OutputStream for a memory stream. Save to Sql Server with your prefered data access method.
Sql server's 'varbinary' will translate to C#'s 'byte[]'. When you're writing it back to the client's browser you'll want to set the 'content-type' header and write to the stream with 'Response.BinaryWrite(byte[])'.
Convert to base64 string then insert into DB.
byte[] pdfBytes = File.ReadAllBytes(pdfPath);
string pdfBase64 = Convert.ToBase64String(pdfBytes);
Happy coding. :)