Click here to Skip to main content
15,903,388 members
Articles / Desktop Programming / MFC

PDF Writer

Rate me:
Please Sign up or sign in to vote.
4.55/5 (22 votes)
16 Mar 2006CPOL3 min read 328.9K   6.7K   128   82
An article on using the Virtual Printer Method to create a PDF writer

Sample Image - PDFWriter1.jpg

Introduction

The main purpose of this paper is to demonstrate how to create a PDF writer by using the Virtual Printer Method, which gives your applications an ability to generate PDF files through simply "printing". I am pretty sure there are many PDF writers using the same technique, like PrimoPDF. However, you probably wish to create your own PDF writer some day. Here, I give an example to uncover the whole process of creating such a kind of PDF writer.

Background

  • Ghostscript is an interpreter for PDF files, which also has the ability to convert PostScript language files to PDF.
  • RedMon is a port monitor, which redirects a special printer port to Ghostscript.

The main idea is actually simple. What you need to do is install a PostScript printer and let RedMon work as a bridge between the printer and Ghostscript.

Using the Code

Before Jumping to the Demo Project

You have to first download Ghostscript. My demo requires AFPL Ghostscript 8.53. Don’t install it at this time. You are encouraged to use WinRAR or WinZip (I didn’t try WinZip) to unzip it to “C:\\UTReportPrerequisite\\gs\\”. You need to copy pdfwrite.rsp (a text file, which can be found in the demo project) to the folder as well.

You may download RedMon from its website, but I suggest you use a replacement that is packed in the demo project. There is a folder “redmon17” in the demo project, please copy this folder to “C:\\UTReportPrerequisite\\”.

The last thing you need is a printer driver. By chance, I chose HP color LaserJet 8550 PostScript driver. Don’t install it at this time. Instead, use WinRAR to unzip all files to “c:\\UTReportPrerequisite\\Driver”.

The final folder structure should be like below:

Sample screenshot

Running the Demo Project

The demo project is very straightforward. Clicking buttons from "Step1", "Step2", "Step3" to "Step4" sequentially, you will have a printer named "UTReport PDF Writer" installed if each step has been done successfully, as shown in the figure below. I want to point out here that "Step3" and "Step4" are a little bit time consuming so more patience should be paid.

Sample screenshot

Now is the right time to test our PDF writer. Open WordPad.exe, type in whatever you want, then print it using "UTReport PDF Writer". Check C:\SampleOut.PDF, which is your output PDF file.

"Step5" to "Step8" let you have a chance to uninstall the printer you installed just now.

Points of Interest

Based on MSDN: Before an application calls the AddPrinterDriver function, all files required by the driver must be copied to the system's printer-driver directory. An application can retrieve the name of this directory by calling the GetPrinterDriverDirectory function. Therefore, in our demo, we have to copy all the printer driver files from c:\\UTReportPrerequisite\\Driver to the folder returned by GetPrinterDriverDirectory.

C++
//Install Driver

void CInstallPrinterDlg::OnBnClickedButtonStep3()
{
    CString msg="Failed";
    if (CopyPrintDriverFiles2System() && AddPrinterDriver())
        msg = "Add Printer Driver Successfully";
    
    AfxMessageBox(msg);

    return ;
}

The other tricky thing is the pDependentFiles field of DRIVER_INFO_3. From MSDN: pDependentFiles is a pointer to a null-terminated string that specifies the files the driver is dependent on. Each file name in the string is also terminated with a null (for example, "Pscript.dll\0Qms810.PPD\0Pscrptui.dll\0Pspcriptui.hlp\0Pstest.txt\0\0"). How do we assign a value to this field? My answer is:

C++
DRIVER_INFO_3 di3; 
...
di3.pDependentFiles = TEXT("hpbafd32.dll\0hpbftm32.dll\0HPLJ8550.cfg\" 
                           "0hpcdmc32.dll\0hpbcfgre.dll\0hpdcmon.dll\0\0");

In order to let our PDF writer work like a charm, we have to update the registry information for both Ghostscript and RedMon.

For RedMon:

C++
//#define PORT_KEY TEXT("SYSTEM\\ControlSet001\\Control\\"
//                      "Print\\Monitors\\Redirected Port\\Ports\\UTReportPDFPort:")

