This site uses advanced css techniques
When supporting a new kind of printer under Microsoft Windows, it's possible to create a print driver from scratch, but this is an enormous job both to create and to support over time across many versions of the operating system. To alleviate this burden, Microsoft has created several OEM printer drivers that can be customized to a very large extent, and this allows the developer to focus on just the unique aspects of the driver rather than the whole infrastructure.
Microsoft provides an interface for customization DLLs (aka "plugins") to augment driver functionality, and in this Tech Tip we're focusing on the PostScript OEM driver. Plugins conform to either the IPrintOemPS or IPrintOemPS2 interfaces to provide these extra functions, and multiple plugins can be included with a single driver.
A customization DLL can implement the IPrintOemPS::Command method to "hook" key points in PostScript output generation, and each callback is made with an index that indicates the current point in the output process. The customization DLL can add or replace PostScript text flowing to the printer.
An example is provided in the Windows Device Driver Kit in the %WINDDK%\src\print\oemdll\watermark\ project in the wmarkps\command.cpp file; it can overlay each page with custom text (perhaps, "DRAFT" or "TOP SECRET") as configured by the user in the print setup dialogs.
This sample customization DLL interjects at two points; PSINJECT_BEGINPROLOG is hooked once at the start of the document to download the PostScript procedures that perform the watermark overlay upon request, and PSINJECT_BEGINPAGESETUP once per page to trigger the actual watermarking on the paper.
While researching how to provide a different bit of functionality, we were confused at times trying to understand these injection points, especially while trying to visualize the overall flow of an entire print job. To aid this work, we created this Tech Tip as a reference; we hope others fine it useful also.
A plugin can implement the IPrintOemPS::Command method, which is called by the driver core with an index from the following table. This information was largely taken from Microsoft's MSDN page, and augmented with our own information.
Some of these indexes serve to inject (add/append) data to the output stream, while others serve to replace data already generated from the driver core. In addition, some indexes are at the overall job level (once per document), while others are per page. We've noted all of these in the table.
These injection points are generally treated on a text line basis, but we have seen cases where hooked output was not at the start of a line (in particular, with PSINJECT_SHOWPAGE in N-Up mode): the customization DLL should take this into account when generating its output.
These indexes are defined as C-language macros in the <wingdi.h> file.
PSINJECT index | Doc/ Page |
Replace /Inject |
Description |
---|---|---|---|
PSINJECT_BEGINSTREAM | Doc | Inject | Inserts text before the first byte of the job stream, including any initialization text specified by a PPD. |
PSINJECT_PSADOBE | Doc | Inject | Inserts text before the %!PS-Adobe line that generally marks the start of a PostScript data. This could be used to provide PJL (Printer Job Language, defined by HP) command sequences to control the job. |
PSINJECT_PAGESATEND | Doc | Replace | Replaces driver's %%Pages (atend) sequence with that generated by the plugin. This might be useful if the plugin knows in advance how many pages are coming and is able to provide the count up front. This is associated to the PSINJECT_PAGES hook that provides the actual page count at the end of the job. |
PSINJECT_PAGES | Doc | Replace | Replaces driver's %%Pages nnn output at the end of a job. Associated with the PSINJECT_PAGESATEND index which appears at the start of the job. |
PSINJECT_DOCNEEDEDRES | Doc | Inject | Allows the plugin to provide a list of resources after the existing %%DocumentNeededResources and list of those provided by the core driver. |
PSINJECT_DOCSUPPLIEDRES | Doc | Inject | Allows the plugin to provide a list of resources it has supplied after %%DocumentSuppliedResources and the list of driver-supplied resources. |
PSINJECT_PAGEORDER | Doc | Replace | Replaces driver's %%PageOrder |
PSINJECT_ORIENTATION | Doc | Replace | Replaces driver's %%Orientation |
PSINJECT_BOUNDINGBOX | Doc | Replace | Replaces driver's %%BoundingBox generated at the end of the document. Note that there is a %%BoundingBox: (atend) at the start of the document that does not appear subject to interception. |
PSINJECT_DOCUMENTPROCESSCOLORS | Doc | Replace | Replaces driver's %DocumentProcessColors color |
PSINJECT_COMMENTS | Doc | Inject | Before %%EndComments |
PSINJECT_BEGINDEFAULTS | Doc | Inject | After %%BeginDefaults |
PSINJECT_ENDDEFAULTS | Doc | Inject | Before %%EndDefaults |
PSINJECT_BEGINPROLOG | Doc | Inject | Adds data after the %%BeginProlog comment but before the core driver issues its own prolog. The Prolog is commonly used for sending fixed resources (whose names are perhaps also included with the PSINJECT_DOCSUPPLIEDRES index). |
PSINJECT_ENDPROLOG | Doc | Inject | Just before %%EndProlog but after all other prolog data has been sent. |
PSINJECT_BEGINSETUP | Doc | Inject | After %%BeginSetup but before the core driver issues its own setup commands. This is commonly used to configure resources previously sent in the Prolog section. |
PSINJECT_ENDSETUP | Doc | Inject | Before %%EndSetup but after all the other setup. |
PSINJECT_TRAILER | Doc | Inject | After %%Trailer |
PSINJECT_DOCUMENTPROCESSCOLORSATEND | Doc | Replace | Replaces driver's %%DocumentProcessColors (atend) |
PSINJECT_PAGENUMBER | Page | Replace | Replaces driver's %%Page |
PSINJECT_BEGINPAGESETUP | Page | Inject | After %%BeginPageSetup |
PSINJECT_ENDPAGESETUP | Page | Inject | Before %%EndPageSetup |
PSINJECT_PAGETRAILER | Page | Inject | After %%PageTrailer |
PSINJECT_PLATECOLOR | Page | Replace | Replace driver's %%PlateColor: color |
PSINJECT_SHOWPAGE | Page | Inject | Before the PostScript showpage operator is generated to display page. Note that this operator may not be generated literally; it may instead be part of a procedure that includes it (among other things). |
PSINJECT_PAGEBBOX | Page | Replace | Replaces driver's %%PageBoundingBox, which describes the bounding box at the start of a page. |
PSINJECT_ENDPAGECOMMENTS | Page | Inject | Before %%EndPageComments |
PSINJECT_VMSAVE | Page | Inject | Before the PostScript save operator |
PSINJECT_VMRESTORE | Page | Inject | After the PostScript restore operator |
PSINJECT_EOF | Doc | Inject | After %%EOF |
PSINJECT_ENDSTREAM | Doc | Inject | After the last byte of job stream |
A single PostScript driver can have multiple customization DLLs associated with it
There are two kinds of return codes from this method: the function itself S_OK, E_FAIL, or E_NOTIMPL, and the *pdwResult parameter can have ERROR_SUCCESS or ERROR_NOT_SUPPORTED stored. This produces a matrix which describes the various combinations to the best of our understanding.
PSINJECT Type |
Return Value |
*pdwResult | What it means |
---|---|---|---|
any | E_FAIL | any | Operation failed; usually bad I/O operation when writing data to the printer |
Replace | S_OK | ERROR_SUCCESS | This indicates that this customization DLL accepted responsibility for handling this request, that no other plugins should be called, and that the driver core should not send the data it's associated with the request. This means that this plugin should generate alternate data to the output stream. |
Replace | S_OK | ERROR_NOT_SUPPORTED | This indicates that the customization DLL has taken a pass
on this request, and that the driver core should keep looking
in the chain for another. If no plugin accepts responsibility
for the request, then it writes the data it has internally
associated with this request. Note that the plugin can nevertheless generate data to the output stream even while indicating a not-supported return; the caller has no idea that this output has occurred, and it could be used to annotate the "Replace" hooks. |
Inject | S_OK | any | The driver core has no idea whether this means that the customization DLL generated output or not, but it does not care; it continues to call additional plugins in either case. |
any | E_NOTIMPL | any | This entry point is not implemented; |
The best way to visualize these injection points is to see a multipage print job showing the injection points. Some are Injection, while others are Replacements, and all are optional. We have stripped out the actual PostScript code very heavily, the main purpose of this is to show the page structuring. Note that some of the "Replacement" injection points didn't have any text, so we don't know what they might be used for. These are noted with "Unknown example".
We've not seen every possible injection index used and don't know how to show where they might appear or be used.
Note that when a Repl injection index is used, existing output is suppressed. However, at no point does it appear that the customization DLL have access to the data that would have been generated by the core driver.
Inj PSINJECT_BEGINSTREAM ESC %-12345X@PJL JOB Inj PSINJECT_PSADOBE %!PS-Adobe-3.0 %%Creator: PScript5.dll Version 5.2.2 %%For: Steve %%BoundingBox: (atend) Repl %%Pages: (atend) PSINJECT_PAGESATENT Repl %%Orientation: Portrait PSINJECT_ORIENTATION Repl %%PageOrder: Special PSINJECT_PAGEORDER %%DocumentNeededResources: (atend) %%DocumentSuppliedResources: (atend) Repl Unknown example PSINJECT_DOCUMENTPROCESSCOLORSATEND Inj PSINJECT_COMMENTS %%EndComments %%BeginDefaults Inj PSINJECT_BEGINDEFAULTS Repl %%PageBoundingBox: 12 13 583 829 PSINJECT_PAGEBBOX %%ViewingOrientation: 1 0 0 1 Inj PSINJECT_ENDDEFAULTS %%EndDefaults %%BeginProlog Inj PSINJECT_BEGINPROLOG // Prolog (Resources) Inj PSINJECT_ENDPROLOG %%EndProlog %%BeginSetup Inj PSINJECT_BEGINSETUP // FEATURE SETUP (mainly from PPD) Inj PSINJECT_ENDSETUP %%EndSetup// START PAGE 1 Repl %%Page: 1 1 PSINJECT_PAGENUMBER Repl Unknown example PSINJECT_PLATECOLOR Repl %%PageBoundingBox: 12 13 583 829 PSINJECT_PAGEBBOX Inj PSINJECT_ENDPAGECOMMENTS %%EndPageComments %%BeginPageSetup Inj PSINJECT_BEGINPAGESETUP // PS PAGE SETUP %%EndPageSetup // PAGE 1 DATA Inj PSINJECT_SHOWPAGE LH % implements showpage %%PageTrailer Inj PSINJECT_PAGETRAILER// START PAGE 2 Repl %%Page: 2 2 PSINJECT_PAGENUMBER Repl Unknown example PSINJECT_PLATECOLOR Repl %%PageBoundingBox: 12 13 583 829 PSINJECT_PAGEBBOX Inj PSINJECT_ENDPAGECOMMENTS %%EndPageComments %%BeginPageSetup Inj PSINJECT_BEGINPAGESETUP // PS PAGE SETUP %%EndPageSetup // PAGE 2 DATA Inj PSINJECT_SHOWPAGE LH % implements showpage %%PageTrailer Inj PSINJECT_PAGETRAILER%%Trailer Inj PSINJECT_TRAILER Repl Unknown example PSINJECT_DOCUMENTPROCESSCOLORS Repl %%BoundingBox: 12 13 583 829 PSINJECT_BOUNDINGBOX %%DocumentNeededResources: // list of needed resources Inj PSINJECT_DOCNEEDEDRES %%DocumentSuppliedResources: // list of supplied resources Inj PSINJECT_DOCSUPPLIEDRES Repl %%Pages: 2 PSINJECT_PAGES %%EOF Inj PSINJECT_EOF ^D ESC %-12345X@PJL EOJ Inj PSINJECT_ENDSTREAM