8

I have been looking into phantomJS and looks like it could be a great tool to use generating PDFs. I wonder if anyone have successfully used it for their .NET applications.

My specific question is: how would you use modules like rasterize.js on the server, receive requests and send back generated pdfs as a response.

My general question is: is there any best practice for using phantomJS with .NET Applications. What would be the best way to achieve it?

I am fairly new in .NET World and I would appreciate the more detailed answers. Thanks everyone. :)

Community
  • 1
  • 1
Balash
  • 143
  • 1
  • 2
  • 8

3 Answers3

13

I don't know about best practices, but, I'm using phantomJS with no problems with the following code.

public ActionResult DownloadStatement(int id)
{
    string serverPath = HttpContext.Server.MapPath("~/Phantomjs/");
    string filename = DateTime.Now.ToString("ddMMyyyy_hhmmss") + ".pdf";

    new Thread(new ParameterizedThreadStart(x =>
    {
        ExecuteCommand("cd " + serverPath + @" & phantomjs rasterize.js http://localhost:8080/filetopdf/" + id.ToString() + " " + filename + @" ""A4""");
    })).Start();

    var filePath = Path.Combine(HttpContext.Server.MapPath("~/Phantomjs/"), filename);

    var stream = new MemoryStream();
    byte[] bytes = DoWhile(filePath);

    return File(bytes, "application/pdf", filename);
}

private void ExecuteCommand(string Command)
{
    try
    {
        ProcessStartInfo ProcessInfo;
        Process Process;

        ProcessInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
        ProcessInfo.CreateNoWindow = true;
        ProcessInfo.UseShellExecute = false;

        Process = Process.Start(ProcessInfo);
    }
    catch { }
}

public ViewResult FileToPDF(int id)
{
    var viewModel = file.Get(id);
    return View(viewModel);
}

private byte[] DoWhile(string filePath)
{
    byte[] bytes = new byte[0];
    bool fail = true;

    while (fail)
    {
        try
        {
            using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                bytes = new byte[file.Length];
                file.Read(bytes, 0, (int)file.Length);
            }

            fail = false;
        }
        catch
        {
            Thread.Sleep(1000);
        }
    }

    System.IO.File.Delete(filePath);
    return bytes;
}

Here is the action flow:

The user clicks on a link to DownloadStatement Action. Inside there, a new Thread is created to call the ExecuteCommand method.

The ExecuteCommand method is responsible to call phantomJS. The string passed as an argument do the following.

Go to the location where the phantomJS app is and, after that, call rasterize.js with an URL, the filename to be created and a print format. (More about rasterize here).

In my case, what I really want to print is the content delivered by the action filetoupload. It's a simple action that returns a simple view. PhantomJS will call the URL passed as parameter and do all the magic.

While phantomJS is still creating the file, (I guess) I can not return the request made by the client. And that is why I used the DoWhile method. It will hold the request until the file is created by phantomJS and loaded by the app to the request.

Felipe Miosso
  • 7,309
  • 6
  • 44
  • 55
  • 4
    You could use Process.WaitForExit instead of a do while loop. http://msdn.microsoft.com/en-us/library/fb4aw7b8(v=vs.110).aspx – Chris Foster Dec 20 '13 at 06:06
  • This does not work when the application is run in IIS on Windows Server 2008+. This solution works when you test it from visual studio but will not work in production when deployed to IIS, http://stackoverflow.com/a/5308011/1062224. – Anish Patel Aug 26 '14 at 08:45
  • We are using it since the date I posted this answer and it's working fine. – Felipe Miosso Aug 26 '14 at 11:53
  • @Filepe Miosso what version of Windows Server, IIS and .NET framework are you using? – Anish Patel Aug 26 '14 at 13:45
  • We are using Windows Server 2008 R2 and .NET 4.0. Just an observation... The code I've posted is being used by a .NET MVC application and not a Windows Service. – Felipe Miosso Aug 26 '14 at 13:54
  • I've tried this in an MVC Controller without success too, using the same code you've supplied. My setup is Windows Server 2012, IIS 8, MVC 5.2. I then tried to do this from a self hosted OWIN application running as a Windows Service, and this did not work either. The command certainly gets called, but then the OS kills the process. Do you have a repository on github with this code working? – Anish Patel Aug 26 '14 at 16:33
  • in method *FileToPDF* file.get refrence is missing not found getting error – MSTdev May 15 '16 at 18:24
  • @Panagiotis Kanavos, As I said, it's not the best solution. This is what I had 3 years ago. Feel free to enlighten us with a better answer that will actually help people. – Felipe Miosso Oct 12 '16 at 13:40
  • The answer typically given to this question is - use the Adobe SDK or a similar library. Better yet, use a reporting library - or an RDLC control. The same applied 3 4-5 years ago. Launching a processs isn't just a performance problem, it's also a huge security risk. Starting a *web browser* on the *server* just to render a PDF is also needlessly convoluted – Panagiotis Kanavos Oct 13 '16 at 07:24
1

If you're open to using NReco.PhantomJS, which provides a .NET wrapper for PhantomJS, you can do this very succinctly.

public async Task<ActionResult> DownloadPdf() {
    var phantomJS = new PhantomJS();
    try {
        var temp = Path.Combine(Path.GetTempPath(),
            Path.ChangeExtension(Path.GetRandomFileName(), "pdf")); //must end in .pdf
        try {
            await phantomJS.RunAsync(HttpContext.Server.MapPath("~/Scripts/rasterize.js"),
                new[] { "https://www.google.com", temp });
            return File(System.IO.File.ReadAllBytes(temp), "application/pdf");
        }
        finally {
            System.IO.File.Delete(temp);
        }
    }
    finally {
        phantomJS.Abort();
    }
}
user2880616
  • 255
  • 3
  • 12
  • Yeah, and a commercial license is required. – Tom Feb 11 '19 at 11:39
  • 1
    @Tom Depends on your application, per their FAQ it "can be used for FREE in single-deployment projects (websites, intranet/extranet) or applications for company's internal business purposes (redistributed only internally inside the company)." – user2880616 Feb 21 '19 at 18:11
0

Here's some very basic code to generate a PDF using Phantom.JS but you can find more information here: https://buttercms.com/blog/generating-pdfs-with-node

var webPage = require('webpage');
var page = webPage.create();

page.viewportSize = { width: 1920, height: 1080 };
page.open("http://www.google.com", function start(status) {
page.render('google_home.pdf, {format: 'pdf', quality: '100'});
phantom.exit();
});