if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, PORT_KEY, 0, 
                       KEY_ALL_ACCESS, &hkey)) != ERROR_SUCCESS) 
{
    /* failed to open key, so try to create it */
    rc = RegCreateKey(HKEY_LOCAL_MACHINE, PORT_KEY, &hkey);
}
if (rc == ERROR_SUCCESS) 
{
    lstrcpy(buffer, "@c:\\UTReportPrerequisite\\gs\\pdfwrite.rsp -");
    RegSetValueEx(hkey, TEXT("Arguments"), 0, REG_SZ, 
                 (CONST BYTE *)buffer, lstrlen(buffer)+1);

    lstrcpy(buffer, "C:\\UTReportPrerequisite\\gs\\gs8.53\\bin\\gswin32c.exe");
    RegSetValueEx(hkey, TEXT("Command"), 0, REG_SZ, 
                 (CONST BYTE *)buffer, lstrlen(buffer)+1);

    dwValue =2;
    RegSetValueEx(hkey, TEXT("ShowWindow"), 0, 
                             REG_DWORD,(CONST BYTE *)&dwValue, 4);

    dwValue =0;
    RegSetValueEx(hkey, TEXT("RunUser"), 0, REG_DWORD,
                            (CONST BYTE *)&dwValue, 4);

    dwValue =300;
    RegSetValueEx(hkey, TEXT("Delay"), 0, REG_DWORD,
                 (CONST BYTE *)&dwValue, 4);

    RegCloseKey(hkey);
}

For Ghostscript:

C++
//#define GHOSTSCRIPT_KEY2 TEXT("SOFTWARE\\AFPL Ghostscript\\8.53")

if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, GHOSTSCRIPT_KEY2, 0, 
                       KEY_ALL_ACCESS, &hkey)) != ERROR_SUCCESS) 
{
    /* failed to open key, so try to create it */
    rc = RegCreateKey(HKEY_LOCAL_MACHINE, GHOSTSCRIPT_KEY2, &hkey);
}

if (rc == ERROR_SUCCESS) 
{
    lstrcpy(buffer, TEXT("C:\\UTReportPrerequisite\\gs\\gs8.53\\bin\\gsdll32.dll"));
    RegSetValueEx(hkey, TEXT("GS_DLL"), 0, REG_SZ, 
                 (CONST BYTE *)buffer, lstrlen(buffer)+1);

    lstrcpy(buffer, TEXT("C:\\UTReportPrerequisite\\gs\\gs8.53\\lib;C:\\" 
                         "UTReportPrerequisite\\gs\\fonts;C:\\UTReportPrerequisite" 
                         "\\gs\\gs8.53\\Resource"));
    RegSetValueEx(hkey, TEXT("GS_LIB"), 0, REG_SZ, 
                 (CONST BYTE *)buffer, lstrlen(buffer)+1);

    RegCloseKey(hkey);
}

pdfwrite.rsp is actually a parameter file used to control the PDF generation, i.e., page size, resolution, etc. -sOutputFile is used to control where the produced PDF file should go. For more details, please read the Ghostscript online help.

-Ic:\UTReportPrerequisite\gs\gs8.53\lib;c:\UTReportPrerequisite\gs\fonts
 -sDEVICE=pdfwrite
 -r600
 -dNOPAUSE
 -dSAFER
 -sPAPERSIZE=letter
 -sOutputFile="c:\SampleOut.PDF"
  1. AddPrinterDriver
  2. Updating the Registry
  3. About pdfwrite.rsp

Acknowledgement

First of all, thanks to all open source projects. Special thanks should be extended to Ghostscript and RedMon.

History

  • 2006/03/16: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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

Comments and Discussions

 
GeneralAddMonitor( ) failed Pin
vijboy5-Jun-07 16:44
vijboy5-Jun-07 16:44 
GeneralRe: AddMonitor( ) failed Pin
ytfrdfiw28-Jan-08 22:24
ytfrdfiw28-Jan-08 22:24 
QuestionPlease Help Pin
Ragha Venkat23-Apr-07 20:21
Ragha Venkat23-Apr-07 20:21 
AnswerRe: Please Help Pin
Ragha Venkat23-Apr-07 23:34
Ragha Venkat23-Apr-07 23:34 
GeneralError in printing PDF with PostScript downloaded fonts Pin
Ramya_Indian6928-Mar-07 23:50
Ramya_Indian6928-Mar-07 23:50 
QuestionVery Excellent Project Pin
MGHH24-Mar-07 2:38
MGHH24-Mar-07 2:38 
AnswerRe: Very Excellent Project Pin
ben68826-Mar-07 4:50
ben68826-Mar-07 4:50 
AnswerRe: Very Excellent Project Pin
illium5-Apr-07 11:50
illium5-Apr-07 11:50 
It's extremely easy to change the output format to an image file instead of pdf.

