Monday, June 18, 2007

Create pdf in .NET using PDFCreator

Long time no post, was busy with office work and most of the time was searching for an open source tool that reliably prints or creates pdf from any document. It should also have ability to merge the documents or pdfs together. Finally, I found great open source tool PDFCreator which lets you create pdf, merge pdf, automatically save them to a directory, set options via code etc etc. In this article we'll see how we can use PDFCreator for printing/creating pdfs. It uses GhostScript for creating PDF which is again distributed free under GPL license.

INSTALLING PDFCREATOR:
1. Download PDFCreator from sourceforge.net (http://sourceforge.net/projects/pdfcreator)
2. If you have already installed GhostScript go for installer which doesn’t have ghost script installer embedded else go for the installer that will install ghost script also.
3. Run the installer and install PDFCreator. You can use any mode while installing (standard or server) but the server mode needs more configuration so better go for standard mode.
4. Give the name of the printer as “PDFCreator”. You can use any name but this is the name that I have used in the code. So if you are using the code as it is then you need to give this name.
5. After installation a new printer will be installed on your machines.
6. You can use this printer from any document. Open the document and say print. Choose the printer created above and ask it to print.

USE PDFCREATOR IN CODE (C#):
In this code I'll be using Word Application to convert a word document to PDF document. Code will be in C# and application will be a console application. In this example we'll also see how to create a single pdf from multiple word documents (or any documents).

1. Create new console application in C#.
2. Add reference to PDFCreator and Word object.
3. Create new instance for PDFCreator and start using the code.
clsPDFCreator creator = new clsPDFCreator();
string parameters = "/NoProcessingAtStartup";
if(!creator.cStart(parameters, false))
{
Console.WriteLine("Unable to start PDFCreator.");
}

4. If printer is started successfully set the options for the printer.
// Set parameters for saving the generating pdf automatically to a directory.
// Use auto save functionality.
opt.UseAutosave = 1;
// Use directory for saving the file.
opt.UseAutosaveDirectory = 1;
// Name of the output directory.
opt.AutosaveDirectory = @"c:\";
// Format in which file is to be saved. 0 if for pdf.
opt.AutosaveFormat = 0;
// Name of the output file name.
opt.AutosaveFilename = [Name of output file];
creator.cOptions = opt;
creator.cClearCache();

5. Create new word application object and save currently active printer. This is done because while creating PDF PDFCreator (or whatever name you have given while installation) should be active. By saving the name of currently active printer we can set that back as active after creating PDF.
// Save currently active printer.
string defaultPrinter = creator.cDefaultPrinter;
// Create new instance of word application.
ApplicationClass wordapp;
wordapp = new ApplicationClass();
Document worddoc = null;
// Set pdf creator as active printer. Name should be same as you gave while installation.
wordapp.ActivePrinter = "PDFCreator";

6. The above steps are common for either creating single pdf from single document or creating single pdf from multiple documents.

CREATE SINGLE PDF FROM SINGLE DOCUMENT
1. Open an existing word document.
Object missingValue = Type.Missing;
Object docName = [Full Path to your document];
worddoc = wordapp.Documents.Open(ref docName, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue,ref missingValue);

2. Print out the document using ‘Printout’ method. As currently active printer is ‘PDFCreator’, word will use that printer for printing the document.
Object false_object = false;
Object missingValue = Type.Missing;
Object background = true;
Object append = true;
Object range = Microsoft.Office.Interop.Word.WdPrintOutRange.wdPrintAllDocument;
Object outputFileName = Type.Missing;
Object from = Type.Missing;
Object to = Type.Missing;
Object item = Microsoft.Office.Interop.Word.WdPrintOutItem.wdPrintDocumentContent;
Object copies = 1;
Object pages = Type.Missing;
Object pageType = Microsoft.Office.Interop.Word.WdPrintOutPages.wdPrintAllPages;
Object printToFile = false;
Object collate = Type.Missing;
Object fileName = Type.Missing;
Object activePrinterMacGX = Type.Missing;
Object manualDuplexPrint = Type.Missing;
Object printZoomColumn = Type.Missing;
Object printZoomRow = Type.Missing;
Object printZoomPaperWidth = Type.Missing;
Object printZoomPaperHeight = Type.Missing;
wordapp.PrintOut(ref background, ref append, ref range, ref outputFileName, ref from, ref to, ref item, ref copies, ref pages, ref pageType, ref printToFile, ref collate, ref fileName, ref activePrinterMacGX, ref manualDuplexPrint, ref printZoomColumn, ref printZoomRow, ref printZoomPaperWidth, ref printZoomPaperHeight);


3. Wait for the job to get queue up in the PDFCreator. And start the printer (PDFCreator) and then wait for the job to get finished.
// Wait till doc gets queued up.
while(creator.cCountOfPrintjobs != 1);

// Start the printer.
creator.cPrinterStop = false;

// Wait till all doc get converted to pdf.
while(creator.cCountOfPrintjobs != 0);


CREATE SINGLE PDF FROM MULTIPLE DOCUMENTS
1. Follow step 1 as described in section Creating single PDF from single document.
2. In step 2 instead of printing single document you can go from printing any number of documents. (Make sure you remember the count).
3. Now instead of waiting for one document. Wait for all the documents to get queued up and then use “combineAll()’ method of PDFCreator object to combine all the documents into single PDF.
// Wait till doc gets queued up.
while(creator.cCountOfPrintjobs != [Your COUNT of documents printed]);

// Tell PDFCreator to combine all the documents.
creator.cCombineAll();

// Start the printer.
creator.cPrinterStop = false;

// Wait till all doc get converted to pdf.
while(creator.cCountOfPrintjobs != 0);


CLOSING PRINTER AND WORD APPLICATION
// Close all the opened documents.
foreach(Document doc in wordapp.Documents)
{
doc.Close(ref false_object, ref missingValue, ref missingValue);
}

// Stop the printer.
creator.cPrinterStop = true;

// Set back the default printer.
wordapp.ActivePrinter = defaultPrinter;
wordapp.Quit(ref false_object, ref missingValue, ref missingValue);

// Close the printer
creator.cClose();
creator = null;


POINTS OF INTEREST / GOTCHA
If you PDF printer settings is not according to the following diagram then the order in which the documents will get printed is not defined. This will be a problem in case of printing multiple documents to a single PDF where order is important.

22 comments:

西瓜爸爸 said...

I tried to use this code to print my doc file to pdf. But PDFCreator either version 0.9.1 or 0.9.3 does not work properly. After job is submitted, it disappeared. And PDFCreator keeps running then. So I have to kill the processes and word.

Next time if I use word to print to PDFCreator printer directly, all those jobs I submitted through this code appear in the PDFCreator monitor.

I do not know what's going on. I just print one word doc to single pdf file.

Any suggestion?

Angrez Singh said...

Hi Ke,

How are you actually trying to print? What type of application you are trying to build. If thats a window service that it has to be given permission to interact with desktop. Actually PDFCreator needs to interact with desktop to actually run the monitor.

- Angrez

Anonymous said...

hello im having problem using this code.

i run the application once and generate no output pdf, then if i try to generate again my app colapse.

can u email me or something

jmpena [at] sii.com.do

Angrez Singh said...

@jose,

Can you tell me exactly what you are trying to do and how you are trying to do it?

Unknown said...

Hi,

I am using this code..

opt.UseAutosave = 1;
// Use directory for saving the file.
opt.UseAutosaveDirectory = 1;

can you please let me know what is opt?

Thanks,
Vivek Khare

softsan said...

hi,
how to create pdf using pdfCreator for web application???

softsan said...

does ghostscript works with hosted webapplication??? or how to use it with web application???

Pat said...

dear,
great code ! i m interested cause i would to combine 2 PDF documents. My only problem is that i don't want to print the last page of the 1st document. But i don't find the property to know how many there are in my file.
thx

R.D.

93Current said...

Hi,

I have the same problem like 'Ke'.

I have installed PDFCreator version 0.9.0.3 and Word 2003. After running the code it keeps in the while loop -> while(creator.cCountOfPrintjobs != 0);

I have to close the application and the PDFCreator process manually.

If I then open a new Word document and print it manually all former print tryings appear in the PDFCreator monitor.

Any suggestions?

Thank you in advance.
S.

Anonymous said...

You can resolve the loop problem by add this line before the 4) block:
clsPDFCreatorOptions opt = creator.cOptions;

Do not use:
clsPDFCreatorOptions opt = new clsPDFCreatorOptions();

bye

Unknown said...

Hi Angres,

Tnx for your helps! I just want to convert a html page from my appication to a pdf file (to be saved on c:\), but can't get it work.

Do you know a simple piece of code?

Unknown said...

Sorry the exact problem is that I don't want the print prompt to pop-up, since this is an automated proces.
Is there some option available or something?

Andrew said...

Thank you for this article! I searched how to export doc to PDF without having word installed on the machine but reading this article I realized that it's impossible as interop.word needs word installed.

Bebe said...

Good article, thank you! :)

