How to render an ASP.NET MVC View in PDF format

68,646

Solution 1

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.

Solution 2

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)

Solution 3

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

Share:
68,646

Related videos on Youtube

nkirkes
Author by

nkirkes

Human, biped, literate.

Updated on July 09, 2022

Comments

  • nkirkes
    nkirkes almost 2 years

    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!

    • jim tollan
      jim tollan about 13 years
      mannish - were you ever able to post your solution to this anywhere?? it would be increadibly useful. tia
  • nkirkes
    nkirkes over 14 years
    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
    nkirkes over 14 years
    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.
  • Hector Minaya
    Hector Minaya about 12 years
    this is by far the easiest solution I have come across for asp.net mvc, thanks a lot...
  • Mark Carpenter
    Mark Carpenter over 11 years
    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!
  • detay
    detay about 11 years
    Rotativa seems to fail with MVC4. When I return an ActionAsPdf I see a string displayed on the screen instead of a PDF.
  • Giorgio Bozio
    Giorgio Bozio almost 11 years
    Works in Azure Web Roles but doesn't work on Azure Web Sites because of limitations to the trust level.
  • Xavier John
    Xavier John about 10 years
    is there a solution for azure website?
  • Kurt Wagner
    Kurt Wagner about 10 years
    Any updates to the website issue? Would really like to use this library for an Azure website if possible~ :X
  • Giorgio Bozio
    Giorgio Bozio about 10 years
    No, sorry, the only solution is to use a Web Role instead of Website. This is due to limitations of Azure Websites.
  • abenci
    abenci almost 10 years
    What about saving these PDF docs on disk before displaying? For storing an invoice copy would be great...
  • Giorgio Bozio
    Giorgio Bozio almost 10 years
    @Alberto you can use the BuildPdf method.
  • HenrikP
    HenrikP over 9 years
    @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.
  • Giorgio Bozio
    Giorgio Bozio over 9 years
    @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...
  • Nic
    Nic almost 9 years
    Oh man , you are the hero, Great solutions, very simple to use.Awesome.Thanks a lot.
  • Slinky
    Slinky over 8 years
    Unfortunately, Rotativa does not work with MVC 4 for some reason: stackoverflow.com/questions/32014929/… @GiorgioBozio
  • JoshYates1980
    JoshYates1980 over 8 years
    This...is...amazing. Just one line of code! return new ActionAsPdf("BillingLetter", null) { FileName = "Bill.pdf" };
  • Giorgio Bozio
    Giorgio Bozio about 8 years
    I'm developing a SaaS solution that will also solve server install and execution issues, like running on Azure websites. You can register at rotativahq.com
  • MSTdev
    MSTdev almost 8 years
    @Giorgio Bozio not working for me it is showing only scroll bar and backgrounds only
  • Guruprasad J Rao
    Guruprasad J Rao almost 8 years
    How to pass model with ActionAsPdf?
  • Giorgio Bozio
    Giorgio Bozio almost 8 years
    @GuruprasadRao you don't, you ca do that when using ViewAsPdf. With ActionAsPdf you can pass route values.
  • Guruprasad J Rao
    Guruprasad J Rao almost 8 years
    @GiorgioBozio.. Yea, am using this right now.. :) Thanks so much for the awesome library.. :) You rock.. :)
  • SAR
    SAR over 7 years
    @GuruprasadRao , it's resulting wiht login page on pdf
  • Saurabh
    Saurabh almost 6 years
    @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
  • Giorgio Bozio
    Giorgio Bozio almost 6 years
    @Saurabh what version of .Net are you using? if it's an old one you should try an older version of Rotativa.
  • Saurabh
    Saurabh almost 6 years
    @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
  • Moiz Tankiwala
    Moiz Tankiwala almost 4 years
    I have flexbox based CSS and Rotativa fails to render that properly, so looking for a Rotativa alternative.
  • Belarmino Vicenzo
    Belarmino Vicenzo over 3 years
    @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
  • Light
    Light about 2 years
    @GiorgioBozio - with [Authorize] it does not work, if we use allow anonymous i'm able to print. Is this expected ?