1

I am using itextsharp to generate PDF in MVC application and this works very well on dev machine. When i deploy this on pre-production machine, this generates blank PDF page and no exception. I tried quick debugging using logging to see if I'm getting content or not after line StringReader sr = new StringReader(sb.ToString()); and i see it has content.

After some research I found one faced similar issue and he resolved this issue just by writing document.Open() after PdfWriter.GetInstance. Source: "The document is not open" error only in production with iTextSharp

After that, I thought to use code which worked for many developers How to convert HTML to PDF using iTextSharp and I still see blank PDF on pre-production but works on dev machine.

I doubt if itextsharp has some sort of dependency on operation system or office suit (with itextsharp latest updates) because as I said it works on dev machine.

Here's the code which generates blank PDF on production machine Windows Server 2012 R2.

public ActionResult DownloadOrderReceipt(string id)
{
    string companyName = "Name Here";
    int orderNo = 2303;
    DataTable dt = new DataTable();
    dt.Columns.AddRange(new DataColumn[5]
    {
            new DataColumn("ProductId", typeof(string)),
            new DataColumn("Product", typeof(string)),
            new DataColumn("Price", typeof(int)),
            new DataColumn("Quantity", typeof(int)),
            new DataColumn("Total", typeof(int))
    });
    dt.Rows.Add(101, "Sun Glasses", 200, 5, 1000);
    dt.Rows.Add(102, "Jeans", 400, 2, 800);
    dt.Rows.Add(103, "Trousers", 300, 3, 900);
    dt.Rows.Add(104, "Shirts", 550, 2, 1100);

    using (StringWriter sw = new StringWriter())
    {
        using (HtmlTextWriter hw = new HtmlTextWriter(sw))
        {
            StringBuilder sb = new StringBuilder();

            // generate invoice header
            sb.Append("<table width='100%' cellspacing='0' cellpadding='2'>");
            sb.Append("<tr><td align='center' style='background-color: #18B5F0' colspan = '2'><b>Order Sheet</b></td></tr>");
            sb.Append("<tr><td colspan = '2'></td></tr>");
            sb.Append("<tr><td><b>Order No: </b>");
            sb.Append(orderNo);
            sb.Append("</td><td align = 'right'><b>Date: </b>");
            sb.Append(DateTime.Now);
            sb.Append(" </td></tr>");
            sb.Append("<tr><td colspan = '2'><b>Company Name: </b>");
            sb.Append(companyName);
            sb.Append("</td></tr>");
            sb.Append("</table>");
            sb.Append("<br />");

            // generate invoice items grid
            sb.Append("<table border = '1'>");
            sb.Append("<tr>");
            foreach (DataColumn column in dt.Columns)
            {
                sb.Append("<th style = 'background-color: #D20B0C;color:#ffffff'>");
                sb.Append(column.ColumnName);
                sb.Append("</th>");
            }
            sb.Append("</tr>");
            foreach (DataRow row in dt.Rows)
            {
                sb.Append("<tr>");
                foreach (DataColumn column in dt.Columns)
                {
                    sb.Append("<td>");
                    sb.Append(row[column]);
                    sb.Append("</td>");
                }
                sb.Append("</tr>");
            }
            sb.Append("<tr><td align = 'right' colspan = '");
            sb.Append(dt.Columns.Count - 1);
            sb.Append("'>Total</td>");
            sb.Append("<td>");
            sb.Append(dt.Compute("sum(Total)", ""));
            sb.Append("</td>");
            sb.Append("</tr></table>");

            // export html string into PDF
            StringReader sr = new StringReader(sb.ToString());
            Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f);
            HTMLWorker htmlparser = new HTMLWorker(pdfDoc);
            PdfWriter writer = PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
            pdfDoc.Open();
            htmlparser.Parse(sr);
            pdfDoc.Close();
            Response.ContentType = "application/pdf";
            Response.AddHeader("content-disposition", "attachment;filename=Invoice_" + orderNo + ".pdf");
            Response.Cache.SetCacheability(HttpCacheability.NoCache);
            Response.Write(pdfDoc);
            Response.End();
        }
    }


    return new EmptyResult();
}

Any help on this is highly appreciated.

Updated 1 (still same issue)

I just updated and simplified my code according to suggestions, but I still see same issue:

public ActionResult DownloadOrderReceipt(string id)
{
    var order = orderRepository.Get(id);
    if (order == null)
        return new EmptyResult();

    using (MemoryStream stream = new MemoryStream())
    {
        string htmlContent = "<p>Write this on PDF Page</p>";
        StringReader sr = new StringReader(htmlContent);
        Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 100f, 0f);
        PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
        pdfDoc.Open();
        XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr);
        pdfDoc.Close();
        return File(stream.ToArray(), "application/pdf", "Invoice_121212.pdf");
    }
}

This also works on dev machine but on pre-production this generates blank PDF page.

Updated 2 (still same issue)

My attempt to fix this working on pre-production is ON. Here's the another two samples which I just tried and no help.

