Sponsor
Background printing of PDF documents
Posted by Tobias on December 29th 2008
I was asked to create some software to handle printing of PDF documents and after scouring the Internet for several hours I realized that this wasn't as easy as I had initially though. There are several methods of doing background printing so I tested all I could find and measured the performance. This implementation was going to be a Windows service written in C# which could pick up newly created files in a directory, do some processing and then print them. The service needed to be threaded to be able to handle multiple files at the same time so I was looking for thread safe components. Another thing high up on the wishing list was the use of open source or free software.
The performance testing was done by giving the service 40 single page PDF documents and measuring the time taken until the last file was processed and sent to the printer queue.
Using Adobe Reader
Version 6 of Adobe Reader supported command line printing; sadly this was removed in newer versions. I could still launch the application using System.Diagnostics.Process class and supplying the "PrintTo" verb so I tested this. Using one thread to print all 40 documents took about 12 minutes with this method. There was however several problems using this method:
- Adobe Reader will not close after the file has been printed and there is no easy way of determining when the application is finished printing. We could wait for a fixed time or wait until the application is idle and then kill it. I decided to wait until the application was idle which worked satisfactory.
- Adobe Reader does not seem to be thread safe. When using two or more threads, files sent to Adobe Reader at the same time was ignored.
- There seemed to be a lot of memory overhead when launching Reader.
These problems led me to continue searching for other solutions.
Using Ghostscript
This method solves all problems I had with Adobe Reader but unfortunately introduces new ones. Ghostscript has a command line interface (gswin32c.exe) which will print a file and then exit. There were no problems using several threads and memory usage seemed to be acceptable. The problem with using Ghostscript was processing time and the size of the spooling file sent to the printer. In some cases the file size difference between Ghostscript and Adobe Reader was a factor of 20. Also, when printing documents with several pages Ghostscript seems to process each page individually. This took way too much time. This method was however twice as fast as Adobe Reader when printing the 40 test documents and finished in about 6 minutes.
Using Adobe Acrobat SDK
So I was back to Adobe and tested their SDK. I can't begin to explain the frustration I felt using their so called SDK and in the end I can't recommend it to anyone.
First, their SDK isn't really an SDK as much as it is a lot of sample code. To be able to use their APIs you need to install the full Acrobat package which is a hassle to obtain just to test it out.
Second, the SDK is .NET compatible but not much more. Their class structure is a complete mess and horrible to work with.
Third, there is no apparent way of supplying the destination printer using the APIs. There is a PrintPagesSilent method but it always prints to the default printer. Somewhere in some developers manual I found out that you have to go through JavaScript to be able to set a destination printer, which is completely insane. Why not add it as an optional parameter to the PrintPagesSilent method? We could of course change the default printer but that wouldn't be thread safe.
Finally, they charge you money for this...
Using Foxit Reader
Finally I tested printing with Foxit Reader which is freely available at www.foxitsoftware.com. It has a command line interface for printing, uses less memory than the previous alternatives and it's also thread safe. When printing the 40 test documents using 3 threads, all documents were processed in less than 1 min 30 sec. Using the command line Foxit Reader will exit after printing the document.
The printer name must be supplied exactly as written in the standard Windows print dialog. This was a minor problem because the printer name supplied with the files was actually the share name. Therefore I wrote a method which fetches all installed printers and matches the names. In order to handle installed printers two basic classes were created: Printer and PrinterList. The Printer class basically contains two strings to store printer name and share name. The PrinterList class is a subclass of System.Collection.Generic.List to hold the instances of Printer and also to do various finding tasks.
Example code
/// <summary> /// Prints a file using Foxit Reader. /// </summary> /// <param name="sFileName">Path to file</param> /// <param name="sPrinter">Name of printer</param> public bool PrintFile(string sFileName, string sPrinter) { string sArgs = " /t \"" + sFileName + "\" \"" + sPrinter + "\""; System.Diagnostics.ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = @"C:\Program Files\Foxit Software\Foxit Reader\Foxit Reader.exe"; startInfo.Arguments = sArgs; startInfo.CreateNoWindow = true; startInfo.WindowStyle = ProcessWindowStyle.Hidden; System.Diagnostics.Process proc = Process.Start(startInfo); proc.WaitForExit(10000); // Wait a maximum of 10 sec for the process to finish if (!proc.HasExited) { proc.Kill(); proc.Dispose(); return false; } return true; } /// <summary> /// Get a list of installed printers. /// </summary> /// <returns>A subclass of List which contains instances of the Printer class.</returns> public PrinterList GetPrinterList() { PrinterList lstPrinters = new PrinterList(); // Custom subclass of List using (ManagementClass printerClass = new ManagementClass("win32_printer")) { ManagementObjectCollection printers = printerClass.GetInstances(); foreach (ManagementObject printer in printers) { if ((bool)printer["Shared"] == true) lstPrinters.Add(new Printer((string)printer["Name"], (string)printer["ShareName"])); } } return lstPrinters; }
Conclusion
Background printing of PDF documents isn't very difficult once you have found the right tools for the task. Ghostscript works fine when printing small documents and it's highly customizable. Sadly Ghostscript will choke when you give it larger documents. Spooling a PDF containing 60 pages using Ghostscript takes minutes instead of a couple of seconds using alternative methods. Adobe Reader has been broken to be able to sell their so called SDK so Foxit Reader was perfect for this project.
Post a comment
| Posted by The Developer on December 3rd 2009 18:22 |
|---|
| Thank you for this post, it really helped me a lot. I really appreciate your help and that you shared your knowledge with the community. |
| Posted by Midwest Developer on May 24th 2010 22:53 |
| This is a great post. Thanks for sharing your findings. |
| Posted by MxYvybbLH on July 30th 2011 06:00 |
| Gee whiz, and I thuohgt this would be hard to find out. |
