TN02 : Configuring your printer using DEVMODE [Updated]

This forum contains a list of all our Technical Notes. These provide developers with sample applications and information on using our products.
Post Reply
Devteam
Posts: 115
Joined: Fri Oct 14 2005
Location: Montreal
Contact:

TN02 : Configuring your printer using DEVMODE [Updated]

Post by Devteam » Fri Oct 26 2012

Correctly using the DEVMODE structure to setup printer parameters

DEVMODE is a Windows structure that holds initialization and environment information about a printer.
It is made up of two parts: public and private. Using the Windows structure the developer can fully
control the printer settings of the Amyuni PDF Converter.

The public part contains data that is common to all printers. The private part contains data that is
specific to a particular printer. The private part can be of variable length and can contain anything the
printer driver designer decides to include in it.

One of trickier aspects of working with the DEVMODE structure is that it depends on the operating
system. Its length and content cannot be known beforehand. No assumptions can or should be made as
to the size of this structure in either its private or public part.

The DocumentProperties function retrieves or modifies printer initialization information or displays a
printer-configuration property sheet for the specified printer. To get the size of the DEVMODE structure
specific to the printer, we should use the DocumentProperties API call. This method is defined in
WINSPOOL.H as:

Code: Select all

DocumentProperties(hWnd, /* Handle of the parent window */
        hPrinter, /* Handle to our printer. */
        pDevice, /* Name of the printer. */
        NULL, /* Asking for size, so */
        NULL, /* these are not used. */
        0); /* Zero returns buffer size. */
DocumentProperties() returns the number of bytes that are required for a DEVMODE buffer when the
last parameter is set to 0. To obtain the size of the DEVMODE structure, we can use the following
instructions:

Code: Select all

/*
Obtain the size of the DEVMODE structure
*/
LONG lSize = 0 ;
LPDEVMODE lpDevMode = NULL ;
lSize = DocumentProperties( NULL, NULL, szPrinterName, NULL, NULL, 0 ) ;
if ( 0 == lSize )
{
        // error , printer is unknown
        return;
}
An alternative way would be to use the OpenPrinter API call, and use the handle returned by this
function to get the DEVMODE structure. The OpenPrinter function retrieves a handle to the specified
printer or print server or other types of handles in the print subsystem.

Code: Select all

/*
Alternative way of obtaining the size of the DEVMODE structure
*/
HANDLE hPrinter;
/* Start by opening the printer */
if ( !OpenPrinter( lpDeviceName, &hPrinter, NULL ) )
        return FALSE;
// get real size of DEVMODE
int nSize = DocumentProperties( NULL, hPrinter, NULL, NULL, NULL, 0 );
Once we have the real size of the structure, we can allocate memory for it and get the printer default
values for all parameters:

Code: Select all

lpDevMode = (LPDEVMODE)GlobalAllocPtr( lSize ) ;
DocumentProperties( NULL, NULL, szPrinterName, lpDevMode, NULL, DM_OUT_BUFFER );
This will also give us the size of the public and private members of the DEVMODE structure:
- lpDevMode->dmSize is the size of the public structure (common to all printers)
- lpDevMode->dmDriverExtra is the size of the private structure (specific to each
printer)

To modify one of the public printer parameters, for example printer resolution and paper orientation,
we can use the following instructions:

Code: Select all

lpDevMode->dmPrintQuality = 600; // set the resolution to 600 DPI
lpDevMode->dmOrientation = DMORIENT_PORTRAIT; // set orientation to landscape
lpDevMode->dmFields = DM_ORIENTATION | DM_PRINTQUALITY; // indicate which fields are modified
Two fields located at the start of the private data can be of interest to the programmer: dwOptions and
dwMargins
dwOptions contains compression and font embedding options as follows:
Bit 0 : flate compression of page content
Bit 1 : font embedding option
Bit 2 : JPEG compression
Bit 3 : 256 color compression

Example, to read or set the JPEG compression option, we would use the following instructions:

Code: Select all

LPDWORD lpdwFlags = (LPDWORD)((LPBYTE)lpDevMode + lpDevMode->dmSize );
BOOL bJpegCompression = (BOOL)(*lpdwFlags & 4); // this returns JPEG compression flag
// now invert the JPEG compression option
if ( bJpegCompression )
{
        *lpdwFlags = *lpdwFlags & ~4; // clear the JPEG compression flag
}
else
{
        *lpdwFlags = *lpdwFlags | 4; // set the JPEG compression flag
}
Another example in case you need to programmatically set or remove the font embedding option

Code: Select all

lpFlags = (LPDWORD)((LPBYTE)lpDevMode + lpDevMode->dmSize );
lpMargins = (LPDWORD)( (LPBYTE)lpDevMode + lpDevMode->dmSize + sizeof( DWORD ) );
*lpFlags |= DM_EMBED_FONTS|DM_EMBED_STANDARD_FONTS|DM_LICENSED_EMBEDDING|DM_MULTILINGUAL_SUPPORT;
dwMargins contains the minimum horizontal margin in the low part, and minimum vertical margin in the
high part. These are defined in 0.1 mm units.

To modify the minimum margins, we can use the following instructions:

Code: Select all

lpMargins = (LPDWORD)( (LPBYTE)lpDevMode + lpDevMode->dmSize + sizeof( DWORD ) );
*lpMargins = MAKELONG( 30, 30 ); // this will set the margins to 3 mm
Once the DEVMODE structure has been modified to suit our configuration, we can either create a
printer device context using the CreateDC API call:

Code: Select all

HDC hDC = CreateDC( _T( "winspool" ), szPrinter, NULL, lpDevMode );
Or make this configuration as the default for this printer and for all applications using this printer.

Code: Select all

DWORD dw;
PRINTER_INFO_2 *pi2;
// get default printer info structure which contains the DEVMODE
GetPrinter( m_hPrinter, 2, NULL, 0, &dw );
pi2 = (PRINTER_INFO_2 *)GlobalAllocPtr( GHND, dw );
GetPrinter( m_hPrinter, 2, (LPBYTE)pi2, dw, &dw );
// set the new printer info structure
pi2->pDevMode = lpDevMode;
SetPrinter( m_hPrinter, 2, (LPBYTE)pi2, 0 );
GlobalFreePtr( pi2 );
// notify applications that the default DEVMODE has changed
SendMessageTimeout( HWND_BROADCAST, WM_DEVMODECHANGE, 0, (LPARAM)szPrinter,
SMTO_NORMAL, 1000, NULL );
The Technical Note TN02, which can be downloaded from the link below, contains an MFC project that
illustrates use Windows DEVMODE structure to fully control the settings of the Amyuni PDF Converter
virtual printer driver.
http://www.amyuni.com/downloads/TN02.zip

Note: A driver supports only those DEVMODE members that are appropriate for the printer technology.

Post Reply