public ActionResult DownloadOrderReceipt(string id)
{
    byte[] bytes;
    using (var ms = new MemoryStream())
    {
        using (var doc = new Document())
        {
            using (var writer = PdfWriter.GetInstance(doc, ms))
            {
                doc.Open();

                var example_html = @"<p>This <em>is </em><span class=""headline"" style=""text-decoration: underline;"">some</span> <strong>sample <em> text</em></strong><span style=""color: red;"">!</span></p>";
                var example_css = @".headline{font-size:200%}";

                // example 1
                //using (var srHtml = new StringReader(example_html))
                //{
                //    XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
                //}

                // example 2
                using (var msCss = new MemoryStream(Encoding.UTF8.GetBytes(example_css)))
                {
                    using (var msHtml = new MemoryStream(Encoding.UTF8.GetBytes(example_html)))
                    {
                        XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHtml, msCss);
                    }
                }

                doc.Close();
            }
        }

        bytes = ms.ToArray();
    }

    Response.ContentType = "application/pdf";
    Response.AddHeader("content-disposition", "attachment;filename=Invoice_1212121212.pdf");
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.BinaryWrite(bytes);
    Response.End();

    return new EmptyResult();
}

SOLUTION

Updating this question after long time, because i noticed new comments. In the process of code review I found that developer had wrote logic to minify HTML content dynamically that caused the bites removed from PDF too and hence empty PDF.

Community
  • 1
  • 1
Abhimanyu
  • 2,173
  • 2
  • 28
  • 44
  • 1
    (1.) You are using `HTMLWorker` which is deprecated. Read the [FAQ](http://developers.itextpdf.com/faq/category/parsing-xml-and-xhtml) and please understand that questions about `HTMLWorker` no longer receive an answer. (2.) If you get a file that looks like a PDF, but that contains nothing but blank pages, you have an encoding problem: you are treating a *binary file* as if it were plain ASCII. See http://stackoverflow.com/questions/20890291/itext-pdf-shows-blank-page (your question is probably a duplicate). – Bruno Lowagie Jan 07 '17 at 10:27
  • 1
    So the cause is most probably the configuration of `Response.OutputStream` on your production server. That production server assumes you are producing HTML (plain text), but you're producing PDF (which is a binary file format). The structure of a PDF is plain ASCII, which is why your PDF viewer can show pages. But the content of each page in the PDF file is compressed (and binary). Your server makes those bytes corrupt. – Bruno Lowagie Jan 07 '17 at 10:32
  • Thanks for the quick response Bruno. Do we have some update .NET samples if you know please share. – Abhimanyu Jan 07 '17 at 10:53
  • @BrunoLowagie just update my question with latest and simplified code but I still see exact same issue. This time no HTMLWorker or plain ASCII. – Abhimanyu Jan 07 '17 at 11:50
  • 2
    Show us the broken PDF. You admit that the code works on one machine, but not on another. **This means that your problem is NOT caused by iTextSharp.** Surely you understand that it doesn't make sense to share **code that works** and then ask *why doesn't this work?* There's a 90% chance that your server "shaves the bytes". Unfortunately, you don't seem to understand what is meant by this answer, but if you share two files (the one created correctly on your machine and the incorrect one), I can *show* you the difference and hopefully you'll trust your eyes and understand. – Bruno Lowagie Jan 07 '17 at 12:21
  • @BrunoLowagie this is not server issue and i'm sure, because another module in the same app uses iTextSharp and it still works well. that's my call now to fix. I posted my attempts because i was trying to get the code which causes the issue and i hope i'll get to that line very soon and update you. appreciate your effort on iTextSharp. – Abhimanyu Jan 07 '17 at 12:42
  • If it works on one machine but not on another, then the problem is not in your code but in your environment. Sharing code that works is easy. Sharing a server that works is hard. Maybe if you virtualize it in VirtualBox and upload several gigabytes of virtual machine? But who will want to download that? – Amedee Van Gasse Jan 08 '17 at 00:09
  • I had html compression logic running on server (but not on dev machine), this caused to remove bytes and then the issue. thanks Bruno. – Abhimanyu Jun 02 '17 at 06:14

1 Answers1

-2

This works for me, MemoryStream is important:

    public class PDFController : Controller
    {
    // GET: PDF
    public ActionResult Index(string address)
    {
        SelectPdf.HtmlToPdf converter = new SelectPdf.HtmlToPdf();
        SelectPdf.PdfDocument doc = converter.ConvertUrl(address);
        var bytes = doc.Save();
        MemoryStream mstream = new MemoryStream(bytes);
        doc.Close();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-disposition", "attachment;filename=kundenvereinbarung.pdf");
        Response.Buffer = true;
        mstream.WriteTo(Response.OutputStream);
        Response.End();
        return null;
    }
}
mlauth
  • 151
  • 12
  • 1
    I don't want to use another library for similar purpose and i hope you have introduced SelectPdf package. – Abhimanyu Jan 07 '17 at 11:55
  • 1
    @AbhimanyuKumarVatsa The library is not the point. It's about security. The IIS Application Pool does not have the same rights as your local user account, such as the windows temp folder. – mlauth Jan 07 '17 at 12:16