Using PDFConverter (https://www.html-to-pdf.net) with Https in ASP.NET MVC

 

During one of the ASP.NET MVC projects, we encountered a challenging scenario. First is – every request was supposed to made via HTTPS and Second – there was a requirement to generate PDF for a search output screen. Well – I thought the first one was quite straight forward and implemented a [RequireSSL] attribute. My solution was working when I was pressing F5 from Visual Studio but that was not the real test. So – I went ahead and published the website to localhost. After this, I installed a self signed certificate and tried https://localhost – it worked but with the certificate error. Now I tried https://mymachinename – it worked without certificate error. We were good to go with the first requirement and I was on cloud 9.

Interesting part started when client asked us to suggest a PDG generator tool. We tried couple of them and ExpertPDF from https://html-to-pdf.net  was looking the most appropriate tool for our need. We also tried using the tool on our https solution and it was working fine as long as we give https://mymachinename to access the url. In other words, it was working fine if we give the certification issuer name correctly. We suggested the tool to the client and here started another nightmare. When we fully developed the PDF generation code, it was working fine in dev environment (as usual). However – in staging/prod it failed. We were getting 2 exceptions and both were related to SSL:

1. We were using a header image and it was coming from an URL.

2. We were using GetBytesFromUrl() method of PDFConverter API and passing a view (used for formatting) with parameters.

By this time, we had the information that in both staging/production environments, the servers are load balanced and behind the firewall. Also – the certificate is not issued for each load balanced server but for the farm as a whole.

To resolve this, we passed the image as a embedded resource and instead of a HTML url, We got the rendered HTML as string and passed to GetBytesFromHTMLString() method of the pdfconverter. I am going to post the code next time in addition to this article.

As Promised, here is the code:

  
    public virtual ActionResult GeneratePDF()
    {
       MyViewModel viewModel = GetViewModel();  //This is a private method which gives the model for the view
             try
            {
                htmlString = RenderViewToString<SettlementHistoryViewModel>("~/Views/MyController/MyPDFView.ascx", objectViewData);
            }
            catch { }

            //initialize the PdfConvert object
            PdfConverter pdfConverter = new PdfConverter();
            pdfConverter.PdfDocumentOptions.PdfPageSize = PdfPageSize.A4;
            pdfConverter.PdfDocumentOptions.PdfCompressionLevel = PdfCompressionLevel.Normal;
            pdfConverter.PdfDocumentOptions.ShowHeader = true;
            pdfConverter.PdfDocumentOptions.ShowFooter = true;
            pdfConverter.PdfHeaderOptions.HeaderHeight = 140;
            pdfConverter.PdfHeaderOptions.DrawHeaderLine = false;

            pdfConverter.PdfHeaderOptions.HeaderImage = MyResources.reportheader1; //The image is set to be embedded resource
            pdfConverter.PdfHeaderOptions.HeaderImageLocation = new PointF(20, 20);
            pdfConverter.PdfHeaderOptions.HeaderTextAlign = HorizontalTextAlign.Right;

            FontFamily fontFamily = null;
            Font font = null;
            FileContentResult file = null;

            try
            {
                fontFamily = new FontFamily("Times New Roman");
                font = new Font(fontFamily, 10, GraphicsUnit.Point);
                pdfConverter.PdfHeaderOptions.TextArea = new TextArea(500, 30, "Page &p;", font);
                pdfConverter.PdfFooterOptions.ShowPageNumber = false;

                byte[] pdfBytes = pdfConverter.GetPdfBytesFromHtmlString(htmlString);

                file = File(pdfBytes, "application/pdf", string.Format("MyPDF_{0}.pdf", DateTime.Now.ToShortDateString().Replace('/', ' ')));
            }
            catch
            {
                throw;
            }
            finally
            {
                if (fontFamily != null) fontFamily.Dispose();
                if (font != null) font.Dispose();
            }

            return file;
        }

       
        /// <summary>
        /// This method is used to get the rendered HTML from a view by passing the model parameter.
        /// </summary>
        /// <typeparam name="T">Type of Model</typeparam>
        /// <param name="viewPath">Relative Path of the View</param>
        /// <param name="model">Model for View</param>
        /// <returns></returns>
        protected string RenderViewToString<T>(string viewPath, T model)
        {
            using (var writer = new StringWriter())
            {
                var view = new WebFormView(viewPath);
                var vdd = new ViewDataDictionary<T>(model);
                var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer);
                viewCxt.View.Render(viewCxt, writer);
                return writer.ToString();
            }
        }