Click here to Skip to main content
15,885,141 members
Articles / Programming Languages / Visual Basic

Automated PDF Conversion using the PDF995 and FreePDF_XP Freeware Printers

Rate me:
Please Sign up or sign in to vote.
4.67/5 (10 votes)
16 Oct 2008BSD7 min read 127.8K   3.1K   89  
An object oriented class automating the creation of PDF files from any file using freeware tools.
<!--------------------------------------------------------------------------->  
<!--                           INTRODUCTION                                

 The Code Project article submission template (HTML version)

Using this template will help us post your article sooner. To use, just 
follow the 3 easy steps below:
 
     1. Fill in the article description details
     2. Add links to your images and downloads
     3. Include the main article text

That's all there is to it! All formatting will be done by our submission
scripts and style sheets. 

-->  
<!--------------------------------------------------------------------------->  
<!--                        IGNORE THIS SECTION                            -->
<html>
<head>
<title>The Code Project</title>
<Style>
BODY, P, TD { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt }
H2,H3,H4,H5 { color: #ff9900; font-weight: bold; }
H2 { font-size: 13pt; }
H3 { font-size: 12pt; }
H4 { font-size: 10pt; color: black; }
PRE { BACKGROUND-COLOR: #FBEDBB; FONT-FAMILY: "Courier New", Courier, mono; WHITE-SPACE: pre; }
CODE { COLOR: #990000; FONT-FAMILY: "Courier New", Courier, mono; }
</style>
<link rel="stylesheet" type=text/css href="http://www.codeproject.com/styles/global.css">
</head>
<body bgcolor="#FFFFFF" color=#000000>
<!--------------------------------------------------------------------------->  


<!-------------------------------     STEP 1      --------------------------->
<!--  Fill in the details (CodeProject will reformat this section for you) -->

<pre>
Title:       Automated PDF Converting using PDF995 and FreePDF_XP freeware printers
Author:      typecast
Email:       typecast@web.de
Member ID:   1425806
Language:    VB.NET
Platform:    Windows, VB.NET
Technology:  OOP
Level:       Beginner
Description: An object oriented class automating the creation of PDF files from any file using freeware tools
Section      Languages
SubSection   VB.NET, HowTo
License:     <a href="http://www.opensource.org/licenses/bsd-license.php">BSD License</a>
</pre>

<!-------------------------------     STEP 2      --------------------------->
<!--  Include download and sample image information.                       --> 

<ul class=download>
	<li>
		<a href="anyfiletopdf_demo.zip">Download demo project - 10 Kb</a>
	</li>
	<li>
		<a href="anyfiletopdf_src.zip">Download source - 30 Kb</a>
	</li> 	
</ul>
<font size=-2>
	<i>In order to convert a file to PDF with the demo software you should either have one of the freeware pdf printers
	or Adobe Acrobat. If you don't have any of them the file will be send to your default printer.</i>
</font>
<!--<p><img src="Article.gif" alt="Sample Image - maximum width is 600 pixels" width=400 height=200></p>-->


<!-------------------------------     STEP 3      --------------------------->

<!--  Add the article text. Please use simple formatting (<h2>, <p> etc)   --> 

<h2>Introduction</h2>

<p>
	Nowadays PDF is standard file form used in many reporting tools. The application that I developed with VB.NET for my job
	needed to convert a lot of  Microsoft Project and Excel files to PDF files and place them to custom specified directory without
	any user interaction. Almost everybody at the office has some free pdf printer instead of Adobe Acrobat - either <a href="http://www.pdf995.com/" target="_blank">PDF995</a> or <a href="http://freepdfxp.de/index.html" target="_blank">FreePDF</a>.
	They are both very nice converters, but my problem was they always pop (naturally) the �<i>Save As</i>� dialog.
	I read the documentation of both printers, searched a while in Internet and actually found a lot of examples how
	to automate the �<b>printing</b>� but none of them in VB.NET.<br />
	So I thought VB.NET is (already) a nice OOP language, why not use this and make a simple to use class for easy converting files into pdf.
	<br />
</p>

<h2>Background</h2>

<p>
	If you have Adobe Acrobat, you will also have a �<b>PDF printer</b>�. Acrobat has also provided in the Acrobat SDK very handy and
	easy to understand examples how to silently convert any file to PDF with VB.NET. In their examples they also show how to find the
	default application for every known file extension.<br />
	For this example this is not needed, because most of the file extensions in windows have already an associated application,
	so all we need is to start a process with the verb �<b>print</b>�. <br />
	First we need to know what a <b>verb</b> is. According to Microsoft a verb is:
	<br /><br />
	&quot;File associations use verbs as shorthand for actions that are invoked on items in the Shell namespace, including files and
	folders. Verbs are closely related to the Shell shortcut menus, where each menu item is associated with a verb. IContextMenu and
	ShellExecute support canonical names for verbs; canonical verb names remain constant regardless of platform or language, which
	makes it possible for developers to invoke known canonical verbs without knowing the details about a Shell namespace item.
	For example, ShellExecute can invoke the Print verb on a Microsoft Word document, which requests that the installed handler
	print the document, without knowing whether Word, Microsoft WordPad, or some other application does the printing.&quot;
	<br /><br />
	and also 
	<br /><br />
	&quot;A file association generally has a preferred action that is taken when the user double-clicks a file of this type.
	This preferred action is linked to a verb referred to as the primary verb. The primary verb is specified by the default
	value of the shell key, or the open key if the shell key has no default value. The most common primary verb is open.
	However, in media files, the most common primary verb is play. The primary verb is also referred to as the default verb.
	...
	print: The application prints the contents of the file and exits, displaying as little as necessary to complete the task.&quot;. 
	<br /><br />
	More detailed information about verbs and file associations can be found here
	<a href="http://msdn.microsoft.com/en-us/library/cc144175(VS.85).aspx" target="_blank">here</a>.
	<br />
</p>

<h2>Using the Code</h2>

<p>
Now that we now what a verb is we will use it to print our file.<br />
One possibility to print an existing file is to start a process giving the file name and the verb �<b>PRINT</b>� to the
<code lang="vbnet">StartInfo</code> structure. What windows does is find the associated application, open the file with the associated application,
send it to the default printer and close the application. 
<br /><br />
Before start explaining the details about PDF995 and FreePDF XP we will discuss in short the start of a process. 
<br /><br />
The .NET framework has the <code lang="vbnet">System.Diagnostics</code> namespace, that provides among other things the Process class.
With it the programmers life is very easy. In order to start a process, make its window invisible and wait until
the process is ready (this is possible when printing files) we write:
<br />
<pre lang="vbnet">
	'Define properties for the print process
	Dim procStartInfo As ProcessStartInfo = Nothing

	procStartInfo = New ProcessStartInfo
	procStartInfo.FileName = "c:\file.xls"  ' e.g. an excel file
	procStartInfo.Verb = "Print"			' print please 

	'Make process invisible
	procStartInfo.CreateNoWindow = True
	procStartInfo.WindowStyle = ProcessWindowStyle.Hidden

	'start print process for the file with/from the associated application
	Dim procPrint As Process = Process.Start(procStartInfo)

	If Not procPrint Is Nothing Then
		procPrint.WaitForExit()
	End If
</pre>

More information about the Process class can be found here:
<a href="http://msdn.microsoft.com/de-de/library/system.diagnostics.process.aspx" target="_blank">.NET Framework Process class</a>.
<br />
Now lets see how the above mentioned pdf printers work.
<br /><br />
The <b>PDF995</b> printer uses an INI file to store its settings. The INI file resides in the �res� subdirectory of the printers
installation path. Further read of the developers FAQ says, that in order to bypass the �Save As� dialog and to place the PDF
file to a custom directory all we need to do is override the following INI file options:
<ol type="1">
	<li>OutputFile � will cause the file to be saved under this name into the given folder</li>
	<li>AutoLaunch � setting this to zero will prevent opening pdf reader application every time after creating a pdf file</li>
</ol>
<img src="anyfiletopdf1.gif">
<br /><br />

The <b>FreePDF XP</b> printer on the other hand uses the windows registry to store its settings and also first converts the file
to a PostScript and then the PS file to a PDF file. To put the PDF file into the user specified directory we need to set
temporarily the �psDir� registry value to the desired directory string. Then the administrator manual says that in order to convert
a pdf file from the command line with the �freepdf.exe� application, we need following options:
<ol type="1">
	<li>�/3 delps, end� - meaning to convert the file, delete the PS file and exit the FreePDF application</li>
	<li>desired PDF file name and PS file name from which we create the PDF. We will convert only one file at a
	time and FreePDF PS files have always the same name format: �current username000001.ps�. If we have queued
	more then one file we have had 000002, 000003 and so on, but since we always convert one file and wait for
	the printer to finish, this should be working correct for us.</li>
</ol>
<img src="anyfiletopdf2.gif">
<br /><br />
Summarizing the actions we need to undertake gives us the following:
<br />
PDF995 stores its settings in INI file so we need to
<ol type="1">
	<li>retrieve the printer installation path through the registry</li>
	<li>save the original INI file from the installation path /res directory</li>
	<li>create customized INI</li>
	<li>convert to PDF</li>
	<li>restore the original INI file</li>
</ol>

FreePDF XP stores its settings in the registry (full path: HKEY_LOCAL_MACHINE\SOFTWARE\shbox\FreePdfXP) and uses a PS
intermediate file, so we need to
<ol type="1">
	<li>save original registry values</li>
	<li>write customized values</li>
	<li>create PS</li>
	<li>convert PS to PDF</li>
	<li>restore registry values</li>
</ol>

Now we know how to design our base printer class. We need by all means a function to save the original printer settings,
a function to restore the original printer settings, a variable to keep the full path of the desired file to print and a
print function. Since both printers have a different settings approach it makes sense to declare this functions as abstract and since the
creating of the print process is always the same it makes sense to override the print function. This is only partial code
for clarity. The full source code can be found in the zip file.

<pre lang="vbnet">
Imports System.IO

'we want an abstract base class
Public MustInherit Class CPrinter

    Protected _strFileToPrint As String = ""
    'save original settings
    Protected MustOverride Sub pushSettings()
    'restore original settings
    Protected MustOverride Sub popSettings()
    'start printing process (adopted from Adobe PDF SDK examples)
    Public Overridable Sub PrintFile()

        'Define properties for the print process
        Dim procStartInfo As ProcessStartInfo = Nothing

        If System.IO.File.Exists(_strFileToPrint) Then

            procStartInfo = New ProcessStartInfo
            procStartInfo.FileName = _strFileToPrint
            procStartInfo.Verb = "Print"

            'Make process invisible
            procStartInfo.CreateNoWindow = True
            procStartInfo.WindowStyle = ProcessWindowStyle.Hidden

            'start print process for the file with/from the associated application
            Dim procPrint As Process = Process.Start(procStartInfo)
            'give the system some time
            System.Threading.Thread.Sleep(2500)

            If Not procPrint Is Nothing Then
                procPrint.WaitForExit()
            End If

        End If

    End Sub

End Class
</pre>
Now that we have an abstract base class we need to inherit it. Currently classes will place the PDF file into the same directory
as the original file. 
<br /><br />
The PDF995 printer class
<pre lang="vbnet">
Imports Microsoft.Win32
Imports System.IO

Public Class CPrinterPDF995 : Inherits CPrinter

    'the INI that we want to change in oder to "silently" use the PDF printer
    Private Const strOriginalIniFile As String = "pdf995.ini"
    'we want to recover the original INI file after every print
    Private Const strOriginalIniSave As String = "pdf995.ini_ORIGINAL"
    'used to locate PDF995 installation directory
    Private Const strRegPath As String = "Software\Microsoft\Windows\CurrentVersion\Uninstall\Pdf995"

    Protected Overrides Sub pushSettings()

        Dim strIniPath As String = RetrieveIniPath()

        'save original INI file first
        FileSystem.Rename(strIniPath + strOriginalIniFile, strIniPath + strOriginalIniSave)

        Me.createCustomIni()

    End Sub

    Protected Overrides Sub popSettings()

        Dim strIniPath As String = RetrieveIniPath()

        'restore original INI file
        File.Delete(strIniPath + strOriginalIniFile)
        FileSystem.Rename(strIniPath + strOriginalIniSave, strIniPath + strOriginalIniFile)

    End Sub

    Private Sub createIni()

        Dim strFileToPrint As String = MyBase.strFileToPrint
        Dim strIniPath As String = RetrieveIniPath()
        Dim fi As System.IO.FileInfo = New System.IO.FileInfo(strFileToPrint)
        Dim strPDFOutputDirectory As String = fi.DirectoryName + "\"
        Dim oWriter As StreamWriter = Nothing

        fi = Nothing

        'write INI file, create PDF from "strFileToPrint" and save it to "directory"
        oWriter = New StreamWriter(strIniPath + "pdf995.ini", False, System.Text.Encoding.Unicode)

        oWriter.WriteLine("[Parameters]")
        oWriter.WriteLine("Install = 0")
        oWriter.WriteLine("Quiet = 1")
        oWriter.WriteLine("AutoLaunch = 0")
        oWriter.WriteLine("Document Name = " + strFileToPrint)
        oWriter.WriteLine("User File = " + strFileToPrint + ".pdf")
        oWriter.WriteLine("Output File = " + strFileToPrint + ".pdf")
        oWriter.WriteLine("Initial Dir = " + strPDFOutputDirectory)
        oWriter.WriteLine("Use GPL Ghostcript = 1")

        oWriter.Close()

        oWriter = Nothing

        System.Threading.Thread.Sleep(1000) ' give Pdf995 some time

    End Sub

    Public Overrides Sub printFile()

        Me.pushSettings()
        MyBase.PrintFile()
        Me.popSettings()

    End Sub

End Class
</pre>

and the FreePDF printer class

<pre lang="vbnet">
Imports Microsoft.Win32
Imports System.IO

Public Class CPrinterXFreePDF : Inherits CPrinter

    Private strFreePDFExe As String = "" 'freepdf.exe full path
    Private strPSTempDir As String = "" 'PS temp directory, needed for silent ps to pdf creation
    'FreePDF path in registry
    Private Const strRegPath As String = "SOFTWARE\shbox\FreePdfXP"

    Public Overrides Sub printFile()

        Dim fi As System.IO.FileInfo = New System.IO.FileInfo(fileToPrint)
        Dim strFileToPrintDir As String = fi.DirectoryName + "\"
        fi = Nothing

        Me.pushSettings()

        Dim strCmd As String = strFreePDFExe + " /3 delps,end ""eBook"" """ + fileToPrint + ".pdf"" """ + _ 
		strFileToPrintDir + Environment.UserName + "000001.ps"""

        'create the PS file
        MyBase.PrintFile()
        'create PDF from PS file, can also be started with System.Diagnostics.Process
        Shell(strCmd, AppWinStyle.Hide, True)

        Me.popSettings()

    End Sub

    Protected Overrides Sub pushSettings()

        Dim regKey As RegistryKey = Nothing
        Dim fi As System.IO.FileInfo = New System.IO.FileInfo(MyBase.strFileToPrint)
        Dim strPSPath As String = fi.DirectoryName + "\"
        fi = Nothing

        regKey = Registry.LocalMachine.OpenSubKey(strRegPath, True)

        'fpDir = freepdf.exe directory
        strFreePDFExe = regKey.GetValue("fpDir") + "freepdf.exe"
        'psDir = location for temp PS files, save it
        strPSTempDir = regKey.GetValue("psDir")

        'set currently new PS files location
        If Not String.IsNullOrEmpty(strPSPath) Then
            regKey.SetValue("psDir", strPSPath)
        End If

        regKey.Close()

        regKey = Nothing

    End Sub

    Protected Overrides Sub popSettings()

        Dim regKey As RegistryKey = Nothing

        regKey = Registry.LocalMachine.OpenSubKey(strRegPath, True)

        'restore original psDir
        regKey.SetValue("psDir", strPSTempDir)

        regKey.Close()

        regKey = Nothing

    End Sub
</pre>

You can always then instantiate an object from one of the classes and convert a file like this:
<pre lang="vbnet">
        Dim oPrinter As CPrinterPDF995 = New CPrinterPDF995

        oPrinter.fileToPrint = "c:\temp\report.xls"
        oPrinter.printFile()

        oPrinter = Nothing
</pre>

I hope the commented code is enough self-explanatory and will help you save some time at home or at work. If you have any trouble
understanding it, ask and i'll be glad if i can help. Also do not forget to set the desired printer as a default printer
system wide from the control panel or from within the code. 
<br /><br />
<img src="anyfiletopdf3.gif">
<br /><br />
Happy converting! :-)
</p>

<h2>History</h2>

<p>
	<ul>
		<li>05-Oct-2008: v1.00 - Initial version</li>
	</ul>
</p>


<!-------------------------------    That's it!   --------------------------->
</body>

</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions