27

I'm working with ExpertPDF's Html-to-PDF conversion utility for this question (although I'm open to other libraries if there's sufficient documentation).

In short, I have a view that is formatted a specific way and I would like to render it as a PDF document the user can save to disk.

What I have so far is a PrintService (which implements an IPrintService interface) and this implementation has two overloads for PrintToPDF(), one that takes just a URL and another that takes an HTML string, and both of which return a byte[]. I've only worked out the details of the second overload which requires the HTML string.

What I would like to do from my controller is something like:

public FileStreamResult Print(int id)
{
    var model = _CustomRepository.Get(id);
    string renderedView = SomethingThatRendersMyViewAsAString(model);
    Stream byteStream = _PrintService.PrintToPdf(renderedView);
    HttpContext.Response.AddHeader("content-disposition", 
        "attachment; filename=report.pdf");
    return new FileStreamResult(byteStream, "application/pdf");  
}

which in theory would render a PDF to the page. It's the "SomethingThatRendersMyViewAsAString" that I'm looking for help with. Is there a quick way to get the string representation of a View? Or perhaps I should just stick with the URL overload and pass in a URL to the view... Any other thoughts?

Thanks!

nkirkes
  • 2,635
  • 2
  • 21
  • 36
  • 6
    mannish - were you ever able to post your solution to this anywhere?? it would be increadibly useful. tia – jim tollan Mar 08 '11 at 10:41

4 Answers4

103

I packaged my solution in a Nuget package: Rotativa http://nuget.org/packages/Rotativa. It's based on wkhtmltopdf.

Usage is really simple.

Having an action you would like to serve as Pdf, instead of Html page. You can define an action that returns an ActionResult of the type ActionAsPdf (RouteAsPdf is also available). So the code is just:

public ActionResult PrintIndex()
{
    return new ActionAsPdf("Index", new { name = "Giorgio" }) { FileName = "Test.pdf" };
}

With name = "Giorgio" being a route parameter.

It works even if the action to print is protected by web forms authentication ([Authorize] attribute)

Giorgio Bozio
  • 2,982
  • 3
  • 20
  • 20
  • 5
    this is by far the easiest solution I have come across for asp.net mvc, thanks a lot... – Hector Minaya Feb 23 '12 at 20:32
  • 2
    This is fantastic - thank you! I'll note that I had to specify the forms authentication cookie name (otherwise the login page would be rendered) and change the renderer to use print media styles (FormsAuthenticationCookieName = "MyCookie", CustomSwitches = "--print-media-type"). Very easy! – Mark Carpenter Jan 10 '13 at 16:45
  • 1
    Rotativa seems to fail with MVC4. When I return an ActionAsPdf I see a string displayed on the screen instead of a PDF. – detay Mar 26 '13 at 14:55
  • 3
    Works in Azure Web Roles but doesn't work on Azure Web Sites because of limitations to the trust level. – Giorgio Bozio Jul 12 '13 at 14:48
  • is there a solution for azure website? – Xavier John Mar 18 '14 at 05:04
  • Any updates to the website issue? Would really like to use this library for an Azure website if possible~ :X – Kurt Wagner Apr 15 '14 at 23:23
  • No, sorry, the only solution is to use a Web Role instead of Website. This is due to limitations of Azure Websites. – Giorgio Bozio Apr 16 '14 at 07:54
  • What about saving these PDF docs on disk before displaying? For storing an invoice copy would be great... – abenci Jul 17 '14 at 08:35
  • @Alberto you can use the BuildPdf method. – Giorgio Bozio Jul 25 '14 at 10:05
  • @GiorgioBozio This works great even locally and on my webhost. But I've heard that wkhtmltopdf uses a lot of resources, but those comments are kinda old by now. How stable is it nowadays? or can you expect the server to run out of process power when running this? I'm not really run these functions a lot though. – HenrikP Oct 09 '14 at 08:05
  • @HenrikP wkhtmltopdf should be pretty fast but didn't do any load testing. Never heard of performance problems though. I has other problems that make me think about providing different "pdf engines" other that wkhtmltopdf. But actually I don't have time to work on it. If you have it will be great if you could collaborate... – Giorgio Bozio Oct 09 '14 at 08:35
  • Oh man , you are the hero, Great solutions, very simple to use.Awesome.Thanks a lot. – Nic Jul 14 '15 at 13:16
  • 1
    Unfortunately, Rotativa does not work with MVC 4 for some reason: http://stackoverflow.com/questions/32014929/rotativa-not-working-in-mvc-4 @GiorgioBozio – Slinky Aug 14 '15 at 19:20
  • This...is...amazing. Just one line of code! return new ActionAsPdf("BillingLetter", null) { FileName = "Bill.pdf" }; – JoshYates1980 Aug 27 '15 at 12:56
  • I'm developing a SaaS solution that will also solve server install and execution issues, like running on Azure websites. You can register at http://rotativahq.com – Giorgio Bozio Feb 20 '16 at 21:40
  • @Giorgio Bozio not working for me it is showing only scroll bar and backgrounds only – MSTdev May 13 '16 at 13:57
  • How to pass `model` with `ActionAsPdf`? – Guruprasad J Rao Jul 09 '16 at 05:50
  • @GuruprasadRao you don't, you ca do that when using ViewAsPdf. With ActionAsPdf you can pass route values. – Giorgio Bozio Jul 21 '16 at 14:49
  • @GiorgioBozio.. Yea, am using this right now.. :) Thanks so much for the awesome library.. :) You rock.. :) – Guruprasad J Rao Jul 21 '16 at 17:24
  • 4
    @GuruprasadRao , it's resulting wiht login page on pdf – SAR Dec 17 '16 at 07:14
  • @GiorgioBozio , in asp.net mvc5 , able to include in from nuget but its not available in controller , 'using Rotavita' statement gives error that cant find the reference – Saurabh May 17 '18 at 04:33
  • @Saurabh what version of .Net are you using? if it's an old one you should try an older version of Rotativa. – Giorgio Bozio May 18 '18 at 07:19
  • @GiorgioBozio , i was trying on 4.5 , i also tried changing the target framework to 4.6.1 , it does create pdf but its blank and funny thing is if i check bookmark section than it show bokkmark there but pdf is white blank. Works well with .net core 2.0 . Which version of Rotattiva you suggesting for .net 4.5 – Saurabh May 18 '18 at 11:22
  • I have flexbox based CSS and Rotativa fails to render that properly, so looking for a Rotativa alternative. – Moiz Tankiwala May 20 '20 at 04:04
  • @GiorgioBozio I don't think it works with [Authorize] I had to create an action only to print and had the [Allow Anonymous] to be able to print. Now it works, if u say it works with [Authorize], I would like a hand, bro – Belarmino Vicenzo Nov 07 '20 at 09:08
  • @GiorgioBozio - with [Authorize] it does not work, if we use allow anonymous i'm able to print. Is this expected ? – Light Feb 20 '22 at 18:41
