Creating PDF docs in Asp.Net MVC is a fairly common functionality requested by LOB applications. Usually either for reporting or, more often, for having printable friendly documents (invoices, receipts etc).
I used to design a Crystal report document and then evaluate it and convert it to PDF in the web application. The desing part was ok but what really bothered me was the problems that often came up with deploying it. Version mismatches could really turn it into a nightmare.
Lately I came up using the excellent wkhtmltopdf tool to convert html content to PDF. It uses the WebKit engine (used by Chrome and Safari) to render html. The nice thing about it is that I can leverage my knowledge of html and css to obtain a good looking PDF and also it's quite fast.
It's a exe file not a dll library (a dll actually exists but it's not managed code and it has some problems related to usage in a multi-threaded app). It has to be executed from the Asp.net app spawning a process, so it requires some unusual coding from a web development perspective. Another downside is that it requires some work to set it up: copying exe and dll files in the web app solution, make sure they get copied over when building and publishing and some config stuff needed to make it access authenticated actions.
It seemed to me a perfect candidate to become a Nuget package. It was fairly easy to build it, except some quirks when trying to modify package properties.
I've named it Rotativa /rota'tiva/, which is Italian for rotary printing press.
With Rotativa all it takes to print a PDF is:
1. Install using the Nuget package manager (search for Rotativa) or, more conveniently, with the package manager console, typing:
2. Writing a controller action to return a view representing the desired PDF output. This means just coding as usual to serve the data as regular MVC action. Such as:Install-Package Rotativa
Nothing special here, just an action. Perhaps the view used will have a different master page (or layout page) then the other views of the web app (or you’ll define a separete css for the print media, see http://www.w3.org/TR/CSS2/media.html). You can test and “preview” the results just using the browser, since we are working with a regular action, returning a html result.public ActionResult Invoice(int invoiceId) { var invoiceViewModel; // code to retrieve data from a database return View(invoiceViewModel); }
3. When the html is ok you can just setup a special action returning a custom ActionResult: ActionAsPdf. The code will look like this:
ActionAsPdf requires a string parameter with the name of the action to be converted to PDF. It can accept a parameter with other route data, in this case we are defining the invoiceId parameter. You can specify the name of the file being returned using the FIleName property.public ActionResult PrintInvoice(int invoiceId) { return new ActionAsPdf( "Invoice", new { invoiceId= invoiceId }) { FileName = "Invoice.pdf" }; }
To link to the PDF file in other views you just have to insert a regular link to the PrintInvoice action. Something like
@Html.ActionLink(“PrintInvoice”)if you’re working with razor views.
And voila, that’s all and it just works, even if the printed action is protected with forms authentication.
Source code is on GitHub https://github.com/webgio/Rotativa.
Great! ASAP i will try your solution!
ReplyDeleteThank you Boy! ;) Let me know if you have problems/ideas...
ReplyDeleteaaaddd
Deletemnlknk
Deleteok
DeleteNice tool Giorgio, however the IT department upgraded Adobe Reader to the newest version.
ReplyDeleteI now get a message from Adobe Reader when I open the PDF file, that says that my PDF file is corrupted.
Any solution for this?
Hi Julian, just tried the latest version of the Acrobat Reader with Rotativa and it seems to work... Are you able to open other pdf files? Does it work from computers with previous versions of Acrobat reader? Could you send me a copy of a sample pdf you are not able to open? Thanks, Giorgio
DeleteHi, i get this error from Nuget "Install-Package : Could not install package 'Rotativa 1.2.1'. You are trying to install this package into a project that targets '.NETFramework,Version=v3.5', but the package does not contain any assembly references that are compatible with that framework. For more information, contact the package author."
ReplyDeleteI downloaded the git version and manually changed target to 3.5, now it works, thanks
DeleteWow! It installed and worked right out of the box, without a hassle. Thanks for this excellent contribution. Now I need to figure out how to force some page breaks for a cleaner looking PDF. Any ideas on where to look for that solution?
ReplyDeleteThanks,
-dqj (Ithaca, NY USA)
Hi dqj! Thanks for using Rotativa.
DeleteYou can force page breaks with css using page-break-after and page-break-before http://www.w3schools.com/cssref/pr_print_pageba.asp
Giorgio
I added a CSS class to some elements:
Delete.break { page-break-after: always; }
but I get no page breaks in the resulting pdf?
Thanks
.breakBefore { page-break-before: always; } --> does the trick!
DeleteThanks.
I don't understand how to use requests that require authentication. When I'm creating a PDF from an action that requires authentication, it just prints out the login screen. Ideally, I would like to pass the credentials of the user that is currently logged in. Where can I specify this?
ReplyDeleteAs long as the user requesting the pdf is already authenticated, it should work without doing anything. Only, if you use a different cookie from the standard web forms auth cookie, then you can define a different cookie name as an optional property of the ActionAsPdfResult. The property name is Cookie, you can set it as you set the Filename prop.
DeleteHope this helps.
Thanks! I'm using an active directory authentication method, which sets a different cookie. I passed that cookie name into your code and it's working perfect. Thank you!
DeleteOne other quick question, is it possible to use an array as a parameter? For example, in the action I'm passing "ActionAsPdf", one of the parameters is a string[]. If I set my route value to the string array, System.String[] is received on the other end, instead of the actual value. From the request, my query string could be "my-url?state=TN&state=TX&", which would get fed into my string[] state parameter. When ActionAsPdf is called, the array values are not passed along.
DeleteI think I found the answer to this, the UrlHelper that gets called in GetUrl() is the cause, and some custom code may be required for array types. See: http://stackoverflow.com/questions/1752721/asp-net-mvc-routedata-and-arrays
DeleteBrosto (or Giorgio)--I have the same issue as my deployment is for an intranet that uses AD authentication. Now that I have built to the server, I am getting "Error: Authentication Required" when I attempt to execute an `ActionAsPdf`.
DeleteI tried setting `FormsAuthenticationCookieName = ".ADAuthCookie"` but it didn't work. Any thoughts?
Thanks,
Matt
How can we have the generated pdf saved to file or send straight up in an email as attachment without having the user download the file
ReplyDeleteIf you mean saved on the server, it's not possible. In order to do that you should create pdf in code and save it on the server. As for the sending email directly, I think i's not possible to do that on the web.
DeleteGiorgio, does it work on asp.net mvc 2?
ReplyDeleteDiego, in fact I didn't test it on asp.net mvc 2. BTW it makes sense it should work, it shouldn't use any feature specific to mvc 3. Have you tried it?
DeleteHi thanks for this, Prove in a normal mvc application worked. But try to prove in an application with azure and I created a PDF that says Bad Request - Invalid Hostname
ReplyDeleteHTTP Error 400. The request is invalid hostname. Any idea why this?
Hi Irving,
Deleteyou could have some problem with Dev Fabric load balancer. Have you tried deploying to Azure?
Hi Giorgio,
DeleteI encountered this issue as well, and you are correct, generating a PDF when running in Azure proper seems to work. Do you have any idea how to configure the emulator to allow it to work locally? It would be a bummer to have to deploy just to test views out.
BTW - awesome tool!
Thanks
Greate work, You should protect your self from commerical packages owners.
ReplyDeleteThanks!
DeleteWhat do you mean by " protect your self from commerical packages owners."? What would they do?
Great work Giorgio. Definitely a step in the right direction. Many steps in fact.
ReplyDeleteDo you have any advice on getting style="page-break-inside: avoid" to work? My divs are still being broken across pages.
Hi Giorgio. Great work. Thanks. But one issue. It works fine for me locally, but when deployed to IIS7 the file saves with 0 bytes. I m assuming the exe is not executing on server. I have given full permissions to the Rotativa folder along with .Net Trust set to full. Any help would be appriciated. Thanks
ReplyDeleteI got the detailed error. Its due to Windows Authentication enabled. What should be done to bypass/fix this?
DeleteRotativa doesn't work with Windows authentication, Just with Web forms authentication or a cookie based one. Your only alternative is turn it off for that page.
DeleteOhh. Thanks Giorgio.
DeleteThis comment has been removed by the author.
DeleteAmazing! Thanks so much!
ReplyDeleteGreat PDF solution. I have a project with multiple solutions. Is there a simple way to use this so I only have 1 install that is accessible from all projects?
ReplyDeleteThat should read solution with multiple projects.
DeleteHi giorgio,
ReplyDeleteIs there a way to print a PDF that contains plots generate via JQPLOT, (Jquery Plot). Is there a way to make it work client side?
Hi Giorgio,
ReplyDeleteI'm having a strange issue with rotativa, once I moved from my dev machine to production server.
Now it generates empty pdf, with acrobat sayng they're broken or tehy have unknown file format.
The action used to render the view that is then converted to pdf functions correctly.
Have you ever experienced this problem? any suggestion?
Thank you in advance,
Alberto
Nooo... it was my fault.. I'v been kinda stupid :)
Deletethe website is currently running on a temporary url, which I have specified to point to my prod. server in file hosts. But the server itself didn't actually "know" where to look for the site url, so wkhtmltopdf.exe couldn't find the page i was trying to render as a pdf.
That solved, now it actually returns a pdf, but it now "print" the login form, even if I am logged and I use the standard asp.net authentication.. strange..
Any help?
Have rotativa any limit of pages to print
ReplyDeleteNot that I know. Did you experience problems?
DeleteGreat tool, I just had one quick question. Why do we need the QT.browser file?
ReplyDeleteThanks! It's needed to make cookies work. http://stackoverflow.com/a/7502669/212398
DeleteHow can I call Pdf from another controllers action? (not-Authenticated secred action :) )
ReplyDeleteGlikoz, not sure I understood what you are trying to do... You already can call another action, even on another route, and nobody will see the actual Url.
DeleteHi, is there any way by which we can combine multiple PDFs containing multiple views?
ReplyDeleteHi Amit, not that I know, sorry. What about simply concatenate them in a "super" view?
DeleteHi Giorgio. Great tool. I'm having a problem when I deploy to production though. I'me getting an error that says "The directory name is invalid". It works great on my development machine. Are there any special folder the component uses that I should give permissions to? Thanks.
ReplyDeleteHi, ones you install the package no other configuration should be needed. How did you deploy to production? You need the Rotativa folder in the root of the web app, or if different provide the path in web.config.
DeleteThanks for yuor quick reply. Then it's totally my bad. I have the Rotativa dll in my bin folder, but nothing else. I'm going to deploy the Rotativa folder as well. Do I need the dll file in the bin folder or juest the Rotative directory in the root? Thanks again.
DeleteWell, it worked. Thanks. Now I'm getting the issue mentioned a couple of comments above where my login page gets rendered instead of my content page, so as suggested I'm going to check the cookes. Funny thing is that I'm not using a specific cookie name, so I'm guessing it's something about IIS.
DeleteQuick question. Is there a way I can programatically get the cookie name from my app? Thanks Giorgio.
Found the way to get the cookie name:
DeleteFormsAuthentication.FormsCookieName
However, I still get the login page instead of the actual authorized view, both whe I set FormsAuthenticationCookieName to ".ASPXAUTH" (the default name) or to FormsAuthentication.FormsCookieName. Not sure what's wrong there, cause on my development pc works fine.
How can we save this pdf to server???pls reply me
ReplyDeleteHi, just uploaded Rotativa 1.4 to nuget gallery. It has a SaveOnServerPath property that, if set with a valid server file system path, saves the pdf. Sorry for taking more time then expected, hope it helps anyway.
DeleteThis comment has been removed by the author.
ReplyDeleteI'm having the redirect to login problem mentioned earlier. I've tried setting FormsAuthenticationCookieName property on ActionAsPdf, but that didn't help. I'm using Forms authentication.
ReplyDeleteCan you tell me how can I resolve this?
Another question - can I save PDF on the server?
Thanks!
Hi Jelena,
DeleteIt should work without having to set the cookie name. If it doesn't you could think about using ViewAsPdf instead.
Ciao,
Giorgio
Grazie Gio!
DeleteViewAsPdf works like a charm - no authentication issues.
Do you have a suggestion how to use Rotativa to generate pdf on the server, so I can attach it to an email. Like invoice email with PDF attached.
Great work. Cheers!
Grazie Jelena! ;)
DeleteYou can use the new property SaveOnServerPath. If it is set with a valid server file system path, it saves the pdf.
Ciao,
Giorgio
Thank you again Giorgio. SaveOnServerPath works great for saving the pdf on the server. Thanks for adding it to the package. Cheers!
DeleteIt's a feature that was requested from several users and was relatively easy to implement, I'm really happy to be able to help my fellow programmers ;)
DeleteCiao!
if I have multiple views, how do I save it to become a multiple pages pdf?
ReplyDeleteTo control page breaks you can use css: http://www.w3schools.com/cssref/pr_print_pageba.asp
DeleteHello Giorgio,
ReplyDeleteIt is working well on my local machine. But it is giving me unhandled exception on server. I am using 'SaveOnServerPath', where I am giving windows temp path to save the document. This is where I am getting the error.
at Rotativa.WkhtmltopdfDriver.Convert(String wkhtmltopdfPath, String switches, String html)
at Rotativa.AsPdfResultBase.CallTheDriver(ControllerContext context)
at Rotativa.AsPdfResultBase.ExecuteResult(ControllerContext context)
Hi, does rotativa support .gif images?
ReplyDeleteHi, if gif images are part of the html to convert they should be converted. Did you experience issues with this?
Delete