After the usual google search I came up with a couple of things I thought might be useful and with a bit of patience came up with the following solution.
- Create a view of the item I want to print (along with the controller)
- Convert the html to string
- Use a tool based on WkHtmlToPdf to create the pdf. (Pechkin)
1.Create a view of the item I want to print
This is pretty straight forward, create a new view probably without a master layout. The only slight change is css has to be part of the view.
If you want to keep items together you can use
page-break-inside: avoid;
2.Convert the html to string
This uses the view engine to render the html with the data.
firstly we assign our model to the view
this.ViewData.Model = vm;
Then we need to do the magic
using (StringWriter stringWriter = new StringWriter()) { ViewEngineResult viewResult = ViewEngines.Engines.FindView(this.ControllerContext, "Print", null); ViewContext viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, stringWriter); viewResult.View.Render(viewContext, stringWriter); var st = stringWriter.GetStringBuilder().ToString(); }so we now have a string "st" with the html in it.
3. Export to PDF.
So to export to pdf I used a tool based on WkHtmlToPdf, the reason for this is it uses Webkit to render the page, before exporting to pdf. It did a much better job of it than plain iTextSharp.
The tool I used is Pechkin which also has a nuget package Pechkin.Synchronized (which is also a winner :-) )
There are 2 config objects you can set. GlobalConfig and ObjectConfig
global config
This sets the page size and also bookmarks etc.
GlobalConfig gc = new GlobalConfig(); // set it up using fluent notation because we can gc.SetDocumentTitle("A Title ") .SetPaperSize(PaperKind.A4) .SetOutlineGeneration(true);
object config
This allows me to add colours to my background, a footer and set the font size
ObjectConfig oc = new ObjectConfig(); oc.SetPrintBackground(true); oc.Footer.SetFontSize(8); oc.Footer.SetLeftText(" Generated By:" + HttpContext.User.Identity.Name + " on " + DateTime.Now.ToString("dd-MMM-yyyy HH:mm"));
Generating the pdf
This generates the pdf, converts it to a byte array and post it back to the browser. We pass the global config in when we create the object, and object config when we create the document
var pechkin = new Pechkin.Synchronized.SynchronizedPechkin(gc); return File(pechkin.Convert(oc,st), "application/pdf");One of the nice things with this approach is you can use a "normal" controller to test the html and call the view like so (assuming your printable view is called "print"
return View("print", vm);The in javascript I called the controller in the normal way
window.open("/controller/Print?id=" + id);I hope this is of help.
Update
a couple of gotchas on deploy,
On IIS 7 I had to enable 32 bit application on the Application Pool
and needed a redirect on the common.logging dll in web.config