10

You might be able to tap into the Response during OnResultExecuting and replace the Filter property with something that stores the resultant HTML in a MemoryStream. Then you could clear the Response during OnResultExecuted and replace it with the results of your PDF conversion. I'm not sure that this would be better than just getting the HTML from the URL, though.

 public FileStreamResult Print(int id)
 {
     var model = _CustomRepository.Get(id);
     this.ConvertToPDF = true;
     return View( "HtmlView" );
 }

 public override OnResultExecuting( ResultExecutingContext context )
 {
      if (this.ConvertToPDF)
      {
          this.PDFStream = new MemoryStream();
          context.HttpContext.Response.Filter = new PDFStreamFilter( this.PDFStream );
      }
 }

 public override OnResultExecuted( ResultExecutedContext context )
 {
      if (this.ConvertToPDF)
      {
          context.HttpContext.Response.Clear();
          this.PDFStream.Seek( 0, SeekOrigin.Begin );
          Stream byteStream = _PrintService.PrintToPDF( this.PDFStream );
          StreamReader reader = new StreamReader( byteStream );
          context.HttpContext.Response.AddHeader( "content-disposition",
                 "attachment; filename=report.pdf" );
          context.HttpContext.Response.AddHeader( "content-type",
                 "application/pdf" );
          context.HttpContext.Response.Write( reader.ReadToEnd() );
      }
}

The PDFStreamFilter would need to override the "Write" method(s) and send the data to the memory stream instead.

J Wynia
  • 10,464
  • 4
  • 40
  • 38
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • I got a solution worked out that resulted in less code, but the answer you provided was a potential solution. Good idea to intercept the Result execution... I'll post my solution soon. – nkirkes Aug 27 '09 at 22:22
1

This sounds like a similar problem I had where I wanted to use Views as email templates. The best answer I found for getting the string representation of a View was here: Render a view as a string

Community
  • 1
  • 1
Jason
  • 1,766
  • 2
  • 14
  • 24
  • I saw that post. I ended up using a similar solution, based on a comment on the brightmix post. I'll post the solution soon. – nkirkes Aug 27 '09 at 22:20
0

The best package I've found is the RazorPDF, available as a package at NuGet.org, based on iTextSharp. Works on Azure Web Sites:

https://nuget.org/packages/RazorPDF

Leonel Sanches da Silva
  • 6,972
  • 9
  • 46
  • 66