Search code examples
c#.netpdfpdf-generationselectpdf

SelectPDF.Document.Footer is always null?


Creating a PDF document from the stream of a HTTP request.

public class HomeController : Controller {
    public HomeController() {
        converter = new HtmlToPdf();
        InitializeConverter();
    }

    public void Index() {
        ConvertHtmlToPdf(new Uri("http://localhost:52328/CertificateOfOrigin?noCertificate=2691"));
    }

    public void ConvertHtmlToPdf(Uri toConvert) {
        if(toConvert == null) throw new ArgumentNullException(nameof(toConvert));
        using(var stream =new MemoryStream()) {
            var doc = converter.ConvertUrl(toConvert.AbsoluteUri);
            // The doc.AddTemplate returns a PdfTemplate and should be assigned to doc.Footer
            doc.Footer = doc.AddTemplate(doc.Pages[0].ClientRectangle.Width, 100);
            var pageNumbering = new PdfTextElement(20, 50, "Page {page_number} of {total_pages}", doc.Fonts[0], Color.Black);
            // Once template defined, I add it to the doc Footer. But...
            doc.Footer.Add(pageNumbering); // Throws a NullPointerException?
            doc.Footer = template;
            doc.Save(stream);
            doc.Close();

            using(var ms = new MemoryStream(stream.ToArray())) {
                Response.AddHeader("content-disposition", "filename=certificate-of-origin.pdf");
                Response.ContentType = "application/pdf";
                ms.CopyTo(Response.OutputStream);
                Response.End();
                Response.Close();
            }
        }
    }

    private void InitializeConverter() {
        converter.Options.MarginBottom = 0;
        converter.Options.MarginLeft = 0;
        converter.Options.MarginRight = 0;
        converter.Options.MarginTop = 0;
        converter.Options.PdfPageSize = PdfPageSize.Letter;
    }

    private readonly HtmlToPdf converter;
}

I put a breakpoint and quick watched the return of doc.AddTemplate method call and it returns an actual PdfTemplate no problem!

Other than that, everything works fine. Document is generated no problem, except when I uncomment the page numbering because the doc.Footer remains null despite its assignment.

Could it be a bug? Idk.


