Reputation: 453
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:
Now I need to:
So what is the most efficient and lightest way to accomplish this?
Upvotes: 0
Views: 1449
Reputation: 453
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)!
Upvotes: 1