How to Implement Your Own Print Processor
Using the Microsoft DDK sample for your Print Processor makes it really easy to develop your own. The code in this article is based on the
genprint DDK and the sample, written in C, was converted to C++ to benefit from object-oriented model strengths.
The sample Print Processor which comes with this article contains an additional separator page for each print job with customizable content: a 1000x1000 bitmap image located at C:\SeparatorPP\Separator.bmp. You may replace it with a picture of your choice and the Print Processor will use it.
Take some time to go over the source files and understand their roles. Table 2 lists these files with their appropriate descriptions and types:
- DDK: This means the file was taken from the Microsoft DDK sample.
- DDK Modified: This means the file was taken from the Microsoft DDK sample and modified.
- New: This means the file is an entirely new creation.
|
File Name
|
Description
|
Type
|
|
SeparatorPP.def
|
Exports definition files
|
DDK (renamed from winprint.def)
|
|
SeparatorPP.cpp
|
This file implements functions that should be exported
by every Print Processor
|
DDK (renamed from genprint.c)
|
|
local.h; local.cpp
|
Debugging output functions
|
DDK (local.cpp is renamed from local.c)
|
|
support.cpp
|
Contains support routines for the Print Processor
|
DDK (renamed from support.c)
|
|
util.cpp
|
Spool memory management functions
|
DDK (renamed from util.c)
|
|
raw.cpp
|
Handles the RAW data type printing
|
DDK (renamed from raw.c)
|
|
text.cpp
|
Handles the TEXT data type printing
|
DDK (renamed from text.c)
|
|
parsparm.cpp
|
Handles sending formfeed to a printer
|
DDK (renamed from parsparm.c)
|
|
emf.cpp
|
Handles the NT EMF data type printing; this is
where the sample code involves addding a separator page
|
DDK Modified (renamed from
emf.c)
|
|
EMFJobModifier.h
|
Defines the abstract base class (CEMFJobModifier)
for EMF job modifications
|
New
|
|
JobSeparator.h; JobSeparator.cpp
|
Defines and implements the job modifier class, which
adds the separator page; derives from the CEMFJobModifier
class
|
New
|
Table 2. The table lists the sample source files and provides a short description and the origin of each file.
Because the DDK sample was originally written in pure C and converted to C++, all the files have the CPP extensions instead of the original C extensions.
Run the new code during the execution of the PrintDocumentOnPrintProcessor exported function. Listing 1 shows the standard implementation of the function (taken from SeparatorPP.cpp). As you can see, the function routes the execution to the appropriate handler, depending on the print job data type. For the sake of simplicity, this sample adds a separator only to NT EMF print jobs. This means you'll be concentrating on the PrintEMFJob function located in the emf.cpp source file. The function accepts two parameters:
- PPRINTPROCESSORDATA: The pointer to the job data.
- LPWSTR: The document name.
Listing 2 shows the code for the
PrintEMFJob function. Only the relevant code appears (for the full code, refer to the source code). The bold lines of code show the modifications that were made to support the separator page functionality. One of the first stages in printing the NT EMF job is to obtain the device context for the printer device (
hPrinterDC):
hSpoolHandle = GdiGetSpoolFileHandle(pData->pPrinterName,
pDevmode,
pDocumentName);
if (hSpoolHandle)
{
hPrinterDC = GdiGetDC(hSpoolHandle);
}
The code then uses this handle to add the separator page. Right after the
PrintEMFJob function starts the document printing, create an instance of the
CJobSeparator class and modify the print job:
DocInfo.cbSize = sizeof(DOCINFOW);
DocInfo.lpszDocName = pData->pDocument;
DocInfo.lpszOutput = pData->pOutputFile;
DocInfo.lpszDatatype = NULL;
if (!GdiStartDocEMF(hSpoolHandle, &DocInfo)) goto CleanUp;
bStartDoc = TRUE;
////////////////////////////////////////////////////////////
// Adding Separator Page
CJobSeparator JobSeparator(hPrinterDC);
JobSeparator.ModifyPrintout();
// End of Adding Separator Page
////////////////////////////////////////////////////////////
The actual job modification happens in the
ModifyPrintout function, which is implemented by the
CJobSeparator class (see
Listing 3).
The StartPage API starts a new page, where you draw the bitmap loaded from the C:\SeparatorPP\Separator.bmp file. ModifyPrintout uses the helper method RetrieveBitmapInfo to build the BITMAPINFO structure prior to stretching the bitmap to the printer device context. Finally, EndPage finishes printing the page.
After adding the separator page, PrintEMFJob completes the print job by playing EMF recordswith the help of the GDI APIand translating them into the specific printer device language.