Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling wkhtmltopdf to generate PDF from HTML

I'm attempting to create a PDF file from an HTML file. After looking around a little I've found: wkhtmltopdf to be perfect. I need to call this .exe from the ASP.NET server. I've attempted:

    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(); 

With no success of any files being created on the server. Can anyone give me a pointer in the right direction? I put the wkhtmltopdf.exe file at the top level directory of the site. Is there anywhere else it should be held?


Edit: If anyone has better solutions to dynamically create pdf files from html, please let me know.

like image 754
Sean Avatar asked Aug 26 '09 01:08

Sean


2 Answers

Update:
My answer below, creates the pdf file on the disk. I then streamed that file to the users browser as a download. Consider using something like Hath's answer below to get wkhtml2pdf to output to a stream instead and then send that directly to the user - that will bypass lots of issues with file permissions etc.

My original answer:
Make sure you've specified an output path for the PDF that is writeable by the ASP.NET process of IIS 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); } 
like image 170
MGOwen Avatar answered Sep 23 '22 12:09

MGOwen


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

This 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 Graham Ambrose and everyone else.

like image 38
Hath Avatar answered Sep 21 '22 12:09

Hath