In the rsp file, change the device from "pdfwrite" to any ghostscript output device...

run "gswin32c --help" to get a list of supported output devices, which gives you this list:

bbox bit bitcmyk bitrgb bj10e bj200 bjc600 bjc800 bmp16 bmp16m bmp256
bmp32b bmpgray bmpmono bmpsep1 bmpsep8 cdeskjet cdj550 cdjcolor cdjmono
declj250 deskjet devicen display djet500 djet500c eps9high eps9mid epson
epsonc epswrite ibmpro ijs jetp3852 jpeg jpegcmyk jpeggray laserjet lbp8
lj250 ljet2p ljet3 ljet3d ljet4 ljet4d ljetplus m8510 mswindll mswinpr2
necp6 nullpage pbm pbmraw pcx16 pcx24b pcx256 pcxcmyk pcxgray pcxmono
pdfwrite pgm pgmraw pgnm pgnmraw pj pjxl pjxl300 pkmraw png16 png16m
png256 pngalpha pnggray pngmono pnm pnmraw ppm ppmraw ps2write psdcmyk
psdrgb psmono pswrite pxlcolor pxlmono r4081 spotcmyk st800 stcolor
t4693d2 t4693d4 t4693d8 tek4696 tiff12nc tiff24nc tiff32nc tiffcrle
tiffg3 tiffg32d tiffg4 tiffgray tifflzw tiffpack tiffsep uniprint



The png devices are png16, png16m, png256, pngalpha, pnggray, and pngmono. You can see there are devices for many other popular formats like bitmap, jpeg, tiff, etc..

Also, be sure to change the file extension on your output filename to match (unless you really want to write a bunch of .png files with the extension .pdf.. :P)

Hope that helps,
Troy


-----
"It's 5:50 a.m., Do you know where your stack pointer is?"

http://vanguard-against-confusion.blogspot.com

GeneralFilename extension replacement Pin
Chris Meech13-Mar-07 8:27
Chris Meech13-Mar-07 8:27 
GeneralRe: Filename extension replacement Pin
ben68814-Mar-07 10:43
ben68814-Mar-07 10:43 
GeneralA minor bug Pin
testano17-Nov-06 21:13
testano17-Nov-06 21:13 
GeneralRe: A minor bug Pin
ben68820-Nov-06 4:41
ben68820-Nov-06 4:41 
QuestionIs there a Problem with Paths including spaces? Pin
MyTassos16-Nov-06 8:14
MyTassos16-Nov-06 8:14 
AnswerRe: Is there a Problem with Paths including spaces? Pin
MyTassos16-Nov-06 22:20
MyTassos16-Nov-06 22:20 
GeneralThanks! Pin
sohbear24-Oct-06 16:28
sohbear24-Oct-06 16:28 
GeneralRe: Thanks! Pin
ben68825-Oct-06 7:07
ben68825-Oct-06 7:07 
QuestionHow do I print PDF Pin
davchen5-Oct-06 9:53
davchen5-Oct-06 9:53 
AnswerRe: How do I print PDF Pin
davchen5-Oct-06 9:58
davchen5-Oct-06 9:58 
GeneralRe: How do I print PDF Pin
ben6886-Oct-06 5:36
ben6886-Oct-06 5:36 
GeneralPrint Error Pin
Allen Friend13-Sep-06 15:26
Allen Friend13-Sep-06 15:26 
AnswerRe: Print Error Pin
ben68814-Sep-06 7:12
ben68814-Sep-06 7:12 
GeneralRe: Print Error Pin
Allen Friend14-Sep-06 11:32
Allen Friend14-Sep-06 11:32 
GeneralRe: Print Error Pin
ben68814-Sep-06 13:34
ben68814-Sep-06 13:34 
GeneralRe: Print Error Pin
Allen Friend14-Sep-06 22:33
Allen Friend14-Sep-06 22:33 
AnswerRe: Print Error Pin
Allen Friend15-Sep-06 14:11
Allen Friend15-Sep-06 14:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.