Call wkhtmltopdf to create PDF from HTML

I am trying to create a PDF file from an HTML file. After looking around a bit, I found: wkhtmltopdf to be perfect. I need to call this .exe from an ASP.NET server. I tried:

Process p = new Process(); p.StartInfo.UseShellExecute = false; p.StartInfo.FileName = HttpContext.Current.Server.MapPath("wkhtmltopdf.exe"); p.StartInfo.Arguments = "TestPDF.htm TestPDF.pdf"; p.Start(); p.WaitForExit(); 

Without the success of any files created on the server. Can someone give me a pointer in the right direction? I placed the wkhtmltopdf.exe file in the top level directory of the site. Is there anywhere else this should be held?




Edit: If anyone has the best solutions for dynamically creating PDF files from html, please let me know.

+53
html pdf pdf-generation wkhtmltopdf
Aug 26 '09 at 1:35
source share
10 answers

Update:
My answer below is creating a pdf file on disk. Then I transferred this file to the users browser as a download. Consider using something like Hath below to get wkhtml2pdf for output to the stream instead, and then send it directly to the user - this bypasses a lot of problems with file permissions, etc.

My original answer:
Make sure you specify the output path for the PDF that can be written by the ASP.NET IIS process running on your server (usually NETWORK_SERVICE, I think).

Mine looks like this (and it works):

 /// <summary> /// Convert Html page at a given URL to a PDF file using open-source tool wkhtml2pdf /// </summary> /// <param name="Url"></param> /// <param name="outputFilename"></param> /// <returns></returns> public static bool HtmlToPdf(string Url, string outputFilename) { // assemble destination PDF file name string filename = ConfigurationManager.AppSettings["ExportFilePath"] + "\\" + outputFilename + ".pdf"; // get proj no for header Project project = new Project(int.Parse(outputFilename)); var p = new System.Diagnostics.Process(); p.StartInfo.FileName = ConfigurationManager.AppSettings["HtmlToPdfExePath"]; string switches = "--print-media-type "; switches += "--margin-top 4mm --margin-bottom 4mm --margin-right 0mm --margin-left 0mm "; switches += "--page-size A4 "; switches += "--no-background "; switches += "--redirect-delay 100"; p.StartInfo.Arguments = switches + " " + Url + " " + filename; p.StartInfo.UseShellExecute = false; // needs to be false in order to redirect output p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardInput = true; // redirect all 3, as it should be all 3 or none p.StartInfo.WorkingDirectory = StripFilenameFromFullPath(p.StartInfo.FileName); p.Start(); // read the output here... string output = p.StandardOutput.ReadToEnd(); // ...then wait n milliseconds for exit (as after exit, it can't read the output) p.WaitForExit(60000); // read the exit code, close process int returnCode = p.ExitCode; p.Close(); // if 0 or 2, it worked (not sure about other values, I want a better way to confirm this) return (returnCode == 0 || returnCode == 2); } 
+51
Nov 09 '09 at 2:43
source share
— -

I had the same problem when I tried to use msmq using a windows service, but for some reason it was very slow. (part of the process).