Solution

  • You need to either set the header/footer content before the conversion, like here: https://selectpdf.com/demo-mvc/HtmlToPdfHeadersAndFooters

    using System;
    using System.Web.Mvc;
    
    namespace SelectPdf.Samples.Controllers
    {
        public class HtmlToPdfHeadersAndFootersController : Controller
        {
            // GET: HtmlToPdfHeadersAndFooters
            public ActionResult Index()
            {
                return View();
            }
    
            [HttpPost]
            public ActionResult SubmitAction(FormCollection collection)
            {
                // get parameters
                string headerUrl = Server.MapPath("~/files/header.html");
                string footerUrl = Server.MapPath("~/files/footer.html");
    
                bool showHeaderOnFirstPage = collection["ChkHeaderFirstPage"] == "on";
                bool showHeaderOnOddPages = collection["ChkHeaderOddPages"] == "on";
                bool showHeaderOnEvenPages = collection["ChkHeaderEvenPages"] == "on";
    
                int headerHeight = 50;
                try
                {
                    headerHeight = Convert.ToInt32(collection["TxtHeaderHeight"]);
                }
                catch { }
    
                bool showFooterOnFirstPage = collection["ChkFooterFirstPage"] == "on";
                bool showFooterOnOddPages = collection["ChkFooterOddPages"] == "on";
                bool showFooterOnEvenPages = collection["ChkFooterEvenPages"] == "on"; 
    
                int footerHeight = 50;
                try
                {
                    footerHeight = Convert.ToInt32(collection["TxtFooterHeight"]);
                }
                catch { }
    
                // instantiate a html to pdf converter object
                HtmlToPdf converter = new HtmlToPdf();
    
                // header settings
                converter.Options.DisplayHeader = showHeaderOnFirstPage ||
                    showHeaderOnOddPages || showHeaderOnEvenPages;
                converter.Header.DisplayOnFirstPage = showHeaderOnFirstPage;
                converter.Header.DisplayOnOddPages = showHeaderOnOddPages;
                converter.Header.DisplayOnEvenPages = showHeaderOnEvenPages;
                converter.Header.Height = headerHeight;
    
                PdfHtmlSection headerHtml = new PdfHtmlSection(headerUrl);
                headerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
                converter.Header.Add(headerHtml);
    
                // footer settings
                converter.Options.DisplayFooter = showFooterOnFirstPage ||
                    showFooterOnOddPages || showFooterOnEvenPages;
                converter.Footer.DisplayOnFirstPage = showFooterOnFirstPage;
                converter.Footer.DisplayOnOddPages = showFooterOnOddPages;
                converter.Footer.DisplayOnEvenPages = showFooterOnEvenPages;
                converter.Footer.Height = footerHeight;
    
                PdfHtmlSection footerHtml = new PdfHtmlSection(footerUrl);
                footerHtml.AutoFitHeight = HtmlToPdfPageFitMode.AutoFit;
                converter.Footer.Add(footerHtml);
    
                // add page numbering element to the footer
                if (collection["ChkPageNumbering"] == "on")
                {
                    // page numbers can be added using a PdfTextSection object
                    PdfTextSection text = new PdfTextSection(0, 10,
                        "Page: {page_number} of {total_pages}  ",
                        new System.Drawing.Font("Arial", 8));
                    text.HorizontalAlign = PdfTextHorizontalAlign.Right;
                    converter.Footer.Add(text);
                }
    
                // create a new pdf document converting an url
                PdfDocument doc = converter.ConvertUrl(collection["TxtUrl"]);
    
                // custom header on page 3
                if (doc.Pages.Count >= 3)
                {
                    PdfPage page = doc.Pages[2];
    
                    PdfTemplate customHeader = doc.AddTemplate(
                        page.PageSize.Width, headerHeight);
                    PdfHtmlElement customHtml = new PdfHtmlElement(
                        "<div><b>This is the custom header that will " +
                        "appear only on page 3!</b></div>",
                        string.Empty);
                    customHeader.Add(customHtml);
    
                    page.CustomHeader = customHeader;
                }
    
                // save pdf document
                byte[] pdf = doc.Save();
    
                // close pdf document
                doc.Close();
    
                // return resulted pdf document
                FileResult fileResult = new FileContentResult(pdf, "application/pdf");
                fileResult.FileDownloadName = "Document.pdf";
                return fileResult;
            }
        }
    }
    

    Or use this approach, to add headers/footers to an already generated pdf: https://selectpdf.com/demo-mvc/ExistingPdfHeadersAndFooters

    using System.Web.Mvc;
    using System.Drawing;
    
    namespace SelectPdf.Samples.Controllers
    {
        public class ExistingPdfHeadersAndFootersController : Controller
        {
            // GET: ExistingPdfHeadersAndFooters
            public ActionResult Index()
            {
                return View();
            }
    
            [HttpPost]
            public ActionResult SubmitAction(FormCollection collection)
            {
                // the test file
                string filePdf = Server.MapPath("~/files/selectpdf.pdf");
                string imgFile = Server.MapPath("~/files/logo.png");
    
                // resize the content
                PdfResizeManager resizer = new PdfResizeManager();
                resizer.Load(filePdf);
    
                // add extra top and bottom margins
                resizer.PageMargins = new PdfMargins(0, 0, 90, 40);
    
                // add the header and footer to the existing (now resized pdf document)
                PdfDocument doc = resizer.GetDocument();
    
                // header template (90 points in height) with image element
                PdfTemplate header = doc.AddTemplate(doc.Pages[0].ClientRectangle.Width, 90);
                PdfImageElement img1 = new PdfImageElement(10, 10, imgFile);
                header.Add(img1);
    
                // footer template (40 points in height) with text element
                PdfTemplate footer = doc.AddTemplate(new RectangleF(0,
                    doc.Pages[0].ClientRectangle.Height - 40,
                    doc.Pages[0].ClientRectangle.Width, 40));
    
                // create a new pdf font
                PdfFont font2 = doc.AddFont(PdfStandardFont.Helvetica);
                font2.Size = 12;
    
                PdfTextElement text1 = new PdfTextElement(10, 10,
                    "Generated by SelectPdf. Page number {page_number} of {total_pages}.",
                font2);
                text1.ForeColor = System.Drawing.Color.Blue;
                footer.Add(text1);
    
                // save pdf document
                byte[] pdf = doc.Save();
    
                // close pdf document
                resizer.Close();
    
                // return resulted pdf document
                FileResult fileResult = new FileContentResult(pdf, "application/pdf");
                fileResult.FileDownloadName = "Document.pdf";
                return fileResult;
            }
        }    
    }
    

    The best approach is the first, so try to move your footer setting before the conversion.