Does this site look plain?

This site uses advanced css techniques

IPrintOemPS::Command 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.

Injection Point Index Tokens

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.

Available PSINJECT indexes
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

"Command" function return values

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;

Example PostScript print job

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