Ay Yuzlum... said...

Hi There;

thanks for the article as it is very useful.

I was just wondering whether you have used and tested security options or not.

Regards

QUINDI said...

Hi,
I follow you examole, but on windows server I have this error:


Unable to cast COM object of type 'PDFCreator.clsPDFCreatorClass' to interface type 'PDFCreator._clsPDFCreator'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{280807DB-86BB-4FD1-B477-A7A6E285A3FE}' failed due to the following error: Interfaccia non supportata. (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

at this line:
if (!creator.cStart(parameters, false))

Have you any idea?

Thank

ismet said...

Hi

I wrote my C# code using PDFCreator COM only. I did not need to include Word. It works fine.

using System;
using System.Collections.Generic;
using System.Text;
using PDFCreator;

namespace PDFConvertorApp
{
class Program
{
public static PDFCreator.clsPDFCreator m_PDFCreator;
static string m_DocumentFullPath = @"c:\x\word.doc";
static string m_OutputPdfFileName = @"mypdfdemo2";
static string m_OutputPdfDirectory = @"c:\x";

static void Main(string[] args)
{
m_PDFCreator = new clsPDFCreator();
PDFCreator.clsPDFCreatorOptions opt;
string strParam = "/NoProcessingAtStartup";

// check if application 'PDFCreator' started
if (!m_PDFCreator.cStart(strParam, false))
{
// app did not start; log activity
Console.WriteLine("Can not start PDFCreator!");
Console.ReadKey();
}
else // app started successfully
{
// 1) set PDFCreator options for the printer
opt = m_PDFCreator.cOptions;
// set parameters for saving the generating pdf
opt.UseAutosave = 1;
// Use directory for saving the file.
opt.UseAutosaveDirectory = 1;
// Name of the output directory.
opt.AutosaveDirectory = m_OutputPdfDirectory;
// Format in which file is to be saved. 0 if for pdf.
opt.AutosaveFormat = 0;
// Name of the output file name.
opt.AutosaveFilename = m_OutputPdfFileName; // file name without path or extension
m_PDFCreator.cOptions = opt;
m_PDFCreator.cClearCache();

// 2) Save currently active printer.
string defaultPrinter = m_PDFCreator.cDefaultPrinter;

// 3) set PDFCreator as defualt printer
m_PDFCreator.cDefaultPrinter = "PDFCreator";

// 4) print out the document using ‘cPrintFile’ method.
// as currently active printer is ‘PDFCreator’
m_PDFCreator.cPrintFile(m_DocumentFullPath);

// 5) Wait till doc gets queued up.
while (m_PDFCreator.cCountOfPrintjobs != 1) ;

// 6) Start the printer.
m_PDFCreator.cPrinterStop = false;

// 7) Wait till all doc get converted to pdf.
while (m_PDFCreator.cCountOfPrintjobs != 0) ;

// 9) Stop the printer.
m_PDFCreator.cPrinterStop = true;

// 10) Set back the default printer.
m_PDFCreator.cDefaultPrinter = defaultPrinter;

// 11) Close the printer
m_PDFCreator.cClose();

// 12) clear PDFCreator object
m_PDFCreator = null;

} // else

} // Main

} // class

} // namespace

Pavan N said...
This comment has been removed by the author.
Pavan N said...
This comment has been removed by the author.
Anonymous said...

Can't you post a COMPLETE code, please, not few uncomplete extracts ?
Because between the first part and the second (at point 4), it miss a lot, i suppose, because, WHAT IS "opt" ????????

Anonymous said...

Can't you post a COMPLETE code, please, not few uncomplete extracts ?
Because between the first part and the second (at point 4), it miss a lot, i suppose, because, WHAT IS "opt" ????????

Angrez Singh said...

Hi Rey,

Please use the code posted by Ismet.

Thanks,
Angrez