Here is what finally worked:

 private void DoDownload() { var url = Request.Url.GetLeftPart(UriPartial.Authority) + "/CPCDownload.aspx?IsPDF=False?UserID=" + this.CurrentUser.UserID.ToString(); var file = WKHtmlToPdf(url); if (file != null) { Response.ContentType = "Application/pdf"; Response.BinaryWrite(file); Response.End(); } } public byte[] WKHtmlToPdf(string url) { var fileName = " - "; var wkhtmlDir = "C:\\Program Files\\wkhtmltopdf\\"; var wkhtml = "C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe"; var p = new Process(); p.StartInfo.CreateNoWindow = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardInput = true; p.StartInfo.UseShellExecute = false; p.StartInfo.FileName = wkhtml; p.StartInfo.WorkingDirectory = wkhtmlDir; string switches = ""; switches += "--print-media-type "; switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm "; switches += "--page-size Letter "; p.StartInfo.Arguments = switches + " " + url + " " + fileName; p.Start(); //read output byte[] buffer = new byte[32768]; byte[] file; using(var ms = new MemoryStream()) { while(true) { int read = p.StandardOutput.BaseStream.Read(buffer, 0,buffer.Length); if(read <=0) { break; } ms.Write(buffer, 0, read); } file = ms.ToArray(); } // wait or exit p.WaitForExit(60000); // read the exit code, close process int returnCode = p.ExitCode; p.Close(); return returnCode == 0 ? file : null; } 

Thanks to Graham Ambrose and everyone else.

+41
Sep 10 2018-10-1010:
source share

Good, so this is an old question, but great. And since I did not find a good answer, I made my own. In addition, I published this super simple project for GitHub.

Here is a sample code:

 var pdfData = HtmlToXConverter.ConvertToPdf("<h1>SOO COOL!</h1>"); 

Here are a few key points:

  • No P / Invoke
  • No creation of a new process.
  • No file system (everything in RAM)
  • Built-in .NET DLL with intellisense etc.
  • Ability to generate PDF or PNG ( HtmlToXConverter.ConvertToPng )
+17
Dec 22 '14 at 23:33
source share

Check out the C # shell library (using P / Invoke) for the wkhtmltopdf library: https://github.com/pruiz/WkHtmlToXSharp

+7
Apr 04 2018-11-11T00:
source share

There are many reasons why this is usually a bad idea. How are you going to control executable files that are generated but end up living in memory if there is a glitch? What about denial of service attacks or if something malicious gets into TestPDF.htm?

I understand that an ASP.NET user account will not be allowed to log on locally. It must also have the correct permissions to access the executable file and write to the file system. You need to edit the local security policy and allow the ASP.NET user account (possibly ASPNET) locally (it may be in the default ban list). Then you need to edit the permissions for the NTFS file system for other files. If you are in a shared hosting environment, you may not be able to apply the required configuration.

The best way to use an external executable like this is to order jobs from ASP.NET code and have some queue monitoring service. If you do this, you will protect yourself from all kinds of bad events. In my opinion, maintenance problems with changing a user account are not worth the effort, and while setting up a service or scheduled work is a pain, its simply the best design. The ASP.NET page should poll the output queue for output, and you can present the user with a wait page. This is acceptable in most cases.

+5
Aug 26 '09 at 1:47
source share

You can tell wkhtmltopdf to send its output to sout by specifying "-" as the output file. Then you can read the result of the process into the response stream and avoid write permissions to the file system.

+5
Mar 03 '10 at 21:52
source share

Thanks for the question / answer / all comments above. I came across this when I wrote my own C # wrapper for WKHTMLtoPDF, and it answered a couple of problems that I had. I ended up writing about this on a blog that also contains my wrapper (you will certainly see the “inspiration” from the above posts leaking into my code ...)

http://icanmakethiswork.blogspot.de/2012/04/making-pdfs-from-html-in-c-using.html

Thanks again to the guys!

+2
Apr 05 2018-12-12T00:
source share

ASP process. Net probably does not have write access to the directory.

Try writing it to %TEMP% and see if it works.

Also, make an ASP.Net page, repeat the stdout and stderr process and check for error messages.

0
Aug 26 '09 at 1:47
source share

Typically, code = 0 is returned if the pdf file is created correctly and correctly. If it is not created, the value is in the -ve range.

0
Dec 17 2018-11-12T00:
source share
 using System; using System.Diagnostics; using System.Web; public partial class pdftest : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } private void fn_test() { try { string url = HttpContext.Current.Request.Url.AbsoluteUri; Response.Write(url); ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = @"C:\PROGRA~1\WKHTML~1\wkhtmltopdf.exe";//"wkhtmltopdf.exe"; startInfo.Arguments = url + @" C:\test" + Guid.NewGuid().ToString() + ".pdf"; Process.Start(startInfo); } catch (Exception ex) { string xx = ex.Message.ToString(); Response.Write("<br>" + xx); } } protected void btn_test_Click(object sender, EventArgs e) { fn_test(); } } 
-one
Feb 06 '10 at 16:25
source share



All Articles