Search code examples
pdfmandrillpdfsharp

Attach a PDF file to a Mandrill email using PDFsharp


I have been working on a project that requires me to attach a PDF document (a fax cover page populated with dynamic data) via Mandrill email and I had the worst time sorting through the scores of different methods to accomplish this seemingly-simple task. Here are the specifics of my project:

  • It is a Console App
  • I parse data from files and email that data via Mandrill


Now I need to:

  • Convert that same parsed data into a PDF
  • Attach this newly-created PDF to my Mandrill email
  • Dispose of this PDF file on the [local] machine

So what is the most efficient and lightest way to accomplish this?


Solution

  • I have compiled and tested my newly-created project and the following works extremely well (I hesitate to say 'flawlessly' or 'perfectly' since I am sure someone will find a better way to accomplish what I did). As you are about to see, I save my data into images and put these images onto the newly-created PDF pages.


    Parent method

    public static void SendEmail()
    {
        try
        {
            /*
                Standard code to prepare Mandrill email. 
                'EmailMessageObj' is the Mandrill.EmailMessage object
            */
    
            //Create PDF with data
            Pdf pdfObject = new Pdf();
            PdfDocument document = Pdf.Document(param1, param2, pdfObject); //create PDF document and pass in object's data. Sets pdfObj properties, such as the byte[] PdfBytes seen below
    
            using (MemoryStream stream = new MemoryStream())
            {
                // Create email attachment
                EmailMessageObj.attachments = new[]
                    {
                        new attachment
                            {
                                content = Convert.ToBase64String(pdfObject.PdfBytes),
                                name = "Fax Cover Sheet.pdf",
                                type = "application/pdf"
                            }
                    };
            }
    
            api.SendMessage(EmailMessageObj);
        }
        catch (Exception error)
        {
            Console.WriteLine("Could not send email." + error);
        }
    }
    


    Pdf object
    I added references to: 'HtmlRenderer', 'HtmlRenderer.WinForms', 'PdfSharp'

    using PdfSharp.Drawing;
    using PdfSharp.Pdf;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using TheArtOfDev.HtmlRenderer.WinForms;
    
    class Pdf
        {
            public string PdfFileDirectory { get; set; }
            public string PdfCoverFileName { get; set; }
    
            public string LogoFileName { get; set; }
    
            public Image PageOne { get; set; }
            public Image PageTwo { get; set; }
            public byte[] PdfBytes { get; set; }
    
            public Pdf()
            {
                PdfFileDirectory = Settings.BaseBaseDirectory + @"\";
                PdfCoverFileName = "FaxCoverPage.pdf";
    
                LogoFileName = "Logo.jpg";
    
                PageOne = null;
                PageTwo = null;
                PdfBytes = null;
            }
    
            /// <summary>
            /// The PDFsharp PDF Document. This creates images out of the data and then puts those images onto the (created within) PDF pages.
            /// </summary>
            /// <param name="addressedTo">The person this is being sent/addressed to</param>
            /// <param name="dataFormattedIntoTable">The formatted data table</param>
            /// <param name="pdfObj">The Pdf object</param>
            /// <returns></returns>
            public static PdfDocument Document(string addressedTo, string dataFormattedIntoTable, Pdf pdfObj)
            {
                //See https://htmlrenderer.codeplex.com/wikipage?title=Image%20generation for documentation
    
                //First Page (To, From, disclaimer, etc.)
                pdfObj.PageOne = HtmlRender.RenderToImageGdiPlus(CoverPage_Front(addressedTo), new Size(600, 0), new Size(800, 0)); //Create an image from passed-in data. Set Min/Max size restriction
                using (MemoryStream stream = new MemoryStream())
                {
                    pdfObj.PageOne.Save(stream, ImageFormat.Png); //save via stream so I do not have to save this file locally and can then dispose of it, thus freeing things up
                }
    
                //Second Page (logo and data table)
                pdfObj.PageTwo = HtmlRender.RenderToImageGdiPlus(CoverPage_Content(addressedTo, dataFormattedIntoTable), new Size(600, 0), new Size(800, 0)); //Create an image from passed-in data. Set Min/Max size restriction
                using (MemoryStream stream = new MemoryStream())
                {
                    pdfObj.PageTwo.Save(stream, ImageFormat.Png); //save via stream so I do not have to save this file locally and can then dispose of it, thus freeing things up
                }
    
                // Create a new PDF document
                PdfDocument document = new PdfDocument();
    
                // Create an empty page
                PdfPage page = document.AddPage();
    
                // Get an XGraphics object for drawing
                XGraphics gfx = XGraphics.FromPdfPage(page);
    
                //I only want two pages so..
                for (int pageCounter = 0; pageCounter < 2; pageCounter++)
                {
                    if (pageCounter == 0)
                    {
                        // draw Fax cover page (To, From, disclaimer, etc.)
                        gfx.DrawImage(pdfObj.PageOne, 0f, 35f); //fax cover page image, margin-left, margin-top
    
                        // since this is the first page, add a second one. This will NOT be done on subsequent pages because we are planning on only have 2 pages for now
                        page = document.AddPage(); //add the second page
                        gfx = XGraphics.FromPdfPage(page);
                    }
                    else
                    {
                        // draw logo
                        Image LogoFileName = Image.FromFile(pdfObj.PdfFileDirectory + pdfObj.LogoFileName); //I store the logo locally
                        gfx.DrawImage(LogoFileName, 130f, 35f); //logo image, margin-left, margin-top
    
                        // draw Table with passed-in data
                        gfx.DrawImage(pdfObj.PageTwo, 0f, 100f); //the main content. The margin-top is set to 100f so it sits below the logo.
                    }
                }
    
                using (MemoryStream stream = new MemoryStream())
                {
                    document.Save(stream, true); //save via stream so I do not have to save this file locally and can then dispose of it, thus freeing things up
                    pdfObj.PdfBytes = stream.ToArray(); //save the byte array of the stream to the object's byte[] property. Mandrill attachments need to be byte arrays and THIS is what we will be attaching
                }
    
                return document;
            }
    
            /// <summary>
            /// The Fax Cover Page is two pages - this is the first one. It displays the From, To, and disclaimer
            /// </summary>
            /// <param name="addressedTo">The name of the person this is being sent to.</param>
            /// <returns></returns>
            public static string CoverPage_Front(string addressedTo)
            {
                // Obviously altered.. You may format your fax cover sheet however you like
                return @"<html>
                            <body>
                                <div>
                                    To: " + addressedTo + @", Cappuccino catcher luxurious admiral charity donate, luxurious big daft brush mouth coiffure clive dunn dickie davies charity donate admiral give him what-for john cleese. By jingo. big daft brush joseph stalin charming villain.
                                </div>
                            </body>
                        </html>";
            }
    
            /// <summary>
            /// The Fax Cover Page is two pages - this is the second one. It displays the logo and the table of data
            /// </summary>
            /// <param name="addressedTo"></param>
            /// <param name="dataFormattedIntoTable"></param>
            /// <returns></returns>
            public static string CoverPage_Content(string addressedTo, string dataFormattedIntoTable)
            {
                // Obviously altered..
                return @"<html>
                            <body>
                                <div>
                                    <table border='0' cellpadding='0' style='background: #E8EBEB;'>
                                        <tbody>
                                            <tr>
                                                <td style='padding: .75pt .75pt .75pt .75pt'>
                                                    <table border='0' cellpadding='0' style='background: white;'>
                                                        <tbody>
                                                            <tr>
                                                                <td style='border: none; border-bottom: solid #004A8F 4.5pt; padding: 0in 7.5pt 0in 7.5pt'>
                                                                    To: " + addressedTo + @", Facial accessory doctor watson. wario arcu, yeoman farmer wario blue oyster bar charlie chaplin old man in pub hungarian nefarious cesar romero facial accessory doctor watson. arcu glorious facial hair? Frontiersman en time-warped cabbie great dictator um yesbaby toothbrush clone zone shopper. 
    
                                                                    " + dataFormattedIntoTable + @"
                                                                </td>
                                                            </tr>
                                                        </tbody>
                                                    </table>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                            </body>
                        </html>";
            }
        }
    


    I really hope this helps you!! This took me some time to figure out and it works very well now. Please let me know if there are any improvements to make to this and I will update my code (and this post)!