PDDR/2 - Generic Printer Library

From EDM2
(Redirected from Generic Printer Library)
Jump to: navigation, search
Printer Device Driver Reference

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation

Contents

Generic Printer Library

The Generic Printer Library (GenPLib) is a set of subroutine components that can be used in the development of 32-bit hardcopy presentation drivers.

Presentation-driver developers often write similar sets of code to implement functions commonly required by hardcopy devices. Heap creation, output buffering, and banding raster data are examples. The GenPLib simplifies development and decreases the development time of 32-bit OS/2 printer presentation drivers because it provides these necessary functional components.

The GenPLib consists of function packages that implement some of the more difficult code that is needed to create a printer driver. The library format is a way to realize software reuse so that other developers can benefit from code that can be made completely independent of any driver.

GenPLib appears in the IBM Developer Connection Device Driver Kit for OS/2 (DDK) as a static library that is linked to OS/2 driver code. The GenPLib code is still in its early versions; updates to the functions and header files can occur with each release of the DDK. Eventually, the GenPLib may become a system DLL; but for now, you can use any static version of GenPLib . By convention, all functions exported from GenPLib begin with the Gpl prefix.

Because of the design of the modules of the GenPLib, some functions can also be used for development in areas other than OS/2 printer drivers.

Following are some reasons to use the GenPLib:

  • Allows developers to quickly, easily, and incrementally add function packages to their printer drivers.
  • Written in portable C source code that can be recompiled for other processors.
  • Provides 32-bit functions for new 32-bit operating-system environments, such as OS/2 2.0+SP and up-including OS/2 Warp.
  • Available in Intel, PowerPC, debug, and non-debug versions. The current 16 -bit drivers cannot run in these environments.
  • Provides a single, consistent source code for common functions.
  • Packages are designed using data abstraction.
  • Makes printer presentation-driver code more readable and maintainable.
  • Produces extensive debugging and logging information for developers and service personnel.
  • Implemented with the newest, most appropriate OS/2 system APIs, tailored to the underlying hardware and operating system version on which it is running (that is, system memory, paging behavior, CPU detection, etc.).

Note: The GenPLib has no 16-bit routines.

GenPLib Functional Packages

The GenPLib currently contains the following function packages:

#Error Assertion Provides a consistent way of checking for errors in driver code and reporting and logging useful information for developers and field support.
#Banding Simplifies the use of journaling that assists in banding pages of raster output data to printers.
#Color Dithering Provides color-dithering support.
#Gamma Correction Provides gamma-correction support.
#Compression Provides commonly used compression routines recognized by printers.
#Exception Management Provides ways of setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.
#Memory Management Provides a simple way of managing global and process memory needed by presentation drivers.
#Pattern Creation Provides functionality to create bitmap patterns.
#Output-Thread Management Provides multi-threaded output and buffer management to printer devices for improved performance.
#Semaphores Helps drivers protect their data in multitasking environments. The implementation detects the current CPU level and implements semaphores based on the smallest possible set of assembly instructions.
#String Management Provides a way to sort text strings that are being drawn to a printer output page so that they can be processed in many page orders when needed.

GenPLib Architecture

The GenPLib is a 32-bit static linking library, GENPLIB.LIB. Developers use LINK386.EXE to link the GPL with their 32-bit printer drivers.

Several C language include files (*.H) are associated with GENPLIB.LIB and define the function names and prototypes in the library. The main include file is GENPLIB.H.

GENPLIB.H includes other relevant library H files based on symbolics (INCLs ) defined to the compiler. For example, the following symbolics have a special meaning to the operation of GENPLIB.H when included by a C program:

INCL_GENPLIB_ASSERT
INCL_GENPLIB_ERROR
INCL_GENPLIB_HANDLER
INCL_GENPLIB_JOURNAL
INCL_GENPLIB_MEMORY
INCL_GENPLIB_SEMAPHORES
INCL_GENPLIB_STRINGSORT
INCL_GENPLIB_THREAD
INCL_GENPLIB_UTIL
INCL_GENPLIB_PATTERNS

To include all functions, use:

INCL_GENPLIB_ALL

When a developer uses GenPLib memory-management routines, the C source file that requires GenPLib memory management would include a set of code resembling the following:

#define INCL_GENPLIB_MEMORY
#include <genplib.h>

This code is included before any GenPLib memory-management functions or structures are referenced. The compiler automatically includes GPLMEM.H within GENPLIB.H.

Components of the Generic Print Library

The following sections describe the components and functions of the GenPLib .

Error Assertion

To use the GenPLib Error-Assertion package, you must include:

INCL_GENPLIB_ASSERT

The error-assertion package of the GenPLib consists of six APIs. Four are macros:

The remaining two are functions:

PMAssert Validates the return code from OS/2 Presentation Manager (PM) APIs that display the WinGetLastError() result and a supplied text string in a message box.
BaseAssert Validates OS/2 APIs that use the APIRET return code.
ASSERTT () and ASSERTF () ASSERTT causes an error assertion if the supplied test evaluates to TRUE. ASSERTF causes an error assertion if the supplied test evaluates to FALSE. An assertion is an INT3 with kernel debug information displaying the failed test, file name, and line number of the location from which the assertion was called.
DBPRINTF (()) and DBPRINTIF (()) DBPRINTF outputs the supplied text string to the kernel debugger. DBPRINTIF outputs the supplied text string to the kernel debugger if the supplied condition is TRUE.

ASSERTT () and ASSERTF ()

The ASSERTT and ASSERTF macros are very useful to precisely locate errors in code. Each is simply a test. If the test fails, the system generates a sound, stops the program, and displays a message. The more often an ASSERTT or ASSERTF macro is used, the better the chance that the first point of failure will be captured for analysis.

Use ASSERTT or ASSERTF prior to:

Pointers Assert that pointers are nonzero.
Divisors Assert that divisors are nonzero.
:Strings Assert that strings are valid.
:Copying Assert that enough space is present before copying.

Use ASSERTT or ASSERTF after:

OS/2 APIs and internal functions Assert result codes of OS/2 APIs and your internal functions.
Pointers Assert pointers after allocation.
Switch statements Use ASSERTT (TRUE) in the default case of switch statements

Do not use ASSERTT or ASSERTF in:

Essential Code - These macros are not included in the retail build of the product. For example, ASSERTT (0 != DosRequestMutexSem (hsem))

The following is sample output on the kernel debugger:

Assertion failed on line 41 of file code.c
'rc == 0'
eax=XXXXXXXX ebx=XXXXXXXX etc=XXXXXXXX edx=XXXXXXXX esi=XXXXXXXX edi=XXXXXXXX
eip=XXXXXXXX esp=XXXXXXXX ebp=XXXXXXXX iopl=2 -- -- -- xx xx xx xx xx xx xx xx
cs=XXXX ss=XXXX ds=XXXX es=XXXX fs=XXXX gs=XXXX  cr2=XXXXXXXX cr3=XXXXXXXX
program:CODE32:BREAKPOINT
XXXX:XXXXXXXX cc                 int    3
##

Use ASSERTT and ASSERTF under the following conditions:

ASSERTT (z);
where z is a Boolean expression that should evaluate to zero or FALSE on success.
ASSERTF (nz);
where nz is a Boolean expression that should evaluate to nonzero or TRUE on success.

The following example shows how to use ASSERTT and ASSERTF:

pointer = NULL;
rc = DosAllocMem( &pointer, size, PAG_EXECUTE | PAG_READ | PAG_WRITE );
ASSERTT (rc);       // Return code should be zero (success)
ASSERTF (pointer);  // Pointer should be nonzero

DBPRINTF (()) and DBPRINTIF (())

DBPRINTF uses the printf functionally of ANSI C to write messages to the kernel debugger screen. DBPRINTF is a macro; but because it can have a variable number of parameters, it must have an extra set of parentheses.

The following is the structure of DBPRINTF:

DBPRINTF(                // Outputs a text string to debug terminal (like C printf command)
   PSZ pszFormatString,  // String with same format options as printf command
   (...) );              // Parameter list of values to be placed in format string

The following is the structure of DBPRINTIF:

DBPRINTIF(                // A conditional DBPRINTF command
   BOOL bDoit,            // Outputs format string if this parameter evaluates to TRUE
   PSZ pszFormatString,   // String with same format options as printf command
   (...) );               // Parameter list of values to be placed in format string

ASSERTT, ASSERTF, DBPRINTF, and DBPRINTIF Notes:

ASSERTT and ASSERTF as well as DBPRINTF and DBPRINTIF are macros. They are built for the debug-level build and disappear for the retail-level build.

These macros rely on the PMDD device driver to be activated. To activate the PMDD device driver, add a /Cx parameter to the following statement (where x is the COM port number):

DEVICE=C:\OS2\PMDD.SYS /Cx

PMAssert

Description

This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. In addition to displaying a user-provided error message, the message box also displays the results of WinGetLastError (), the Presentation Manager error code. Use this function to assert OS/2 Presentation Manager APIs.

Format

PMAssert(                 // Display a message box with pszString, and show WinGetLastError()
     BOOL  bDoit,         // Display message box only if this expression evaluates to TRUE
     HAB   habErr,        // Anchor block handle of thread checking for a PM error
     PSZ   pszString );   // Message to display on message box with PM error code
Parameters
bDoit (BOOL) Boolean value. If TRUE, then a message box is displayed.
habErr (HAB) Anchor block of program.
pszString (PSZ) Message string.
Returns
TRUE Evaluated to TRUE
FALSE Evaluated to FALSE

Example Code

BOOL rc;
rc = GpiDestroyPS( hps );
PMAssert( rc == FALSE,
          hab,
          "GpiDestroyPS failed!" );

BaseAssert

Description

This function is similar to ASSERTT and ASSERTF, but it displays a message box if an error condition occurs. Use this function to assert return codes from those OS/2 APIs that use DOS Error-type return codes.

Format

BaseAssert(                // Display message box if a base error (DOS) is passed in (ulError)
      ULONG  ulError,      // Value of base error as defined in BSEERR.H header file
      PSZ    pszString );  // String to print out with base error code
Parameters
ulError (ULONG) Return code from OS/2 BaseAPI, either NO_ERROR (0) or one of the ERROR_xxx DOS Error codes. If ulError is not equal to NO_ERROR, a message box is displayed.
pszString (PSZ) Message string.
Returns
TRUE Evaluated to TRUE
FALSE Evaluated to FALSE

Example Code

APIRET apirc;
   apirc = DosAllocMem( pbuf, 1024h, PAG_READ | PAG_WRITE );
   BaseAssert( "DosAllocMem failed", apirc );

Banding

This component controls all aspects of managing a journal file and banding such as:

  • Calculating the band size-determines if banding is necessary based on the input parameters (resolution, bits/pel, page size, etc.).
  • Creating a journal file, and managing COM_DRAW flags.
  • Playing back either a full page or bands within a page, and managing the DC offset for each band.
  • Offering many different play options.
  • Handling destructive and non-destructive plays (if it is necessary to play more than once).
  • Handling portrait and landscape cases.
  • Playing bands from top-to-bottom or bottom-to-top.

To use the GenPLib Banding package, you must include:

INCL_GENPLIB_JOURNAL

Journaling Overview

A journal file is a record of all the GreXXX function calls that a driver receives between the time that it starts recording a journal file and the time it stops recording a journal file. Typically, recording starts at the beginning of a page and stops at the end of a page. After the recording has stopped, you can replay the journal file as often as necessary.

There are many reasons to record the GreXXX calls for a particular handle to a drawing context (HDC). Color sorting and banding are two examples.

For color sorting, printer commands are output for a particular color while the journal file is recorded. Then, the journal file is played back for the remaining colors. Printer commands are output only when the current color is the color with which you are working.
For banding, a portion of the page is defined as a bitmap and only that area is drawn in while the journal file is played back. Then, the DC origin is moved up the size of the band and the journal file is replayed. This must be done for every band on the page.

The following functions are used for journaling:

#GplQueryPhysicalMem
#GplJournalCalcBandSize
#GplJournalCreateInstance
#GplJournalPlayInstance
#GplJournalDeleteInstance
#GplJournalAbortDoc
#GplJournalResetAbortDoc

Call Flow

GplQueryPhysicalMem is the first function that you can call. It tells you how much physical RAM you have available.

GplJournalCalcBandSize is the second function that you can call. It is an optional helper function that tells you whether or not you need to band given your memory requirements and band size.

The next function that you call is GplJournalCreateInstance. This function initializes an instance or reuses an instance.

The next function that you call is GplJournalPlayInstanceGplJournalPlayInstance. This function replays the journal file either per-page or per-band; it can also preserve or change the attributes of the page.

Finally, you call GplJournalDeleteInstance to clean up the instance data.

GplQueryPhysicalMem

Description

This function tells you how much physical RAM is installed on your system.

The function ulPhysMem can be used in determining the amount of memory that should be reserved for banding. For the Omni driver, for example:

    if( ulPhysMem <= 4 )
       ulMaxBandMem = 256 * 1024;      // 256K bands
    elseif( ulPhysMem < 12 )
       ulMaxBandMem = 512 * 1024;      // 512K bands
    elseif( ulPhysMem < 20 )
       ulMaxBandMem = 1000 + 1024;     // 1M bands
    elseif( ulPhysMem >=20 )
       ulMaxBandMem = 2000 + 1024;     // 2M bands
Returns
ulPhysMem - Amount of physical RAM installed on system.

GplJournalCalcBandSize

Description

This function helps you decide if you need to break the page up into bands. If you do need to journal, then it will set up the band size to be constrained by the memory limit and to be a multiple of the modulus.

Based on the page size, number of bits per pel, number of color planes, etc ., the function calculates the number of bytes needed for the page bitmap. If the amount exceeds the memory limit, it calculates the number of bands into which to break the page.

Format

APIRET APIENTRY GplJournalCalcBandSize(
                   PIJOURNAL  pIJournal,           // Journal data structure
                   USHORT     usBitsPerPel,        // 1, 4, 8, or 24 bits per pel
                   USHORT     usNumPlanes,         // Number of color planes
                   USHORT     usModulus,           // Band size should be a multiple of this
                   USHORT     usDotsPerColumn,     // Resolution
                   ULONG      ulMaxBandMem );      // Maximum memory available for banding
Parameters
pIJournal (PIJOURNAL) Journal data structure. The following input fields are used:
usXLength Length of page in pels along x axis.
usYLength Length of page in pels along y axis.
bPortrait Orientation of page-portrait or landscape.
usBitsPerPel (USHORT) Page bitmap, number of bits per pel.
usNumPlanes (USHORT) Page bitmap, number of planes.
usModulus (USHORT) The band size must be zero or a multiple of this number. This may be necessary to cause byte, word, or Dword alignment to align band size to match the width of a print head. This functionality may also be helpful if you need to send the bits in a certain column height.
usDotsPerColumn (USHORT) Resolution.
ulMaxBandMem (ULONG) Maximum memory available for banding.
This parameter is the limit to the number of bytes that your band bitmap can exceed.
Returns
FALSE Journaling is not necessary.
TRUE Journaling is necessary. The following is filled in and valid:
pIJournal (PIJOURNAL)
Journal data structure
usBandLength
Band length in pels
pIJournal is output with usBandLength updated as follows:
x-band length indicates landscape orientation.
y-band length indicates portrait orientation.

Notes

This function should be called at COMPLETE_OPEN_DC time; because at this time, job properties have been obtained that may affect how pIJournalis filled in (for example, form size, mono/color selections).

GplJournalCreateInstance

Description

This function will create a reusable instance for a particular DC. The opposite of this function is #GplJournalDeleteInstance. Following are the four cases in which this function may be called:

Case 1 Ask for the number of bytes to allocate for the handle.
Case 2 DEVESC_STARTDOC time is the first (real) time this function is called. hJournal has been allocated. Initialize hJournal's bytes to 0.
Case 3 At DEVESC_NEWFRAME time, another page, more processing. After the call to #GplJournalPlayInstance has completed, set it up for the next page.
Case 4 At DEVESC_NEWFRAME time, another page, but this time we want the information about the page to change. The call to GplJournalPlayInstance has completed.

Format

APIRET APIENTRY GplJournalCreateInstance( HJOURNAL   hJournal,    // Journal handle
                                          PIJOURNAL  pIJournal,   // Journal data structure
                                          ULONG      ulFlags );   // Creation flags (defaults)
Parameters
hJournal (HJOURNAL) Handle to the journal file instance.
pIJournal (PIJOURNAL) A pointer to the input structure.
ulFlags (ULONG) This function performs different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
CREATE_DRAW_WHILE_RECORDING Turn on drawing while the journal file is being recorded.
Returns
Success TRUE
Failure FALSE

Implementation

This function will perform different actions based on the input parameters:

Case 1

The function will return the size (in the number of bytes) of a handle. It is the responsibility of the of caller to allocate and free the memory for a journal handle.

hJournal NULL.
pIJournal NULL.

Case 2

This function creates an engine journal file and starts recording to it. It also starts the accumulation of bounds. Call #GplJournalPlayInstance to stop recording and play the journal file.

hJournal Address of newly allocated memory.
pIJournal Address of the input structure. The structure has following format:
ulSize Size of this structure.
hModule Module handle of the calling program.
hdc Current HDC.
usXLength Page size in pels.
usYLength Page size in pels.
bPortrait Orientation of page.
TRUE -portrait
FALSE -landscape
usBandLength Band height in pels.
pfunBand Pointer to a callback function with the following prototype. This function is only called at the end of each band. (_System *)(PVOID, PRECTL)
pfunArg Any pointer. This will be passed to the first parameter of the previous callback function.
Flags Contains creation flags.

Case 3

The function performs the same functionality as Case 2. However, you can change any of the parameters in the input structure from the previous call to GplJournalCreateInstance.

hJournal Handle to a journal file instance.
pIJournal Address of the input structure. The structure has following format:
ulSize Size of this structure.
hModule Module handle of the calling program.
hdc Current HDC.
usXLength Page size in pels.
usYLength Page size in pels.
bPortrait Orientation of page.
TRUE -portrait
FALSE -landscape

Case 4

For this case, a new page has been sent and you will restart the entire process (Case 2). This function uses the same parameters that were given to the original call to GplJournalCreateInstance.

hJournal Handle to a journal file instance.
pIJournal NULL.

GplJournalPlayInstance

Description

This function signifies the end of the recording. If you want to call this function multiple times for a page, then for calls 1..n-1 the flag PLAY_ NONDESTRUCTIVE should be used. On the last call, n, the flag PLAY_ DESTRUCTIVE should be used.

Format

APIRET APIENTRY GplJournalPlayInstance( HJOURNAL  hJournal, ULONG     ulFlags );
Parameters
hJournal (HJOURNAL) Handle to the journal file instance.
ulFlags (ULONG) This function will perform different actions based on the ulFlags. The ulFlags is a bit vector (individual bits have different meanings). The options are separated into mutually exclusive groups that must have one member set.
PLAY_JOURNAL_BAND Play all bands at this time. The callback function that was defined at #GplJournalCreateInstance time will be called after each band has been played.
or
PLAY_JOURNAL_PAGE Plays the journal file once for this page.
-
PLAY_NONDESTRUCTIVE The DC state will not be altered after the call has been completed.
or
PLAY_DESTRUCTIVE The DC state will now have the final attributes of the page.
-
PLAY_TOP_TO_BOTTOM Bands are played from the maximum Y value to 0.
or
PLAY_BOTTOM_TO_TOP Bands are played from 0 to the maximum Y value.
Returns
Success TRUE
Failure FALSE

GplJournalDeleteInstance

Description

This function is the opposite of #GplJournalCreateInstance. It should be called at DEVESC_ENDDOC or enable subfunction 11 (begin close DC) time. It is responsible for cleaning up all of the resources that it used.

Format

APIRET APIENTRY GplJournalDeleteInstance( HJOURNAL hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

GplJournalAbortDoc

Description

This function is called when a DEVESC_ABORTDOC is received.

Format

APIRET APIENTRY GplJournalAbortDoc( HJOURNAL  hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

GplJournalResetAbortDoc

Description

This function is called when a DEVESC_ENDDOC is received and the job has been aborted.

Format

APIRET APIENTRY GplJournalResetAbortDoc( HJOURNAL  hJournal );
Parameter
hJournal (HJOURNAL) Handle to the journal file instance.
Returns
Success TRUE
Failure FALSE

Example Code

#GplQueryPhysicalMem Usage

Following is an example of GplQueryPhysicalMem usage:

case COMPLETE_OPEN_DC:                             // Final driver enable subfunction
{
  // If we are enabling for a DC where we may need to journal
  if( OD_DIRECT == ulDCType || PM_Q_RAW == ulDataType )
  {
    // Fill in journal information structure
    IJournal.ulSize      = sizeof( pddc->IJournal );
    IJournal.bPortrait   = ( pddc->pJobProps->ulOrientation == PORTRAIT );
    IJournal.hdc         = pddc->hdc;                         // Handle to DC
    IJournal.hModule     = globals.hModule;             // Driver's module handle
    IJournal.pfunBand    = (PJFUN)NewFrame;             // Callback function
    IJournal.pfunArg     = (PVOID)pddc;                 // Arguments to callback function
    IJournal.usXLength   = pddc->pCurrentForm->hcInfo.xPels;
    IJournal.usYLength   = pddc->pCurrentForm->hcInfo.yPels;

    // Find out how much physical RAM is installed on the system
    GplQueryPhysicalMem( &ulPhysMem );

    // Omni uses ulPhysMem to determine the maximum memory for banding
    If( ulPhysMem <= 4 )
        ulMaxBandMem = 256 * 1024;      // 256K bands
    elseif( ulPhysMem < 12 )
        ulMaxBandMem = 512 * 1024;      // 512K bands
    elseif( ulPhysMem < 20 )
        ulMaxBandMem = 1000 + 1024;     // 1M bands
    elseif( ulPhysMem >= 20 )
        ulMaxBandMem = 2000 + 1024;     // 2M bands

  } /* end if */
} /* end case */

#GplJournalCalcBandSize Usage

Following is an example of #GplJournalCalcBandSize usage during OS2_PM_DRV_ ENABLE for a dot-matrix printer:

case COMPLETE_OPEN_DC:                            // Final driver enable subfunction
{
    IJournal.ulSize      = sizeof( pddc->IJournal );
    IJournal.bPortrait   = TRUE;
    IJournal.hdc         = hdc;
    IJournal.hModule     = globals.hModule;
    IJournal.pfunBand    = (PJFUN)NewFrame;
    IJournal.pfunArg     = (PVOID)pddc;
    IJournal.usXLength   = hcInfo.xPels;
    IJournal.usYLength   = hcInfo.yPels;

    // NOTE: Modulus should be should be '1' on full-page printers or print-head width
    bBanding = GplJournalCalcBandSize(
                                     &IJournal,      // PIJournal
                                     1,              // Bits per pel
                                     1,              // Planes
                                     40,             // Modulus
                                     64000 );        // Maximum memory available

} /* end case */

#GplJournalCreateInstance Usage

Following is an example of GplJournalCreateInstance usage to allocate/create a journal instance handle during processing of the start-document escape. At this point, the port is opened and the output thread is already created.

case DEVESC_STARTDOC:
{
  if( ulDCType == OD_DIRECT || ulDataType == PM_Q_RAW )
  {
    // If we need to band (that implies journaling)
    if( pddc->fBanding )
    {
      //  First call to size of memory needed for journal handle
      ulSize = GplJournalCreateInstance( NULL, NULL, 0 );

      // Allocate memory needed for journal handle (driver's responsibility)
      hJournal = GplMemoryAlloc( hmcbHeap, ulSize );  // User per-DC heap

      // Allocate the memory for formal instance
      lrc = GplJournalCreateInstance( hJournal,        // Journal handle
                                      &IJournal ),     // Journal information structure
                                      0 );             // Creation flags (defaults)
    } /* end if */
  } /* end if */
/* end case */

#GplJournalPlayInstance and #GplJournalDeleteInstance Usage

Following are examples of #GplJournalPlayInstance and #GplJournalDeleteInstance during the processing of the end-document escape:

case DEVESC_ENDDOC:
{
  if( ulDCType == OD_DIRECT || ulDataType == PM_Q_RAW )
  {
    // If we need to band (that implies journaling)
    if( pddc->fBanding )
    {
      apiret = GplJournalPlayInstance( hJournal,
                                       PLAY_JOURNAL_BAND  |
                                       PLAY_DESTRUCTIVE   |
                                       PLAY_TOP_TO_BOTTOM );

      apiret = GplJournalDeleteInstance( hJournal );

      // Remember:  Free memory allocated for journal instance handle (hjournal)

     } /* end if */
  } /* end if */
} /* end case */

#GplJournalAbortDoc Usage

Following is an example of GplJournalAbortDoc usage:

case DEVESC_ABORTDOC:
{
  if( ulDCType == OD_DIRECT && ulDataType == PM_Q_RAW )
  {
    // If we have a journal file (that is, we are banding)
    if( pddc->hJournal )
    {
      // Inform journal code that abort condition is in effect
      GplJournalAbortDoc( pddc->hJournal );

    } /* end if */
  } /* end if */
} /* end case */

#GplJournalResetAbortDoc Usage

Following is an example of GplJournalResetAbortDoc usage:

case DEVESC_ENDDOC:
{
  if( ulDCType == OD_DIRECT && ulDataType == PM_Q_RAW )
  {
    // If we have been called with an abort document
    if( pddc->fAbortDocCalled )
    {
      // Inform journal code that abort condition is over
      GplJournalResetAbortDoc( pddc->hJournal );

    } /* end if */
  } /* end if */
} /* end case */

Color Dithering

Color dithering is technique of interleaving cyan, magenta, yellow, and optionally black so that the eye perceives another color-such as pink or orange. Color dithering converts picture pels in sizes 2, 4, 8, 16, 24, or 32 bits of RGB/K color to a 3 or 4 bitpel consisting of cyan, magenta, yellow, and optionally K(black). The best dither functions diffuse or spread out colors and also act as a low-pass filter to changes in color. The diffusion of color causes the printing of cyan, magenta, and yellow in the proper density so that from a distance, the eye perceives the correct color.

Following are the dithering functions:

Gamma-correction functions are a part of the color-dithering package and are used internally by the dithering code, but they are also available separately. See #Gamma Correction.

GplDitherCreateInstance

Description

To optimize throughput, a dither handle is initialized when this function is invoked. This includes allocation and initialization of transform tables and parameters.

Format

APIRET APIENTRY GplDitherCreateInstance(
                         PPVOID         ppdh,     // Returned pointer to dither handle
                                                  // pointer
                         HMCB           hmcb,     // User-specified memory handle
                         PDITHEREQUEST  pReq,     // Pointer to dither-request structure
                         PDITHERESULT   pReply ); // Pointer to dither-reply structure
Parameters
ppdh (PPVOID)
Pointer to the dither handle pointer that was created and returned by #GplDitherCreateInstance.
hmcb (HMCB)
If it is not zero, this parameter must be a handle (pointer to the API heap).
pReq (PDITHEREQUEST)
bReq (BYTE) Set to CMY_ALL or CMYK_ALL.
lDitherType (LONG)
From JobProperties.
HT_LEVEL
When selected, the following may be ORed with HT_LEVEL:
HT_PARAMETER
HT_MIN_COLOR
HT_PERCENT
HT_FUNCTION
HT_DITHER_4x4
Use a 4x4 halftone pattern for 16 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x4x4 byte array of dither patterns.
HT_DITHER_8x8
Use a 8x8 halftone pattern for 64 levels of perceived intensity. Also "snap.pHalftone" must be a pointer to a 16x8x8 byte array of dither patterns.
HT_MAGIC_SQUARES
A tessalated (tiled) dither that populates a dither matrix to guarantee a semi-random color mix yet controls average occurrence of colors (randomized pattern).
HT_ORDERED_SQUARES
Use recursive tesselated tiles of rectangular threshold arrays. Similar to HT_MAGIC_SQUARES, but uses a different method of populating dither matrix.
HT_ERR_DIFFUSION
Stucki Error Diffusion-a nice quality filter for photograph-quality images.
HT_FAST_DIFFUSION
Error diffusion with lower sampling rate, so error propagated less-essentially, a high-pass filter.
HT_STEINBERG_DIFFUSION
Steinberg Error Diffusion-slightly higher pass than Stucki.
SrcOptions (SRCMODS)
Flags that indicate the state of the buffer information.
fStartOfPage (BOOL)
Set in users NewFrame function; initialized by SYSTEM.
fNewColors (BOOL)
Set if pSrcInfo Color Table changes; initialized by SYSTEM.
fModify (BOOL)
Set if pSrc may be modified for K (Black) when CMY set.
fResetErrBuf (BOOL)
Set between pages or pictures when Err Buffers must be reset.
ulResetErrBufs (ULONG)
0,1,2,or 3 for Diffusion ONLY.
ulValue (ULONG)
xxx << 20 value to be set as initialization value.
snap (SNAPIT)
SYSTEM initialized midpoint and function control parameters.
pHalftone (PRGB2)
Pointer to 4x4 8x8 or user-supplied table.
ulParm (ULONG)
User-supplied midpoint or function address.
pArray (PBYTE)
Pointer to 16x16 tables such as paintMixer or OrderSquares.
ulResolution (ULONG)
The larger of x or y resolution.
iNumColors ( INT)
Set by user (2 ** number bits per pel).
iSrcRowPels (INT)
Number of pels in a source row.
iNumDitherRows (INT)
Number of rows in a band.
pSrcInfo (PBITMAPINFO2)
Pointer to user supplied BITMAPINFO2 structure; requires that cx and cy be set.
pbSrc (PBYTE)
Printer to RGB2 input bitmap (rows are 32bit aligned).
iNumSrcRowBytes32 (INT)
iDWordBytesInCMYKBandRow.
iNumDestRowBytes (INT)
iBytesInSinglePlaneBandRow.
delta (DELTACOLOR)
From JobProperties for tessalated-squares dithering only .
lHue (LONG) 0..360
lSaturation (LONG) 0..100
lValue (LONG) 0..100
fRemoveBlackColor (BOOL)||Eliminate coincident black and color after dithering.
ulRGamma (ULONG)
3 .. 255 true gamma value; actual value times 10.
ulRBias (ULONG)
0 .. 255 gamma table minimum value.
ulGGamma (ULONG)
3 .. 255 true gamma value.
ulGBias (ULONG)
0 .. 255 gamma table minimum value.
ulBGamma (ULONG)
3 .. 255 true gamma value.
ulBBias (ULONG)
0 .. 255 gamma table minimum value.
pReply (PDITHERESULT)
Reply structure is allocated and initialized on return
Returns
Success NO_ERROR 0
Failure INVALID_PARAMETER

Example Code

Following is an example of filling the dither-request structure and using GplDitherCreateInstance from the Omni driver during start-document processing:

 pHandle->Req.iSrcRowPels = iBitsInArray;

 // Number of bytes in source, dword aligned
 pHandle->Req.iNumSrcRowBytes32 = iDWordBitsInArray/8;

 // Number of bytes in destination, byte aligned
 pHandle->Req.iNumDestRowBytes = iBitsInArray/8;

 // Fill in number of colors (derived from bitcount)
 pHandle->Req.iNumColors = Power ( 2, pddc->bmp.cBitCount );
 pHandle->Req.iNumDitherRows = 1;

 if( pddc->pdb->pResInfo->ulXRes >= pddc->pdb->pResInfo->ulYRes )
   pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulXRes;
 else
   pHandle->Req.ulResolution = pddc->pdb->pResInfo->ulYRes;

 /*-------------------*/
 /* Color Technology  */
 /*-------------------*/

 pHandle->Req.ulRGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bRGamma;
 pHandle->Req.ulGGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bGGamma;
 pHandle->Req.ulBGamma = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBGamma;
 pHandle->Req.ulRBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
 pHandle->Req.ulGBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;
 pHandle->Req.ulBBias  = (ULONG)( pddc->pdb->pDriver->pGammas->pSelect+i )->bBias;

 pHandle->Req.ulRGamma = (ULONG)( (LONG)pHandle->Req.ulRGamma + pddc->pdb->pJobProperties->lRedGamma );
 pHandle->Req.ulGGamma = (ULONG)( (LONG)pHandle->Req.ulGGamma + pddc->pdb->pJobProperties->lGreenGamma );
 pHandle->Req.ulBGamma = (ULONG)( (LONG)pHandle->Req.ulBGamma + pddc->pdb->pJobProperties->lBlueGamma );
 pHandle->Req.ulRBias  = (ULONG)( (LONG)pHandle->Req.ulRBias  + pddc->pdb->pJobProperties->lRedBias );
 pHandle->Req.ulGBias  = (ULONG)( (LONG)pHandle->Req.ulGBias  + pddc->pdb->pJobProperties->lGreenBias );
 pHandle->Req.ulBBias  = (ULONG)( (LONG)pHandle->Req.ulBBias  + pddc->pdb->pJobProperties->lBlueBias );

 /*-------------------*/
 /* Color Algorithm   */
 /*-------------------*/

 switch ( pddc->pdb->pPrintMode->ulColorTech )
 {
   case COLOR_TECH_CMYK:
     pHandle->Req.bReq = CMYK_ALL;
     break;

   case COLOR_TECH_CMY:
     pHandle->Req.bReq = CMY_ALL;
     break;
 }

 /*-------------------*/
 /* Color Algorithm   */
 /*-------------------*/

 // Need gamma buffers only if printing in color (CMY/CMYK)

 if( pddc->pdb->pPrintMode->ulColorTech != COLOR_TECH_K )
 {
   // Init modify flag telling Gamma/HSV that we may replace/change
   // values in the source bitmap.  1,2,4,8 bit pel bitmaps; then
   // if C,M,Y are to be printed, the RGB index will be set to white.
   // If 16, 24 bit bitmaps, then the R,G,B value is replaced with white.
   pHandle->Req.SrcOptions.fModify  = TRUE;

   /*-------------------*/
   /* HSV               */
   /*-------------------*/

   pHandle->Req.delta.lHue        = pddc->pdb->pJobProperties->lHue;
   pHandle->Req.delta.lSaturation = pddc->pdb->pJobProperties->lSaturation;
   pHandle->Req.delta.lValue      = pddc->pdb->pJobProperties->lValue;

   /*-------------------*/
   /* Dither            */
   /*-------------------*/

   pHandle->Req.lDitherType = pddc->pdb->pJobProperties->ulAlgorithm;

   GPL_DITHER_SETUP_DEFAULTS( pHandle->Req,  pddc->pdb->pJobProperties->ulLevel )
 }

 // Set printer's physical head position in our ddc 
 pddc -> ptlPrintHead.x = 0;

 if ( ORIENTATION_PORTRAIT == pddc -> pdb -> pJobProperties -> ulOrientation ) 
     pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.yPels - 1;
 else
     pddc -> ptlPrintHead.y = pddc -> pdb -> pFormInfo -> hcInfo.xPels - 1;

 GplDitherCreateInstance ( (PPVOID) & pHandle -> pdh,
                             pddc -> pdb -> hmcbHeap,
                           (PDITHEREQUEST) & pHandle -> Req, 
                           (PDITHERESULT ) & pHandle -> Reply);

 /*--------------------------------*/
 /* Init Dither Req struct         */
 /*--------------------------------*/

 pCMYReq->pSrcInfo = pbmi;   // Passed to choose rasterize

 LOOP {                           // Each band

   // Copy raster bits into our buffer
   pCMYReq->pbSrc = pbBitMap + iCurrentRow * pHandle->Req.iNumSrcRowBytes32;

   GplDitherRGBtoCMYK ( (PPVOID)&pHandle->pdh,
                        pCMYReq,
                        (PDITHERESULT)&pHandle->Reply );

GplDitherRGBtoCMYK

Description

This function performs the dithering.

Format

APIRET APIENTRY GplDitherRGBtoCMYK( PHDITHER       pdh,
                                    PDITHEREQUEST  pReq
                                    PDITHERESULT   pReply );
Parameters
pdh (PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
pReq (PDITHEREQUEST)
If a diffusion dither algorithm is selected, use the macro GPL_SETUP_DITHER( req, ulLevel ) to set the SrcOptions structure.
or
If Magic Squares or Ordered Squares is selected, the following fields must be set:
lHue
lSaturation
lValue Address of the API's pre-stuffed request structure. The following fields must be set by the API:
fNewColors Must be set to TRUE at the beginning of the page. This element is set to FALSE on return.
If fNewColors equals TRUE, the following two fields must be set:
ulGamma
lBias
pSrcInfo Must have the following two fields set:
cx
cy
If BitsPerPel is less than 16 and fNewColors equals TRUE, the pbmi array, argbColor[256] must be initialized.
pbSrc Pointer to the surface bitmap.
pReply (PDITHERESULT)
Pointer to the reply structure.
Returns
Success NO_ERROR 0
Failure INVALID_PARAMETER

Example Code

Following is an example of dithering RGB to CMYK data from the Omni driver' s "render" callback function:

// Change logical height of band bitmap to 1 (this device chooses to dither each scanline)
ulSavedcy = pddc->pbmi->cy;
pddc->pbmi->cy = 1;

// For each row in the current band's bitmap (each scanline)
for( iCurrentRow=ulSavedcy; iCurrentRow > 0; iCurrentRow-- )
{
  // Set pointer to current row in our band's bitmap
  cmykReq.pbSrc = pbBits + iCurrentRow * iDWordBytesInCMYKBandRow;

  GplDitherRGBtoCMYK ( &pddc->pdh, &pddc->cmykReq, &pddc->pReply );

  // Now our consecutive KCMY buffers contain dithered data to send to printer

} /* end for */

// Restore bitmap height to TRUE value (no longer 1)
pddc->pbmi->cy = ulSavedcy;

GplDitherRGBtoRGB

Description

This function is a shell for #GplDitherRGBtoCMYK. It simply inverts the CMY pDest buffer to RGB for a printer that requires RGB.

Format

APIRET APIENTRY GplDitherRGBtoRGB( PHDITHER       pdh,
                                   PDITHEREQUEST  pReq
                                   PDITHERESULT   pReply );
Parameters
pdh (PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
pReq (PDITHEREQUEST)
Address of the API's pre-filled request structure.
pReply (PDITHERESULT)
Address of the reply structure.
Returns
Success NO_ERROR

GplDitherDeleteInstance

Description

This function must be called at the end of a document so that memory that was allocated in #GplDitherCreateInstance can be freed.

Format

APIRET APIENTRY GplDitherDeleteInstance( PHDITHER  pdh );
Parameter
pdh(PHDITHER)
Pointer to the dither handler that was created by #GplDitherCreateInstance.
Returns
Success NO_ERROR

Example Code

Following is an example of using GplDitherDeleteInstance:

// Tell the dither function to deallocate all it's buffers
GplDitherDeleteInstance( (HDITHER)pHandle->pdh );

// Reminder:
// Free all buffers our driver allocated on behalf of GENPLIB's dithering code
// This includes error buffers for error-diffusion algorithms
// as well as the destination buffers used for rendering

Gamma Correction

Printer paper, ink, resolution, and other characteristics can cause the actual printed color to be different from the desired color. This "skew" is non-linear and occurs over the entire color spectrum. A gamma-compensation curve is used for each color to approximate the non-linear skew. Because the reflected frequencies of colors can cause them to appear different than expected, you can modify the gamma of each color. Increasing the gamma will lighten an image that might otherwise look too dark.

Following are the gamma functions:

GplGammaCreateTable

Description

This function creates an RGB (K) gamma table based on input gammas and bias .

Format

APIRET APIENTRY GplGammaCreateTable( PBYTE  pbGamma,
                                     LONG   lRgamma,
                                     LONG   lGgamma,
                                     LONG   lBgamma,
                                     LONG   lbias );
Parameters
pbGamma (PBYTE)
A byte pointer to a 768-byte buffer.
pbGamma can be used as a PRGB2 instead of a PBYTE.
lRgamma (LONG)
Red. Gamma multiplied by 10, with a range of 1 to 99.
lGgamma (LONG)
Green. Gamma multiplied by 10, with a range of 1 to 99.
lBgamma (LONG)
Blue. Gamma multiplied by 10, with a range of 1 to 99.
lbias (LONG)
Offset, normally zero, generally for printers over 600 dpi.
Returns
Success 0
Failure INVALID_PARAMETER

GplGammaColorTableToGamma2

Description

For 8-bit pels or less, replaces an RGB color value in the PBITMAPINFO2 argbColor[0..256] structure with a gamma-corrected color value.

Format

APIRET APIENTRY GplGammaColorTableToGamma2( PBYTE          pbGamma,
                                            PBITMAPINFO2   pbmi );
Parameters
pbGamma (PBYTE)
A byte pointer to a 768-byte buffer.
pbGamma can be used as a PRGB2 instead of a PBYTE.
pbmi (PBITMAPINFO2)
PBITMAPINFO2 is assumed to have a valid RGB2 color table as input. The following PBITMAPINFO2 fields are used:
cBitCount - Bits per pel.
argbColor - When cBitCount is 8 or less, this parameter must include values for 2 to the power of cBitCount (2**cBitCount).
Returns
Success 0
Failure INVALID_PARAMETER

Compression

The compression package contains routines that compress raster image data using common printer hardware-supported compression routines.

Raster printer drivers render on a per-page basis into a raster representation of an output page. As soon as rendering is completed for a given page, that raster data can be compressed into a format that the printer hardware supports. Compressed data is typically smaller than the original uncompressed data and will therefore transmit faster across communication lines to the printer.

Note: The caller of a compression routine should allocate a buffer to hold the compressed data whose size (in bytes) is as large as the the original uncompressed data.

To use the GenPLib Compression package, you must include:

INCL_GENPLIB_COMPRESS

Following are the GenPLib compression routines:

GplCompressRLL

Description

This function performs run-length limited (RLL) encoding.

Format

LONG APIENTRY GplCompressRLL(
                     PBYTE  pbDataIn,      // Input raster data to compress (page/band)
                     LONG   cbDataIn,      // Length of input data buffer (pbDataIn)
                     PBYTE  pbDataOut,     // Output buffer to which compressed data is written
                     LONG   cbDataOut );   // Length count of compressed data written
Parameters
pbDataIn (PBYTE)
Pointer to input data.
cbDataIn (LONG)
Count of bytes in, pointed to by pbData.
pbDataOut (PBYTE)
Pointer to output data.
cbDataOut (LONG)
Size of buffer in bytes pointed to by pbDataOut.
Returns
Signed LONG, which is the length in bytes of the compressed data pointed to by cbDataOut.
Success
Number of bytes in compressed return string.
Failure
GPLCOMPRESS_ERROR (-1)
The output buffer length has been exceeded.

Notes

The input data is encoded with pairs of bytes. The first byte is the replacement count and the second byte is the data. A replacement count of 0 means that the data is not repeated.

GplCompressTIFF

Description

This function performs tagged image file format (TIFF) encoding, PackBits encoding algorithm. This function compresses a scanline.

Once in a repeated run, this function stops and emits a block if a different byte is encountered.

Once in a literal run, this function stops and emits a block if at least four repeated bytes follow. The next block will be a repeat block. Repeats of two or more instances of a byte b are represented by -(n-1) b. Literal runs of different bytes b1 b2 b3 ... bn are represented by (n-1) b1 b2 b3 . .. bn.

Format

LONG APIENTRY GplCompressTIFF(
                     PBYTE  pbDataIn,      // Input raster data to compress (page/band)
                     LONG   cbDataIn,      // Length of input data buffer (pbDataIn)
                     PBYTE  pbDataOut,     // Output buffer to which compressed data is written
                     LONG   cbDataOut );   // Length count of compressed data written
Parameters
pbDataIn (PBYTE)
Pointer to input data.
cbDataIn (LONG)
Count of bytes in.
pbDataOut (PBYTE)
Pointer to output data.
cbDataOut (LONG)
Size of buffer in bytes pointed to by pbDataOut.
Returns
Success Count of bytes put in destination.
Failure GPLCOMPRESS_ERROR (-1)

GplCompressDeltaRow

Description

This function performs delta-row encoding. This method replaces only the bytes in a new row that are different from the preceding (seed) row. Unreplaced bytes are replicated from the seed row. The new row then becomes the seed row after it is printed.

Format

INT APIENTRY GplCompressDeltaRow(
                      INT      usTotalBytes,   // Bytes in the scan line
                      PBYTE    pbData,         // Input raster data to compress
                      PBYTE    pbLastLine,     // Previous scanline's raster data
                      INT      usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,       // Output buffer with compressed data
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs
Parameters
usTotalBytes (INT)
Count of bytes in, pointed to by pbData.
pbData (PBYTE)
Pointer to bytes of data to compress.
pbLastLine (PBYTE)
Pointer to previous scanline's raster data.
usMaxReturn (INT)
Size of output buffer.
pbReturn (PBYTE)
Pointer to compressed data will be written.
pDeltas (PUSHORT)
Differences indexes = array of start and end pairs.
Note: This array is returned as the result of calling #GplCompressChooseMode; therefore, you must call GplCompressChooseMode() before calling this function.
Returns
0 Current and previous line are identical.
>0 Length of data pointed to by pbReturn.
-1 GPLCOMPRESS_ERROR
usMaxReturn is not large enough for the compressed output.

GplCompressRLLDeltaRow

Description

This function performs RLL delta-row encoding. It compresses a string of bytes and returns the compressed string at pbReturn and the new string usLength as its return value. This method replaces only bytes in the current row that are different from the proceeding seed row. Unlike #GplCompressDeltaRow, however, the replacement or delta bytes themselves are encoded.

Format

INT APIENTRY GplCompressRLLDeltaRow(
                      INT      usTotalBytes,   // Bytes in the scan line
                      PBYTE    pbData,         // Input raster data to compress
                      PBYTE    pbLastLine,     // Previous scanline's raster data
                      INT      usMaxReturn,    // Length of output buffer
                      PBYTE    pbReturn,       // Output buffer with compressed data
                      PUSHORT  pDeltas );      // Differences indexes-start/end pairs
Parameters
usTotalBytes (INT)
Count of bytes pointed to by pbData and pbLastLine.
pbData (PBYTE)
Pointer to bytes of data to compress.
pbLastLine (PBYTE)
Pointer to previous uncompressed data.
usMaxReturn (INT)
Size of output buffer.
pbReturn (PBYTE)
Pointer to compressed data will be written.
pDeltas (PUSHORT)
Differences indexes = array of start and end pairs.
Returns
0 Current and previous line are identical.
>0 Length of data pointed to by pbReturn.
-1 GPLCOMPRESS_ERROR
usMaxOutput not large enough for the compressed output.

GplCompressChooseMode

Description

This function uses a semi-intelligent algorithm to select the compression mode that will most likely give the best compression for the row of data.

RLL Delta Row is currently chosen if this method best and if the printer supports this compression. Delta Row is chosen if most of the bytes are the same as the corresponding bytes in the previous row. TIFF is chosen if most of the bytes are repeated within this row. RLL is chosen if most of the bytes are repeated within this row and the printer does not support TIFF PackBits. No compression (value of 0) is chosen of most bytes are neither repeats within the row nor duplicates of the previous row.

Note: The function GplCompressChooseMode() must be run before either of the delta-compression functions #GplCompressDeltaRow() or #GplCompressRLLDeltaRow(). This function must be run first because pDelta is filled in by GplCompressChooseMode() while it is reading pbRow and pbLast_ Row. Performance is increased by combining GplCompressChooseMode() with either #GplCompressDeltaRow() or #GplCompressRLLDeltaRow().

Format

INT APIENTRY GplCompressChooseMode( PBYTE    pbRow,
                                    PBYTE    pbLast_Row,
                                    INT      usRow_Length,
                                    ULONG    CompressModes,
                                    PUSHORT  pDelta );
Parameters
pbRow (PBYTE)
Pointer to the current row's data.
pbLast_Row (PBYTE)
Pointer to the last row's data.
usRow_Length (INT)
The number of bytes in the row. Note that both the previous row and the current row are equal in size.
CompressModes (ULONG)
Compression modes supported.
This is a ULONG with the follow bit positions used to represent compress modes that this printer supports:
GPLCOMPRESS_NONE
GPLCOMPRESS_RLL
GPLCOMPRESS_TIFF
GPLCOMPRESS_DELTAROW
GPLCOMPRESS_RLLDELTAROW
These values may be logically ORed together to create a bitfield of all compression methods to consider. For example,
CompressModes = GPLCOMPRESS_RLL | GPLCOMPRESS_TIFF
This tells the routine to only choose between the RLL and TIFF compression routines and GPLCOMPRESS_NONE.
pDelta (PUSHORT) Array of unsigned shorts to hold the offsets from the beginning of pbRow that differ from pbLast_Row. This data will be used by #GplCompressDeltaRow and #GplCompressRLLDeltaRow. Note that pDelta must have a length equal to two times usRow_Length plus one to hold the pDelta output . There is no check on the size of pDelta. You must supply a size of a minimum of two times usRow_Length plus one. This is because the maximum number of differences occur when every other byte differs from the preceding row.
Returns
The mode that will probably compress the best:
0 No compression
1 RLL (run-length limited) compression
2 TIFF (tagged image file format) compression
3 Delta-row compression
9 RLL delta-row compression

Notes

For #GplCompressDeltaRow and #GplCompressRLLDeltaRow, pDelta is filled with the start and end of the difference fragments. Note that a difference fragment is defined as a contiguous group of bytes in the current row that differ from those in the previous row. For example:

Column Number 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Current Row Byte Value 7 8 1 0 4 5 6 7 23 128 257 1 7 8
Previous Row Byte Value 7 1 3 1 4 5 9 7 23 128 7 9 7 8

There are three difference fragments:

  • Fragment one begins at column 2 and ends at column 4.
  • Fragment two begins at column 7 and ends at column 7.
  • Fragment three begins at column 11 and ends at column 12.

The output in pDelta will be 2 4 7 7 11 12 0. Note that these are unsigned shorts and the difference array is null-terminated. If no difference fragments are found (the previous and current buffers are the same) the first unsigned short in pDelta will be 0.

pDelta above will point to a array of the indexes stored to used by GplCompressDeltaRow() and GplCompressRLLDeltaRow().

Example Code

Following is an example of GplCompressChooseMode usage from the Omni driver for a typical inkjet device:

iNewMode = GplCompressChooseMode(
                 pbBuffer,             // Contains current row of data
                 pbLastLine,           // Contains previous row of data
                 iBytesInRow,          // Number of bytes in output row (pbBuffer)
                 GPLCOMPRESS_RLL |     // All compression modes supported
                 GPLCOMPRESS_TIFF,
                 pDelta );             // Offsets into pbBuffer ((2 x pbBuffer) + 1)

  if( iNewMode != iCurrentMode )
  {
    // Send command to printer to set new mode
    iCurrentMode = iNewMode;

  } /* end if */

  switch( iNewMode )
  {
     case 0:                           // No Compression
     break;
     case 1: iCompressed = GplCompressRLL();
     break;
     case 2: iCompressed = GplCompressTIFF();
     break;
     case 3: iCompressed = GplCompressDeltaRow();
     break;
     case 9: iCompressed = GplCompressRLLDeltaRow();
     break;

  } /* end switch */

  // Write raster data transfer command and compressed data

GplCompressAscii85

Description

This function performs ASCII encoding. It encodes 4 bytes in to 5 bytes out , and it converts data from a base 256 on input to a base-85 representation on output. The output is then output in ASCII with base 85.

Buffer allocation is the responsibility of the driver.

Format

INT APIENTRY GplCompressAscii85(
                     PBYTE  pSource,         // Source input data buffer (normal Hex 256)
                     PBYTE  pDest,           // Destination output data buffer (ASCII 85)
                     INT    usSourceLen );   // Source buffer input length
Parameters
pSource (PBYTE)
Pointer to input data buffer.
pDest (PBYTE)
Pointer to output data buffer. It must be at least 5/4 size of pSource.
usSourceLen (LONG)
Length of source buffer in bytes.
Returns
Success: Count of bytes put in destination.
Failure: GPLCOMPRESS_ERROR (-1)

GplFaxG3EncodeBlock

Description

This function performs G3 "Fax"-type compression.

It provides a run-length type compression with table-lookup replacement encodings for certain bit sequences. This type of compression is also know as "Modified Huffman." Format

GplFaxG3EncodeBlock( HTHREAD hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Parameters
hThread (HTHREAD)
Handle.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Number of output bytes; must be divisible by 4.
pulBitposition (PULONG)
Pointer to bit position in output buffer; gets updated and must be stored off the pddc.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
The prototype for this function must be:

void pfnFlushBuffer ( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as G4.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success: Count of data written out (compressed data count).
Failure: GPLCOMPRESS_ERROR (-1)
Related Functions
The following G3 "Fax"-compression related functions provide help in sending down special G3 commands.
GplFaxG3StartBlock()
Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of the first page. This function outputs the EOL to pbOutput and initializes pulBitposition to 12. It simply sends out the EOL bit sequence to signal the start of raster data (for example, '000000000001' binary, eleven 0's and a single 1).
Inputs
pbOutput Output buffer to fill
pulBitposition Bit-position count in the output buffer
Returns Void
GplFaxG3EndBlock()
End of block flushed.

GplFaxG3EndBlock( PVOID hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread PVOID.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1).
GplFaxG3EndDoc()
Modified Huffman (MH) Group 3 return to control (RTC) sent at the end of document.

GplFaxG3EndDoc( PVOID hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread PVOID.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; gets updated and must be stored off the pddc.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1).
GplFaxG3NewFrame()
Modified Huffman (MH) Group 3 end of line (EOL) sent at the beginning of each new page. This function is the same as GplFaxG3EncodeBlock() except that it flushes data at this point to cause the current page to be completely sent. This call sends compressed data followed by the EOL bit sequence (see GplFaxG3StartBlock).
Instead of GplFaxG3NewFrame() you can use pfnFlushBuffer() = NULL to see whether or not the buffer for GplFaxG3EncodeBlock() is large enough. You output the bytes after GplFaxG3EncodeBlock() returns OK. You can then call GplFaxG3StartBlock(). This function places an EOL in the output buffer and sets the pulBitposition to 12.

GplFaxG3NewFrame( HTHREAD hThread,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition
PFN pfnFlushBuffer );

Inputs
hThread HTHREAD
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits.
pulBitposition Bit-position count in the output buffer.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as above.
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)

GplFaxG4EncodeBlock

Description

This function performs G4 "Fax"-type compression.

It is similar to #GplFaxG3EncodeBlock(), but it has five extra parameters. These extra parameters are used to keep track of on/off bit runs between calls.

The driver is again responsible for all memory allocations.

Make sure all buffers for on/off bit tracking are equal to scanline DWORD aligned.

Format

GplFaxG4EncodeBlock( PVOID pddc,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer,
PULONG pulwstartc,
PULONG pulbstartc,
PULONG pulwstartp,
PULONG pulbstartp,
ULONG ulSkipCount );

Parameters
pddc (PVOID)
First parameter to pfnFlushBuffer; generally pddc or hThread.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition (PULONG)
Pointer to position in the output buffer; gets updated and must be unique to each print job; *pulBitposition must be zero on the first invocation of this function for a block.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits; must be less than or equal to 2560 + 63 = 2623.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
The prototype for this function must be:

void pfnFlushBuffer ( PVOID,
PBYTE,
ULONG );

PVOID Use is the same as G3; necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
pfnFlushBuffer can be either NULL or point to a function that you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
If pfnFlushBuffer() = NULL this function will compress the data and place it into pbOutput. If the output buffer size is not large enough for all the compressed data, the function will return with -1 = error. If pfnFlushBuffer() is not NULL, every time pbOutput is full, pfnFlushBuffer( ) is called to write the compressed bytes out. Note that the return value will the total number of bytes written even though the buffer was filled multiple times.
pulwstartc (PULONG)
Pointer to array of ULONGs. Its length must be ( ulBitwide + 31 ) / 32 + 2. The 2 extra ULONGs are needed. So for an image of 371 bits width, the length of this array is ( 371 + 31 ) / 32 = 12 plus 2 more-which is 14 unsigned longs. This array is used to store the start bits of the beginning of consecutive 0 bits for the current scan line.
pulbstartc (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the current scan line.
pulwstartp (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 0 bits for the previous scan line. This must be zero filled on the first invocation of this function.
pulbstartp (PULONG)
Size is the same calculation as above. This array is used to store the start bits of the beginning of consecutive 1 bits for the previous scan line. This must be zero filled on the first invocation of this function.
ulSkipCount (ULONG)
Number of bytes to skip to the next row of input data to compress.
Returns
Success: Count of data written out (compressed data count).
Failure: GPLCOMPRESS_ERROR (-1)
Related Function
The following is a G4-compression related function automatically sends the "End Block" data sequence (24 bits represented by Hex 001001).
GplFaxG4EndBlock()
Modified Read (MRR) Group 4 compression with K = infinity for sending end-of-facsimile block (EOFB). This function has the same first eight parameters as GplFaxG4EncodeBlock().

GplFaxG4EndBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Inputs
pddc PVOID; used as first parameter to the pfnFlushBuffer function; generally the pddc or HTHREAD.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; gets updated and must be unique to each print job.
ulBitwide Width of the raster image in bits.
ulBitheight Height of the raster image in bits.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
pfnFlushBuffer can be either NULL or point to the function you supply that will write the compressed bytes out. If pfnFlushBuffer is not NULL, this function will be called when the output buffer is full. If pfnFlushBuffer is NULL, you must write the bytes out yourself.
Returns
Success Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)

GplFaxTIFF2EncodeBlock

Description

TIFF algorithm 2 compression. This function essentially provides a " Modified Huffman" G3 compression with no size limits. It is limited only by the byte representation of ULONG (4 Gigabytes).

Format

GplFaxTIFF2EncodeBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Parameters
pHandle (PVOID)
Pointer to a handle.
pbOutput (PBYTE)
Pointer to output buffer to fill.
ulOutputSize (ULONG)
Size in bytes of output buffer; must be a multiple of 32 bits; must be divisible by 4.
pulBitposition (PULONG)
Pointer to position in output buffer.
pbInput (PBYTE)
Input pointer to upper left corner of raster image to compress.
ulBitwide (ULONG)
Width of raster image to compress in bits.
ulBitheight (ULONG)
Height of raster image to compress in bits.
pfnFlushBuffer (PFN)
Flush buffer function; prototype for this function must be:

void pfnFlushBuffer( PVOID,
PBYTE,
ULONG );

PVOID Necessary parameter for separating printout; generally pddc or hThread.
PBYTE Unsigned char * to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
Success:Count of data written out (compressed data count).
Failure GPLCOMPRESS_ERROR (-1)
Related Functions
The following are TIFF2-compression related functions.
GplFaxTIFF2StartBlock()
TIFF algorithm 2 compression start-of-document function.
Inputs
pulBitposition Bit-position count in the output buffer; *pulBitposition gets updated and must be stored off the pddc; function just sets pulBitposition to 0 (you can do this yourself).
Returns Void
GplFaxTIFF2EndBlock()
TIFF algorithm 2 compression to flush output buffer.

GplFaxTIFF2EndBlock( PVOID pHandle,
PBYTE pbOutput,
ULONG ulOutputSize,
PULONG pulBitposition,
PBYTE pbInput,
ULONG ulBitwide,
ULONG ulBitheight,
PFN pfnFlushBuffer );

Inputs
hThread HTHREAD.
pbOutput Output buffer to fill.
ulOutputSize Size in bytes of output buffer; must be divisible by 4.
pulBitposition Bit-position count in the output buffer; must be a multiple of 32 bits; gets updated and must be stored off the pddc.
pbInput Input pointer to upper left corner of raster image to compress.
ulBitwide Width of the raster image in bits.
ulBitheight Height of the raster image in bits.
pfnFlushBuffer Flush buffer function; prototype for this function must be:

int pfnFlushBuffer( HTHREAD,
PBYTE,
ULONG );

HTHREAD Gotten from GplThreadCreateInstance().
PBYTE Unsigned char to output buffer; same as pbOutput.
ULONG Unsigned long; size of pbOutput buffer; same as ulOutputSize.
Returns
TRUE If OK
FALSE If error

Exception Management

This package contains functions for setting errors with WinSetErrorInfo and logging appropriate warnings, exceptions, and error codes.

Four ascending severity levels are defined for error messages:

Warning The function detected a problem, took corrective action, and was able to complete processing successfully.
Error The function detected a problem for which no sensible corrective action is possible. The function is not executed and the system remains at the same state as when the function was requested.
Severe Error The function detected a problem from which the system cannot reestablish its state. The function has partially executed and the application must now make some corrective action to restore the system to a known state.
Unrecoverable Error The function detected an error from which it is impossible for the system to reestablish the state that it held at the time that the function was called. It is also impossible for the application to restore the system to a known state.

To use the GenPLib Exception-Management package, you must include:

INCL_GENPLIB_ERROR

The following functions are included in the package:

GplErrSetDosError

Description

This function saves DOS error.

Format
ERRORID APIENTRY GplErrSetDosError( USHORT usDosError );
Parameter
usDosError (USHORT)
Any error code defined in BSEERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

GplErrSetError

Description

This function saves an error.

Format
ERRORID APIENTRY GplErrSetError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure None

GplErrSetSevereError

Description

This function saves a severe error.

Format
ERRORID APIENTRY GplErrSetSevereError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

GplErrSetWarning

Description

This function saves the error warning.

Format
ERRORID APIENTRY GplErrSetWarning( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success:ERRORID
Failure: None

GplErrSetUnrecoverableError

Description

This function saves an unrecoverable error.

Format
ERRORID APIENTRY GplErrSetUnrecoverableError( USHORT usError );
Parameter
usError (USHORT)
Any error code defined in PMERR.H that best identifies the failing event.
Returns
Success: ERRORID
Failure: None

Memory Management

This package manages global and process (shared and non-shared) memory areas (heaps) using a single handle to a memory control block (HMCB) for all allocations. Therefore, you do not need to manage different heap or memory handles within your code.

This package calls appropriate OS/2 system APIs for memory creation and allocation. Dosxxx, SSxxx, and other API decisions are optimized for current OS/2 version performance. Examples include the underlying file system paging architecture and the number of processes in system.

Memory areas (heaps) are automatically grown when the initial memory size runs out. Committing more memory than needed is not required. Your code can use a single HMCB and underlying code will manage multiple memory areas ( heaps) if necessary.

Sets of debug trace information are displayed whenever memory is overwritten by other processes or writes to memory exceed the allocation size. Memory that has been allocated and not freed (memory leaks) is also shown as debug information.

To use the GenPLib Memory-Management package, you must include:

INCL_GENPLIB_MEMORY

The following APIs comprise the GenPLib Memory-Management package:

Call Flow

The GplMemoryCreateInstance() function should be the first memory API called in order to get a handle to a memory control block (HMCB). The HMCB handles all memory allocations. Using an HMCB, any number of memory allocations can be made using GplMemoryAlloc(). These allocations should be freed using GplMemoryFree.

GplMemoryDeleteInstance() is used when the HMCB is no longer needed for memory allocations.

Note: Any memory that was allocated using GplMemoryAlloc() using an HMCB being deleted by GplMemoryDeleteInstance and not freed by a corresponding GplMemoryFree() call will be shown as debug output (with location of function where allocation was done) for developers to correct memory leaks within their code.

GplMemoryCreateInstance

Description

This function creates an MCB and a user heap.

Format

HMCB APIENTRY GplMemoryCreateInstance( ULONG ulPrimarySize,
ULONG ulExtSize,
ULONG ulThreshold,
ULONG ulType );

Parameters
ulPrimarySize (ULONG)
Initial Heap size in bytes.
ulExtSize (ULONG)
Heap growth size fills in bytes.
ulThreshold (ULONG)
Threshold for heap allocation. Requests larger than this will be converted to DosAllocMem.
ulType (ULONG)
Heap type (process, shared). See the following notes for a description of process and shared heaps.
Returns
Success: Valid handle to memory control block.
Failure: NULL.
WinGetLastError() will return one of the following:
PMERR_MEMORY_ALLOCATION_ERROR
PMERR_INV_OR_INCOMPAT_OPTIONS
Notes

Treat creating a memory instance like creating a memory heap. The ulSize parameter should be a reasonable amount of memory for most of your processing. It is not necessary to set ulSize to a high number so that you do not run out of memory, because the memory area will automatically grow if your processing allocates more than the amount originally set by ulSize.

When the memory area is automatically grown, the ulExtSize value is used to determine how large an additional memory area should be created. A default value of 0 (zero) may be used, and the growth size will be tailored to a reasonable amount for the OS/2 version.

The #GplMemoryAlloc() API will allocate memory blocks differently based on the ulThreshold value. Typically, allocations of memory of sizes less than the threshold are allocated in a fast access memory area, and sizes larger than the threshold are allocated from a different memory pool. A default value of 0 (zero) may be used and the threshold will be automatically set to perform most efficiently on the current OS/2 version.

The ulType parameter allows you to specify the type of memory that you want to allocate. This defines the scope of accessibility to the memory within your driver or program and is one of the following:

SHARED_MEMORY
Used to create a global memory area (heap). For example, it is used for allocating global data like string tables in a printer driver.
PROCESS_MEMORY
Used to create a per process memory area (heap). For example , it is used for allocating a per device context (DC) memory in a printer driver.

GplMemoryDeleteInstance

Description

This function frees memory heap(s) for the user.

Format
APIRET APIENTRY GplMemoryDeleteInstance( HMCB  hMCB );
Parameter:hMCB (HMCB)
Pointer to memory allocated.
Returns
Success: NO_ERROR. Returns valid handle to memory control block.
Failure: ERROR_INVALID_PARA
or return values from:
SSFreeMem
DosFreeMem
DosSubUnsetMem
WinGetLastError() returns one of the following:
PM_INV_OR_INCOMPAT_OPTIONS
PMERR_MEMORY_DEALLOCATION_ERR

GplMemoryAlloc

Description

This function allocates memory for the user.

Format
PVOID APIENTRY GplMemoryAlloc( HMCB   hMCB,
                               ULONG  ulSize );
Parameters
hMCB (HMCB)
Handle to memory control block.
ulSize (ULONG)
Amount to allocate.
Returns
Success: Valid pointer to memory allocated.
Failure: NULL.
WinGetLastError() returns one of the following:
PMERR_INV_OR_INCOMPAT_OPTIONS
PMERR_INSUFFICIENT_MEMORY

GplMemoryFree

Description

This function frees memory for a user.

Format
APIRET APIENTRY GplMemoryFree( PVOID  pData );
Parameter
pData (PVOID)
Pointer to memory allocated.
Returns
Success: NO_ERROR.
Failure: ERROR_INVALID_PARA
or return values from:
SSFreeMem
DosFreeMem
DosSubFreeMem
WinGetLastError() returns one of the following:
PM_INV_OR_INCOMPAT_OPTIONS
PMERR_MEMORY_DEALLOCATION_ERR

GplMemoryGetObjectSize

Description

This function returns the size of a memory object allocated by #GplMemoryAlloc.

Format
LONG APIENTRY GplMemoryGetObjectSize( PVOID  pData );
Parameter
pData (PVOID)
Pointer to object.
Returns
Success :Size of memory object.
(LONG) lsize
Failure: 0, no size found for memory object. WinGetLastError will return PMERR_INVALID_PARAMETERS (meaning pData was invalid or NULL).

GplMemorySetUserID

Description

This function is optional, and it is helpful in debugging. You attach a pointer to the heap instance (like your pddc or pdb)-this extra pointer can then be viewed under the kernel debugger.

Format
VOID APIENTRY GplMemorySetUserID( HMCB   hMCB,
                                  ULONG  ulUserID );
Parameters
hMCB (HMCB)
Handle to memory control block.
ulUserID (ULONG)
Pointer to something helpful.

Implementation

The GenPLib Memory-Management package was created to provide consistency and to simplify the growth and management of heaps.

This package was one of the first developed for driver use. When writing a driver "from scratch" or modifying a driver to use GenPLib, begin with this package because many of the other GenPLib packages require memory allocated in the unique format of the memory-management package.

Previously, all drivers had to manage their own memory heaps, often using memory allocation packages exported from other parts of the OS/2 system-for example, the INCL_WINHEAP functions from PM WIN or the Subsystem Allocation routines in the PM Graphics Rendering Engine (GRE). It was never clear what routines should be used in a presentation driver or where they should be used (especially for portability). Many of these packages did not allow the caller to grow their heaps, provided no controls for memory leakage, and had no validation or recovery when a driver terminated unexpectedly.

A typical raster printer driver creates two types of heaps: a shared heap during Dynamic Link Library (DLL) initialization processing, and a per-DC heap when the PM GRE enables the driver during the FILL_PHYSICAL (02 Hex) subfunction. These heaps are freed during corresponding DLL termination and DISABLE_PHYSICAL calls to the driver.

In addition, a per-process heap is also created for the OS2_PM_DRV_DEVMODE entry point in a printer presentation driver. This entry point causes the driver to display its printer and job properties dialogs to the user or to retrieve printer and job property information for a specific output device/ destination.

A typical driver initially allocates a 256KB shared global heap and a 100KB per-DC heap and elects to use the memory package's default heap growth size and threshold for larger allocations (achieved by passing in 0L for parameters 2 and 3 for #GplMemoryDeleteInstance).

In case of unexpected driver termination, a driver should register a per- process exception handler for each of the major presentation driver entry points. This exception handler can contain cleanup code that frees any per- process heaps created using #GplMemoryCreateInstance in case any code problems cause an exception to occur.

One major benefit of the GenPLib memory code is the ability to detect memory leaks (memory a driver allocates but never frees). Whenever a call to #GplMemoryDeleteInstanceis made, all memory allocated from the heap is freed so that no leaks can occur. If the debug version of the GenPLib library is used, while #GplMemoryDeleteInstance is cleaning up memory areas that a developer forgot to free, it will inform the developer on a debugging terminal which line of code allocated the memory that was not freed. This is possible because the memory-management package records the function address (and other information) from where #GplMemoryAlloc is called.

An OS/2 presentation driver must consider the memory constraints of the user and other programs in the system. In the past, drivers allocated large heaps, hoping the memory an application needed would never overrun the amount initially allocated and cause an exception. Under the paging architecture of OS/2 Warp, however, all the heap memory is often committed during heap creation.

All this memory could be forced in and never used by an application (for example, an application queries the driver for the devices it supports and performs no output). In this case, the driver's memory pages would not be touched, so OS/2 would slowly page it out and give it back to the applications that had it in the first place. As a result, a good deal of swapping could occur for active addressable memory, especially on low-RAM systems. The alternative was to force the driver to manage its own heap growth and consolidate multiple heaps.

Using the GenPLib Memory-Management package, the heap is identified to the driver as a generic handle to a memory control block (HMCB), not as an address into memory. This handle is supplied by #GplMemoryCreateInstance and is used to identify the heap for all subsequent memory allocations and frees.

Therefore, a reasonable initial heap can be created that is just large enough to allow the driver to enable and respond to basic queries. As applications use the driver to render graphics (or other memory-intensive operations), the memory package detects when a given #GplMemoryAlloc call will overflow the current heap and grows it by a default or specified size. The caller does not ever need to know that the heap has grown because it continues to use the same HMCB, while the memory package chains these heaps together.

Example Code

Creating and Allocating From a Per-DC Heap

Following is an example of creating and allocating from a per-DC heap during a printer-driver enable via the OS2_PM_DRV_ENABLE entry point:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )             // Determine which enable/disable subfunction
  {
    case FILL_PHYSICAL:           // Enable subfunction
    {
       // Create a per-DC memory area (heap)
       hmcbHeap = GplMemoryCreateInstance(
                           LEN_DCHEAP,            // Size of DC Heap (80K)
                           0L,                    // Default extent
                           0L,                    // Default threshold
                           PROCESS_MEMORY );      // Type of heap (define in GPLMEM.H)

       // If GplMemoryCreateInstance succeeded
       if(hmcbHeap != NULL);
       {
         // Allocate PDEVICEBLOCK
         pddc->pdb = ( PDEVICEBLOCK ) GplMemoryAlloc( hmcbHeap, sizeof( DEVICEBLOCK ) );

       } /* end if */
     } /* end case */
     break;
  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */

Note: System memory must be used when passing data buffers to PrtWrite() calls.

Creating a Global Heap

Following is an example of creating a global heap:

ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )        // Flag to determine which load/unload module subfunction
  {
    case 0:               // Load module
    {
      // Create a global heap
      globals.pvHeap = GplMemoryCreateInstance(
                                 LEN_SHAREDHEAP,   // Initial size (256K)
                                 0,                // Default extent
                                 0,                // Default threshold
                                 SHARED_MEMORY);   // Type of heap (defined in GPLMEM.H)
    } /* end case */
    break;
  } /* end switch */
} /* end _DLL_InitTerm */
Allocating From a Global Heap

Following is an example of allocating from a global heap:

switch( ulSubFunc )                       // Determine which enable/disable subfunction
{
  case FILL_LOGICAL:                      // Enable subfunction
  {
    // For every string in our driver's string resource table
    for( i=0; i < STRING_TABLE_SIZE; i++ )
    {
      // Load each string from resource table (returns length)
      sLen = WinLoadString (szTemp);

      // Allocate memory to hold string just loaded from global heap
      globals.pbStringTable[i] = GplMemoryAlloc( globals.pvSharedHeap, sLen+1 );

      // Copy string from temporary buffer to our driver's global string table
      strcpy( globals.pbStringTable[i], szTemp );

     } /* end for */
     break;
   } /* end case */
} /* end switch */
Deleting a Per-DC Heap

Following is an example of deleting a per-DC heap:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )
  {
    case DISABLE_PHYSICAL:
    {
      // Free per-DC heap after freeing per-DC memory objects
      GplMemoryDeleteInstance( hmcbHeap );

    } /* end case */
    break;
   } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
Deleting a Global Heap

Following is an example of deleting a global heap:

ULONG APIENTRY  _DLL_InitTerm( ULONG hThisModule, ULONG ulFlag )
{
  switch( ulFlag )
  {
    case 1:
    {
      // Free global heap after freeing global memory objects
      GplMemoryDeleteInstance( globals.pvSharedHeap );

    } /* end case */
    break;
  } /* end switch */
} /* end _DLL_InitTerm */

Output-Thread Management

The output-thread management module provides a way to create multiple output threads that will manage the sending of data buffers to the printer. This functionality has also been called second-thread code; however, the implementation allows a separate thread per physical output address (for example, LPT, COM, and file).

This package seamlessly manages multiple buffer allocations and chains them together. It adjusts the internal data buffering to always try to keep the printer busy processing data. The functions assure that buffer sizes and their reuse align for the best performance with the system-memory memory model (for example, the OS/2 paging architecture).

Note: You may override the default buffer size and the maximum number of buffers to create.

Other benefits of using the separate output-thread code in your driver include:

  • Ability to pass off data buffers as fast as you can fill them without waiting for a device with slower transfer speeds
  • Unknowingly, the OS/2 PM graphics engine (and other system components) might have locked down system resources, such as fonts, while calling your driver. Therefore, implementing the thread code allows you to return to the graphics engine and free up the resource for other parts of the system, such as the display. This makes the system and applications more responsive to the user.
  • The package uses the GenPLib Exception-Management package for simple debug tracing.
  • The package uses the GenPLib Memory-Management package for allocation of instance data.

To use the GenPLib Output-Thread Management package, you must include:

INCL_GENPLIB_THREAD

Following are the Output-Thread Management APIs:

Call Flow

The thread instance (data and semaphores) is initialized; but a real thread is not spun off until you are certain that the application is really sending data to be printed. Therefore, you can systematically use GplThreadCreateInstance() at enable and GplThreadStart() at the first data write. This keeps the code straightforward.

GplThreadCreateInstance

Description

This function is used to allocate and initialize thread instance data. This function must be called before making any other thread calls in order to get a thread instance handle (HTHREAD). Semaphores and buffers are not created or allocated by this call.

Format

BOOL APIENTRY GplThreadCreateInstance( HMCB hMcb
HMODULE hMod,
HTHREAD *phThread,
PVOID pvUserData );

Parameters
hMcb (HMCB)
Handle to the memory control block (See #Memory Management).
hMod (HMODULE)
Handle of calling module. Used for exception management and error logging.
*phThread (HTHREAD)
-output Thread instance handle.
pvUserData (PVOID)
-output This is used in debugging and is optional. It is recommended that you place a helpful pointer, such as your pddc or pdb pointer.
Returns
Success: TRUE
Failure: FALSE

GplThreadStart

Description

This function starts or updates thread parameters. It creates semaphores and buffers, committing system resources.

Format

BOOL APIENTRY GplThreadStart( HTHREAD hThread,
LHANDLE hSpooler,
HFILE hDev,
ULONG ulBufferSize,
USHORT usMaxBuffers,
UCHAR ucAbortChar,
ULONG ulAbortCharCount,
ULONG ulAbortBufferSize,
PBYTE pbAbortBuffer,
PSZ pszAbortString,
PSZ pszLogAddress,
PSZ pszWriteError,
PSZ pszDeviceDescription,
LONG lDCType );

Parameters
hThread (HTHREAD)
Instance handle of thread to start. Returned by #GplThreadCreateInstance.
hSpooler (LHANDLE)
Handle to Spooler (OD_QUEUED). Returned from SplQmOpen. May be zero (0) if hDev is supplied.
hDev (HFILE)
Handle to Device (OD_DIRECT). Returned from PrtOpen. May be zero (0) if hSpooler is supplied.
ulBufferSize (ULONG)
Size of buffer allocations. May be 0 to use default size.
usMaxBuffers (USHORT)
Maximum number of buffers to create. May be 0 to use default # of buffers.
ucAbortChar (UCHAR)
Character to send before the abort string. ucAbortChar can be used for devices that need to be sent a number of bytes before the abort string can be sent. ulAbortChar is sent first during the abort sequence.
ulAbortCharCount (ULONG)
Number of ucAbortChars to send to the device.
ulAbortBufferSize (ULONG)
Abort buffer size in bytes.
pbAbortBuffer (PBYTE)
Pointer to buffer to send when aborting a job. Buffer must not be deleted until after #GplThreadEnd. ulAbortBuffer is sent after ulAbortChar.
pszAbortString (PSZ)
Abort job reset command for your specific printer language. May be NULL and no string will be sent. pszAbortString is sent last in the abort sequence.
pszLogAddress (PSZ)
File or port name (for example, LPT1). Used for error reporting. May be NULL.
pszWriteError (PSZ)
Error prompt string to display in a SplMsgBox (for example, "Unable to write to printer"). May be NULL.
pszDeviceDescription (PSZ)
Device description to display in a SplMsgBox ( for example, "Laserprinter"). May be NULL.
lDCType (LONG)
DC type (OD_QUEUED, OD_DIRECT).
Returns
Success: TRUE
Failure: FALSE
Notes
ulAbortChar is sent first during the abort sequence.
ulAbortBuffer is sent after ulAbortChar.
pszAbortString is sent last in the abort sequence.

GplThreadOutput

Description

This function is used to pass data to a thread instance (referenced by the thread instance handle passed in) for output. #GplThreadStart must be called prior to calling GplThreadOutput.

Format

BOOL APIENTRY GplThreadOutput( HTHREAD hThread,
PBYTE pBytes,
ULONG ulCount,
ULONG fulDataType );

Parameters
hThread (HTHREAD)
Thread instance handle.
pBytes (PBYTE)
Pointer to data to buffer.
ulCount (ULONG)
Size of data (byte size) pointed to by pBytes parameter
fulDataType (ULONG)
Data type flag.
THREAD_DT_PRINTERLANGUAGE
Data is printer language commands and only requires an abort string to be sent when aborting during this buffer. See pszAbortString in #GplThreadStart.
THREAD_DT_BINARY
Data is binary (raster) data and requires a series of bytes to be sent to ensure the printer is not in binary transfer mode before the abort string can be sent. See ucAbortChar in GplThreadStart.
Returns
Success: TRUE
Failure: FALSE

GplThreadEnd

Description

This function frees up any resources allocated by the thread instance (semaphores and data buffers). A call to this function implies that any remaining data buffers are flushed before the return of this call. This call is the logical pair to GplThreadCreate, which allocates system resources.

Format
BOOL APIENTRY GplThreadEnd( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadDeleteInstance

Description

This function is used to free thread instance data allocated by a corresponding call to #GplThreadCreateInstance().

Format
BOOL APIENTRY GplThreadDeleteInstance( HTHREAD *phThread );
Parameter
phThread (HTHREAD)
Pointer to the thread instance handle to be freed.
Returns
Success: TRUE
Failure: FALSE

GplThreadAbortDoc

Description

This function is called to abort output from the thread that is referenced by the thread instance handle that was passed in. After output is stopped, the function will attempt to restore the printer to a known state where it can process data from another print job.

The first issue to consider when using this function is that the device may be executing a command that has a large amount of data that it is expecting to receive as a block. Therefore the parameter ulAbortChar passed in on #GplThreadStart is sent ulAbortCharCount times.

The second issue to consider is that the abort string 'pszAbortString' provided in #GplThreadStart() is sent last.

Format
BOOL APIENTRY GplThreadAbortDoc( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadResetAbortDoc

Description

This function is used to resolve any previous calls to #GplThreadAbortDoc() so that another job can start printing using the same thread instance handle.

This function will wait, if necessary, for the abort processing started by #GplThreadAbortDoc to be completed before returning so that the thread instance can be put into a state to receive data from a new print job.

Format
BOOL APIENTRY GplThreadResetAbortDoc( HTHREAD  hThread );
Parameter
hThread (HTHREAD)
Handle to thread instance.
Returns
Success: TRUE
Failure: FALSE

GplThreadFlushBuffer

Description

This function causes all data buffers currently being managed by the thread instance to be flushed (written) to their final output destination (for example, parallel port and spool file).

This call allows a flag to be set so that all the data buffers are sent (flushed) before this call returns (fwait == TRUE) or synchronously (fwait = = FALSE).

Format
BOOL APIENTRY GplThreadFlushBuffer( HTHREAD  hThread,
                                    BOOL     fWait );
Parameters
hThread (HTHREAD)
Handle to thread instance.
fWait (BOOL)
Flag indicating flush asynchronously or synchronously.
Returns
Success: TRUE
Failure: FALSE

GplThreadHookDeviceWrites

Description

This is an optional function. This call was created because a class of devices added to the Omni driver did not set status lines according to known standard protocols. Calling this function allows the Omni driver to interpret the status lines because it knows about the devices' special protocols.

Format

BOOL APIENTRY GplThreadHookDeviceWrites( HTHREAD hThread,
PFN PfnPrtWrite,
pddc );

Parameters
hThread (HTHREAD)
Handle to thread instance.
pfnPrtWrite (PFN)
Our function.
pddc
Parameter to our function.
Returns
Success: TRUE
Failure: FALSE

Implementation

After the memory-management package is implemented, the next logical package that can be added to a driver is the GenPLib Output-Thread Management package.

The best place to implement threads in a driver is when sending and buffering data to what is more than likely a slower output device (or file) . This allows the driver to return back to the application and PM GRE for rendering the next page immediately to improve system response.

The benefits of adding multiple threads are proven in tests performed on OS/2 printer drivers that have added the GenPLib thread-management package. Results have shown a 7%-12% performance increase.

A printer driver retrieves a thread handle (HTHREAD) from GenPLib by calling #GplThreadCreateInstance during the FILL_PHYSICAL enable subfunction . This call is made only if the DC type is OD_QUEUED or OD_DIRECT (the DC types that request output). This call does not start a thread at this time but causes the GenPLib thread code to initialize its internal structures with information you supply. Once again, a corresponding call to #GplThreadDeleteInstance is made during the driver's disable subfunction DISABLE_PHYSICAL to free the thread handle.

The actual thread is started in the driver when the start-document escape code is received from the application (DEVESC_STARTDOC from the DevEscape() device call) using #GplThreadStart. The #GplThreadEnd call that stops the thread is placed in disable subfunction BEGIN_CLOSE_DC by the driver. It is not placed in end-document (DEVESC_ENDDOC) code because some applications choose to call many DEVESC_STARTDOC/DEVESC_ENDDOC pairs for the same DC. Placing the call in a disable subfunction assures that a thread will not start and stop many times in a row. The side effect of this is that the GenPLib thread code will receive multiple #GplThreadStart calls for the same thread. In this case, subsequent GplThreadStart calls cause an implied flush of all data received at that time for the thread.

If you call #GplThreadStart multiple times, the thread code accepts any changed information for input parameters to this call. This allows the driver to dynamically change the buffer size and the maximum number of buffers used by the thread code.

The #GplThreadAbortDoc call is also added to the driver's escape code processing routine for the abort-document call (DEVESC_ABORTDOC). This call the thread code to clear data out of all its buffers and to ignore any other data received by the thread until a #GplThreadResetAbortDoc call is made to the thread. As soon as all data is flushed by #GplThreadAbortDoc, the abort sequences supplied during #GplThreadStart will place the printer in a known state for the next print job.

#GplThreadResetAbortDoc tells the thread code that the job has ended, that no more data will be sent for this print job, and to resume sending data. This call is added during the end-document escape processing (DEVESC_ENDDOC) in the driver. This allows applications that perform multiple DEVESC_ STARTDOC/DEVESC_ENDDOC calls for a single DC (that is, submitting multiple print jobs using one DC) to function normally.

#GplThreadOutput is used to add any data to the thread's output buffers. The call has been added for the DEVESC_RAWDATA escape code. This escape code is sent to a driver to pass through raw data received from DOS or Windows applications along with a data buffer and data count. The call is also implemented wherever the driver needs to send out its own escape codes for graphical or text generating output. This data includes printer/job initialization commands, raster image data (compressed or uncompressed), font/text data, and job/printer termination commands.

The last function in the thread-management package is #GplThreadFlushBuffer, which tells the thread code to send all data currently held in the thread's buffers before the call returns. This call appears just after #GplThreadResetAbortDoc in a driver's DEVESC_ENDDOC processing code.

Example Code

Starting a Thread With Output to a Printer

Following is an example of starting a thread with output to a printer:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // If going to a file or direct to a printer
    if( OD_DIRECT == ulDCType )
    {
      // Open output port (printer-driver version of DosOpen)
      rc  =  PrtOpen( hFile, pszLogAddress );

      // Create/Update the second print thread
      rc = GplThreadStart( hThread,                 // Instance handle
                           0,                       // Handle to spooler
                           hFile,                   // Handle to device
                           0,                       // Default buffer size
                           0,                       // Default number of buffers
                           '\0',                    // Abort character
                           1024,                    // Abort character repeat count
                           sizeof( pszCmdAbort );   // Length of abort command
                           pszCmdAbort,             // Our abort command
                           pszLogAddress,           // For example, LPT1
                           "Error",                 // Error string if write fails
                           pszDeviceName,           // For example, "Epson LQ2550"
                           ulDCType );              // DC type
    } /* end if */
  } /* end case */
Starting a Thread With Output Queuing "Raw" Data

Following is an example of starting a thread with output queuing "raw" printer escapes:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_RAW )
      {
        if( !hspl )
        {
          // Open appropriate queue (from DevOpenData) for output
          hspl  =  SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );
          SplQmStartDoc( hspl, szDocumentName );

        } /* end if */

        // Create/Update the second print thread
        rc = GplThreadStart( hThread,                 // Instance handle
                             hspl,                    // Handle to spooler
                             0,                       // Handle to device
                             0,                       // Default buffer size
                             0,                       // Default number of buffers
                             '\0',                    // Abort character
                             1024,                    // Abort character repeat count
                             sizeof( pszCmdAbort );   // Length of abort command
                             pszCmdAbort,             // Our abort command
                             pszLogAddress,           // For example, LPT1
                             "Error",                 // Error string if write fails
                             pszDeviceName,           // For example, "Epson LQ2550"
                             ulDCType );                // DC Type
      } /* end if */
    } /* end if */
  } /* end case */
Starting a Thread With Output Queuing "Standard" Data

Following is an example of starting a thread with output queuing "standard" data:

  case DEVESC_STARTDOC:                           // Escape code signals start of document
  {
    // Else DC type is queued, not direct to a printer
    elseif( OD_QUEUED == ulDCType )
    {
      if( ulDataType == PM_Q_STD )
      {
        SplStdStart( pddc->pdb->hdc );

        // Open appropriate queue (from DevOpenData) for output
        hspl = SplQmOpen( "*", 9, (PQMOPENDATA)&DevOpenData );

        // Signal start of document (no thread is started by Omni driver)
        SplQmStartDoc( hspl, szDocumentName );

      } /* end if */
    } /* end if */
  } /* end case */
GplThreadCreateInstance and GplThreadDeleteInstance Usage

The enable and disable subfunctions FILL_PHYSICAL and DISABLE_PHYSICAL are ideal points at which to create and delete a thread instance. Following is an example of #GplThreadCreateInstance() and #GplThreadDeleteInstance() usage :

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                                 // Determine enable subfunction
  {
      case FILL_PHYSICAL:                             // An enable subfunction
      {
        // Only create a thread if we expect to output data
        if( (OD_QUEUED == pdb->ulType )  ||  (OD_DIRECT == pdb->ulType ) )
        {
          // Create an output thread instance (do not start thread yet)
          bRC = GplThreadCreateInstance( hmcbHeap, globals.hmod, &hThread, pddc );

        } /* end if */
      } /* end case */
      break;

      case DISABLE_PHYSICAL:                          // A disable subfunction
      {
        // Delete the output thread instance if allocated
        if( hThread )
        {
          bRC = GplThreadDeleteInstance( &hThread );

        } /* end if */
      } /* end case */
      break;

  } /* end switch */
} /* end OS2_PM_DRV_ENABLE */
GplThreadOutput Usage

Following is an example of #GplThreadOutput() usage:

  case DEVESC_NEWFRAME:                            // Page eject requested
  {
     CHAR achRasterCmd[] = "*Esc*r1Q";

     // When getting a DEVESC_NEWFRAME or DEVESC_ENDDOC,
     // The current page's raster image can be retrieved from the
     // device surface and transferred to the printer

     // Enter raster mode by sending down escape code for current device
     bRC = GplThreadOutput( hThread,                 // Thread instance handle
                            achRasterCmd,            // Pointer to data buffer
                            strlen(achRasterCmd),    // Size of data buffer
                            THREAD_DT_BINARY );      // Data type

     // Dither and/or compress raster data and send to output thread
     bRC = GplThreadOutput( hThread,
                            pbRasterData,
                            ulRasterDataLength,
                            THREAD_DT_BINARY );

  } /* end case */
GplThreadEnd Usage

Following is an example of #GplThreadEnd usage during OS2_PM_DRV_ENABLE:

ULONG APIENTRY OS2_PM_DRV_ENABLE( ULONG ulSubFunc, ULONG p1, ULONG p2 )
{
  switch( ulSubFunc )                              // Determine enable subfunction
  {
    case BEGIN_CLOSE_DC:
    {
      // If we created an output thread
      if( hThread )
      {
        // End   the   second   thread   implied   flush   with   wait 
         bRC   =   GplThreadEnd (   hThread   ) ; 

       }   / *   end   if   * / 
     }   / *   end   case   * / 
   }   / *   end   switch   * / 
}   / *   end   OS2 _ PM _ DRV _ ENABLE   * /
GplThreadAbortDoc Usage

Following is an example of #GplThreadAbortDoc usage for handling an abort- document escape:

  case DEVESC_ABORTDOC:
  {
    // Tell second thread that an abort doc occurred
    GplThreadAbortDoc( hThread );

    // Store a state flag in our pddc to indicate abort is in effect
    pddc->fAbortDocCalled = TRUE;

  } /* end case */
GplThreadResetAbortDoc Usage

Following is an example of using #GplThreadResetAbortDoc:

case DEVESC_ENDDOC:
{
  // If we received a DEVESC_ABORTDOC
  if( pddc->fAbortDocCalled )
  {
    // Notify the output thread that the abort condition is over
    GplThreadResetAbortDoc( hThread );

    // Reset the abort state so thread will begin processing data again
    pddc->fAbortDocCalled = FALSE;

  } /* end if */
} /* end case */
GplThreadFlushBuffer Usage

Following is an example of using #GplThreadFlushBuffer:

case DEVESC_ENDDOC:
{
  // If going to a file or direct to a printer
  if( OD_DIRECT == ulDCType )
  {
    // Flush all data in second thread; we are done
    GplThreadFlushBuffer( hThread, TRUE );

   } /* end if */

   elseif( OD_QUEUED == ulDCType  &&  PM_Q_RAW == ulDataType )
   {
     // Flush all data in second thread; we are done
     GplThreadFlushBuffer( hThread, TRUE );

   } /* end if */
} /* end case */
GplThreadHookDeviceWrites Usage

Following is an example of using #GplThreadHookDeviceWrites:

case DEVESC_STARTDOC:
{
  // We have just called thread Start
  GplThreadStart();

  // We inform the thread that we want to subclass the write of data
  // to the output destination, replacing it with our callback function
  GplThreadHookDeviceWrites( hThread,        // Thread handle
                             pfnPrtWrite,    // Our "write" function
                             pddc );         // Parameter to our "write" function
} /* end case */

Pattern Creation

This module contains functions to create bitmap patterns.

To use the GenPLib Pattern-Creation package, you must include:

INCL_GENPLIB_MEMORY
INCL_GENPLIB_PATTERNS

The following functions are used to create bitmap patterns.

GplPatternCreateBitmaps

Description

This function fills in an array of BMAPINFO structures with the PATSYM patterns. This function is designed to be used to fill in the DEVICESURFACE structure with patterns that match the resolution of the target device. GplPattern functions use the GenPLib Memory-Management package and require that a HMCB had been created with #GplMemoryCreateInstance.

Format
LONG APIENTRY GplPatternCreateBitmaps(
                            HMCB        hMcb,               // Memory instance handle
                            ULONG       ulXResolution,      // X device resolution
                            ULONG       ulYResolution,      // Y device resolution
                            ULONG       ulWidth,            // Desired pattern width
                            ULONG       ulHeight,           // Desired pattern height
                            PBMAPINFO   paBmapInfo,         // Array of pattern bitmaps (output)
                            LONG        lCount,             // Number of bitmaps to create
                            ULONG       ulFlip );           // Flip bitmaps horiz/vertically
Parameters
hMcb (HMCB)
Handle to initialized memory control block.
ulXResolution (ULONG)
Target device x resolution.
ulYResolution (ULONG)
Target device y resolution.
ulWidth (ULONG)
Size of bitmap X.
ulHeigh (ULONG)
Size of bitmap Y.
paBmapInfo (PBMAPINFO)
Pointer to an array of BMAPINFO.
lCount (LONG)
Number of bitmap information structures.
ulFlip (ULONG)
Bit vector. If GPLPAT_INVERT_BITS is set, the bitmaps are inverted.
Returns
TRUE: Success
FALSE Failure

GplPatternDeleteBitmaps

Description

This function deletes the bits and clears the array of BMAPINFO.

Format
LONG APIENTRY GplPatternDeleteBitmaps(
                             HMCB       hMcb,            // Memory instance handle
                             PBMAPINFO  paBmapInfo,      // Pointer to bitmaps to be freed
                             LONG       lCount );        // Count of bitmaps to be freed
Parameters
hMcb (HMCB)
Handle to initialized memory control block.
paBmapInfo (PBMAPINFO)
Pointer to an array of BMAPINFO.
lCount (LONG)
Number of bitmap information structures.
Returns
TRUE: Success
FALSE: Failure

GplPatternCreate

Description

This function can be used by a presentation driver to create a bitmap representation of any one of the predefined OS/2 patterns. Typically, it is called to create all or a subset of patterns a device driver will support at the start of a print job.

Format

LONG APIENTRY GplPatternCreate( HMCB hMcb, // Memory instance handle
ULONG ulXResolution, // X device resolution
ULONG ulYResolution, // Y device resolution
ULONG ulPatSym, // OS/2 pattern define
PBMAPINFO pBmapInfo ); // A pattern bitmap (output)

Parameters
hMcb (HMCB)
Handle to initialized Memory control block.
ulXResolution (ULONG)
Target device x resolution.
ulYResolution (ULONG)
Target device y resolution.
ulPatSym (ULONG)
Pattern number as defined for OS/2 in PMDDI.H:
PATSYM_DEFAULT
PATSYM_DENSE1
PATSYM_DENSE2
PATSYM_DENSE3
PATSYM_DENSE4
PATSYM_DENSE5
PATSYM_DENSE6
PATSYM_DENSE7
PATSYM_DENSE8
PATSYM_VERT
PATSYM_HORIZ
PATSYM_DIAG1
PATSYM_DIAG2
PATSYM_DIAG3
PATSYM_DIAG4
PATSYM_NOSHADE
PATSYM_SOLID
PATSYM_BLANK
PATSYM_HALFTONE
PATSYM_HATCH
PATSYM_DIAGHATCH
pBmapInfo (PBMAPINFO)
Number of bitmap information structures.
Returns
TRUE: Success
FALSE Failure

Example Code

GplPatternCreateBitmaps Usage

Following is an example of using #GplPatternCreateBitmaps:

// PM Graphics Rendering Engine v2.2 Device Surface Function from the Omni driver

LONG APIENTRY QueryDeviceSurface( PDDC pddc, PVOID pv )
{
  // Initialize all fields of the device surface structure, then...
  if( !pddc->pdb->bAllocatedPatterns )
  {
      if( ORIENTATION_PORTRAIT == pdb->pJobProperties->ulOrientation )
      {
        if( GplPatternCreateBitmaps ( pddc->hmcbHeap,
                                      pddc->pResInfo->ulXRes,
                                      pddc->pResInfo->ulYRes,
                                      64L,
                                      64L,
                                      (PBMAPINFO)&pds->abmapinfoDefPattern,
                                      DEFAULT_PATTERNS_NUMBER,
                                      0L ) )
        {
          pddc->pdb->bAllocatedPatterns = TRUE;

        } /* end if */
      } /* end if */
  } /* end if */
} /* end QueryDeviceSurface */
GplPatternDeleteBitmaps Usage

Following is an example of using #GplPatternDeleteBitmaps:

case BEGIN_CLOSE_DC:

  // Perform other disable processing, then near end of case...
  if( GRE_22 <= globals.ulGreVersion )  // PM Graphics Rendering Engine v2.2 or higher
  {
    PDEVICESURFACE pds;

    // Note:  We only allocate patterns in QueryDeviceSurface for GRE v2.2 and higher
    pds = (PDEVICESURFACE)pddc->pdb->pDeviceSurface;
    if( GplPatternDeleteBitmaps( pddc->pdb->hmcbHeap,
                                 (PBMAPINFO)&pds->abmapinfoDefPattern,
                                 DEFAULT_PATTERNS_NUMBER ) )
    {
       pddc->pdb->bAllocatedPatterns = FALSE;

    } /* end if */
  } /* end if */

Semaphores

Printer drivers are responsible for serializing threads' access to any of their device contexts (DCs). OS/2 mutual exclusion (MUTEX) semaphores are a possible choice to implement this serialization. When two threads attempt to use the same printer DC at the same time, the second thread should receive a failing result code and PMERR_HDC_BUSY from WinGetLastError.

Given this behavior of OS/2 and its presentation drivers, most OS/2 applications use just one thread when printing. Whenever there is just one thread, there is little contention for the DC; but presentation drivers must safely manage this contention.

There is some performance overhead to consider whenever a thread calls into the OS/2 kernel, as is the case for calls to DosRequest/ReleaseMutexSem. The overhead occurs because the kernel reloads code and data selectors on kernel entry and exit. OS/2 MUTEX semaphores are always safe semaphores, however, because of this transition to the kernel.

Semaphores in the GenPLib

The semaphore package in the GenPLib implements a Ring-3 RAM semaphore when running on the 486 processor. The 486 has the CMPXCHG instruction that is not present on the 386. This single instruction implements a fast, safe, RAM semaphore at Ring 3.

On a 486, a thread requesting an unowned semaphore will remain at Ring 3 and not enter the kernel. If the semaphore is owned and the thread intends to wait, however, then it will enter the kernel to block on an event semaphore.

On a 386, the GenPLib simply uses OS/2 MUTEX semaphores. Implementing a semaphore on the 386 requires multiple instructions; thus, a switch to the kernel is required to disable interrupts while these multiple instructions execute.

For either processor, a thread may repeatedly request the same semaphore without an intervening release (nested requests). Use #GplSemQueryMutexSem to query the nested use count.

The GenPLib detects the processor type at runtime.

All GenPLib semaphore APIs return 0 on success. When not zero, they return the same result codes documented for their OS/2 MUTEX semaphore counterparts.

The GenPLib semaphore APIs require a pointer to a MTXSEM data structure instead of a semaphore handle like OS/2 MUTEX semaphores. It is the responsibility of the client to allocate one of these structures per semaphore and supply its pointer to the GenPLib semaphore API.

When the MTXSEM semaphore structure resides on shared memory, it may be created with the DC_SEM_SHARED flag. Additional processes may obtain access to this shared memory and open the semaphore.

All GenPLib semaphores are created in the unowned state.

To use the GenPLib Semaphores package, you must include:

INCL_GENPLIB_SEMAPHORES

The following APIs are included in the semaphore package of GenPLib:

With the exception of the first parameter, PMTXSEM, the parameters to the GenPLib semaphore APIs are as documented for OS/2 MUTEX semaphores.

GplSemCreateMutexSem

Description

This call creates a MUTEX semaphore.

Format
APIRET APIENTRY GplSemCreateMutexSem( PMTXSEM  pmtxsem,
                                      ULONG    flAttr )
Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
flAttr (ULONG)
Creation flags. DC_SEM_SHARED is the only flag.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or return code from:
DosQueryMem
DosCreateEventSem
DosCreateMutexSem
Notes

The caller must first allocate memory for the MTXSEM structure. See GPLSEM. H, where this structure is defined. If the memory is allocated from shared memory, the caller may specify the DC_SEM_SHARED flag for flAttr parameter.

GplSemOpenMutexSem

Description

This call opens a shared GPL Mutex semaphore.

Format
APIRET APIENTRY GplSemOpenMutexSem( PMTXSEM  pmtxsem );
Parameter
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
Notes
When multiple processes are using a shared semaphore, then subsequent processes must open the semaphore. It is not necessary for the creating process to perform an open.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or return code from:
DosOpenEventSem
DosOpenMutexSem

GplSemRequestMutexSem

Description

This call is used to request an OS/2 PM MUTEX semaphore.

Format
APIRET APIENTRY GplSemRequestMutexSem( PMTXSEM  pmtxsem,
                                       LONG     lTime );
Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
lTime (LONG)
Time out value in milliseconds.
Returns
Success: NO_ERROR
Failure: Typical error returns for this function include ERROR_INVALID_ PARAMETER or return codes from DosRequestMutexSem.
ERROR_TIMEOUT (RC 640d) means that the semaphore is owned.
Notes

The caller can specify the timeout value for the request. When timeout is 0 , the call returns immediately. When timeout is -1, the call is an indefinite wait. Otherwise the call will block up to the timeout specified.

GplSemReleaseMutexSem

Description

This call will release an owned MUTEX semaphore.

Format
APIRET APIENTRY GplSemReleaseMutexSem( PMTXSEM  pmtxsem );
Parameter
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
ERROR_NOT_OWNER (not the owner of the MUTEX semaphore)
DosReleaseMutexSem
Notes

This call decrements the nested use count for the semaphore. When the nested use count goes to zero, the semaphore is truly released.

GplSemCloseMutexSem

Description

This call closes a GPL MUTEX semaphore for the process.

Format
APIRET APIENTRY GplSemCloseMutexSem( PMTXSEM  pmtxsem );

Parameter:pmtxsem (PMTXSEM)

Address of MTXSEM structure.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
DosCloseEventSem
DosCloseMutexSem
Notes

Every process using GPL semaphores should close each one with this API.

GplSemQueryMutexSem

Description

This call returns information about the semaphore.

Format

APIRET APIENTRY GplSemQueryMutexSem( PMTXSEM pmtxsem,
PID *ppid,
TID *ptid,
PULONG pulCount );

Parameters
pmtxsem (PMTXSEM)
Address of MTXSEM structure.
*ppid (PPID)
Address where process ID is returned.
*ptid (PTID)
Address where thread ID is returned.
pulCount (PULONG)
Request count.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER
or the return codes from:
DosQueryMutexSem
Notes

This API will return information about the semaphore, such as what process and thread currently owns it, and if owned, what is the nested use count.

It is important for asynchronously killed threads to make this call to determine if they own the DC semaphore. If they own the semaphore, they should release it the appropriate number of times before exiting.

Differences Between GenPLib Semaphores and Kernel Semaphores

If a nonzero request time times out, it will timeout to within 30 milliseconds of the supplied time out.

DosQueryMutexSem returns a process and thread ID even if the MUTEX sem is unowned; that is, the count returned is zero. This semaphore package returns process and thread ID of zero when the semaphore is unowned regardless of 386 or 486.

String Management

The string-management package provides string-sorting functionality for printer output devices.

Certain printer output devices can output data only in small bands, yet they are expected to keep track of strings that might be positioned anywhere on a physical output page. These devices need an independent mechanism for keeping those strings in sorted "page" order until the device is actually processing the band of that string.

This is true for raster printers that utilize a print head and must only send text strings that are drawn using device (printer) fonts within a certain range of the print head when printing graphics data. The following diagrams represent this concept:

                        Output Page
                        +----------+
                        |          |
                        |          |
                        |xxxxxxxxxx|
                        |xxxxxxxxxx| <--- Current band of raster output
                        |          |      that can be processed by the
                        |          |      output device.
                        | Hello    |
                        |          |
                        |          | <--- Text is below current band so
                        |    ABCD  |      we can't output it yet
                        |          |
                        |          |
                        |          |
                        +----------+


                        Output Page (later)
                        +----------+
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
                        |xHelloxxxx| <--- Current band of raster output
                        |xxxxxxxxxx|      that can be processed by the
                        |          |      output device.
 Lower string still --> |    ABCD  |      This time the text string is in
 not in range           |          |      the band region and it should be
                        |          |      processed at this time
                        |          |
                        |          |
                        +----------+


                        Output Page (later)
                        +----------+
                        |          |
                        |          |
                        |          |
                        |          |
                        |          |
   String already   --> | Hello    |
   processed            |          |
                        |          |
                        |          |
                        |xxxxABCDxx|   <--- Current band of raster output
                        |xxxxxxxxxx|        that can be processed by the
                        |          |        output device.
                        |          |        This time the second string is in
                        +----------+        the band region and it should be
                                            processed at this time.

The package sorts text strings passed in page order. Top-to-bottom and left -to-right is the current default page order.

Once strings are inserted, you can extract strings in respect to a rectangular area (typically the current graphics band rectangle) that you pass in.

The string-sorting package utilizes the GenPLib Memory-Management and Exception-Management packages, thereby providing very efficient system resource usage and consistent error reporting and logging.

To use the GenPLib String-Management package, you must include:

INCL_GENPLIB_STRING

The following functions comprise the string-management package:

GplStringSorterCreateInstance

Description

This function creates a string sorter instance (memory resource allocated) and returns a handle that must be used by all other string sorter package functions.

The default heap size that is created is based on current printer driver media sizes and application text usage. A parameter is supplied so that the user can increase the initial memory area that is created to hold text strings. Otherwise a default value, based on test averages, will be used.

Format

APIRET APIENTRY GplStringSorterCreateInstance( HDC hdc,
PHSORTER phStrSort,
ULONG ulHeapSize,
ULONG ulOptions );

Parameters
hdc (HDC)
Handle to current DC.
phStrSort (PHSORTER)
Pointer to the location where string-sorter handle will be placed.
ulHeapSize (ULONG)
Initial size of heap to allocate for strings. This can be zero. A default memory area will be created.
ulOptions (ULONG)
Creation options (for example, DBCS and TTY). This parameter is reserved for future use. This parameter must be zero.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterInsertString

Description

This function adds a text string to a string instance. Information, including the bounding rectangle passed in and a pointer to the data, is stored with the string. You can store with the string (for example, a pointer to a structure containing font name, font ID, and color).

Currently, the sort order is determined by the top-left corner of the text box passed in. The strings are sorted in page order. Page order is the order the text would be printed by a banding device on a hardcopy page.

Format

APIRET APIENTRY GplStringSorterInsertString( HSORTER hStrSort,
PSZ pszStr,
RECTL rectlStr,
PVOID pUserDef,
PVOID pUnused );

Parameters
hStrSort (HSORTER)
Handle of the string sorter to be used for insert operation.
pszStr (PSZ)
Pointer to text string to insert.
rectlStr (RECTL)
Bounding rectangle of string being inserted.
pUserDef (PVOID)
Pointer to user-defined data to be stored with this string .
pUnused (PVOID)
NULL. Reserved for future use.
Returns
Success: NO_ERROR
Failure:
ERROR_INVALID_PARAMETER
ERROR_xxx

GplStringSorterQueryNumStrings

Description

This function returns the current number of strings in the string sorter instance (HSORTER) referenced on the call.

Format
APIRET APIENTRY GplStringSorterQueryNumStrings( HSORTER  hStrSort,
                                                PULONG   pulNumStr );
Parameters
hStrSort (HSORTER)
Handle of string sorter instance to be queried.
pulNumStr (PULONG)
Pointer to the location where number is to be returned.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterGetFirstString

Description

This function is used to get a linked list of strings from the string sorter in relation to a band rectangle passed in.

The strings are not deleted from the linked list on this call because we allocated the information and will de-allocate on a #GplStringSorterReset() call or a #GplStringSorterRemoveStrings() call.

Currently, the defined relationships that can be selected are:

Option Description
STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.
Notes
  1. All retrievals are inclusive on all boundaries of the rectlBand passed in.
  2. GplStringSorterGetFirstString retrievals are non-destructive. (the string remains in the string-sorter instance and are only removed by #GplStringSorterRemoveStrings or #GplStringSorterReset calls).
Format

APIRET APIENTRY GplStringSorterGetFirstString( HSORTER hStrSort,
ULONG ulOption,
RECTL rectlBand,
PSZ *ppszStr,
PRECTL prectlStr,
PPVOID ppUserDef );
Parameters
hStrSort (HSORTER)
Handle of string-sorter instance to be used for string retrieval.
ulOption (ULONG)
Option passed in to indicate the relationship to test for on the "Get" operation. See options above.
rectlBand (RECTL)
The band rectangle passed in that will be used to test all text-string rectangles against.
*ppszStr (PSZ)
-output Pointer to first text string meeting retrieve criteria (ulOption).
prectlStr (PRECTL)
-output Pointer to the bounding rectangle of first string retrieved (passed in during #GplStringSorterInsertString).
ppUserDef (PPVOID)
-output Pointer to the user-defined data that was associated with text string retrieved (passed in during #GplStringSorterInsertString).
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterGetNextString

Description

This function is used to retrieve the next string matching the retrieve criteria passed in on the last call to #GplStringSorterGetFirstString(). The criteria is based on the ulOption and rectlBand parameters from that function). This function can be called multiple times until an ERROR_NO_ MORE_ITEMS return code is detected. Each successful call (NO_ERROR) returns another string from the string sorter instance meeting the criteria until no more strings meet the criteria.

Format

APIRET APIENTRY GplStringSorterGetNextString( HSORTER hStrSort
PSZ *ppszStr,
PRECTL prectlStr
PPVOID ppUserDef );

Parameters
hStrSort (HSORTER)
Handle of the string-sorter instance to be used for the retrieve operation.
*ppszStr (PSZ)
- output Pointer to the location that the text string will be retrieved to.
prectlStr (PRECTL)
- output Bounding rectangle of the string being retrieved.
ppUserDef (PPVOID)
- output Pointer to user-defined data stored with string during the insert operation.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterRemoveStrings

Description

This function is used to delete all strings from the string sorter instance , based on a relationship (ulOption) to a rectangle (rectlBand) both passed .

See StringGetFirstStringFromSorter() and StringGetNextStringFromSorter() calls for non-destructive string retrievals.

Remove options include:

Option Description STR_INCL_PART_IN_BAND Return any string whose text box intersects any part of the band passed in.
STR_INCL_ALL_IN_BAND Return any string whose text box is entirely inside the band passed in.
STR_INCL_TOPLEFT_IN_BAND Return any string whose top-left coordinate is inside the band passed in.

Removes are inclusive on all sides of rectlBand passed in.

Format
APIRET APIENTRY GplStringSorterRemoveStrings( HSORTER  hStrSort,
                                              ULONG    ulOption,
                                              RECTL    rectlBand );
Parameters
hStrSort (HSORTER)
Handle of the string sorter to be used for remove operation.
ulOption (ULONG)
Remove options.
rectlBand (RECTL)
Rectangle that is used for comparison by the remove option.
Returns
Success: NO_ERROR
Failure: ERROR_xxx

GplStringSorterReset

Description

This function resets the string sorter instance by removing all text strings inserted into the sorter.

Format
APIRET APIENTRY GplStringSorterReset( HSORTER  hStrSort );
Parameter
hStrSort (HSORTER)
Handle of the string sorter to be reset.
Returns
Success: NO_ERROR
Failure:ERROR_xxx

GplStringSorterDeleteInstance

Description

This function destroys a string sorter instance by freeing any system resources allocated on the corresponding call to #GplStringSorterCreateInstance(). This function implies a #GplStringSorterReset() call.

Format
APIRET APIENTRY GplStringSorterDeleteInstance( HSORTER  hStrSort );
Parameter
hStrSort (HSORTER)
Handle of string sorter to be destroyed. The handle is no longer valid after the call.
Returns
Success: NO_ERROR
Failure: ERROR_INVALID_PARAMETER

Example Code

Retrieving Strings From String Sorter During Band Processing

Following is an example of retrieving strings from string sorter during band processing in a raster printer driver:

 HSORTER hSorter;
 CharString( PDDC pddc, ..., LONG cChars, PCH pchString, ... )
 {
   if( bDeviceFont )                        // If we have a device font to manage
   {
     if( ! hSorter )                        // If we have not yet created a sorter
     {
       // Create a string sorter to handle our device fonts
       apiret = GplStringSorterCreateInstance( pddc,&hSorter, 0L, 0L );

     } /* end if */

     // Calculate text box of string from our character-width tables
     // Insert character string into sorter
     apiret = GplStringSorterInsertString( hSorter,
                                           (PSZ)pchString,
                                           rectlTextBox,
                                           (PVOID)NULL,
                                           (PVOID)NULL );

     // Example: check number of strings currently in sorter
     apiret = GplStringSorterQueryNumStrings( hSorter,&ulNumStr );

   } /* end if */
 } /* end CharString */

 Usage Notes:

   Used default values for heap size and options during 'StringCreate' API (LTR, TTB).

   Used default value for during 'StringInsert' API.

   For 'StringInsert' API, we could have allocated and filled in our own
   structure that helps us process this string (for example, font name, identifier,
   color, etc.).
Removing Strings From a String Sorter During Processing of a Graphics Band

Following is an example of removing strings from a string sorter during processing of a graphics band (banding):

 ProcessBand( PDDC pddc, ...)
 {
   APIRET  apiret;
   RECTL   rectlString, rectlBand;
   PSZ     pszString;

   // Retrieve first sting in the raster band we are processing
   apiret = GplStringSorterGetFirstString(
                hSorter,
                STR_INCL_TOP_LEFT_IN_BAND,
                                 // Retrieve strings whose Top-Left corner are in band
                rectlBand,
                                 // Rectangle of the graphic band we are processing
                &pszString,
                                 // Pointer that first string will be returned in
                &rectlString,
                                 // Text box of the string
                (PVOID)NULL );
                                 // We had no user-defined data to retrieve

   // if we have a string meeting retrieval criteria
   while( apiret != ERROR_NO_MORE_ITEMS )
   {
     // Get next string (uses same retrieve option as "GetFirst")
     apiret = GplStringSorterGetNextString( hSorter,
                                            &pszString,
                                            &rectlString,
                                            (PVOID)NULL );
   } /* end while */

 } /* end ProcessBand */
Removing Strings From a String Sorter After Retrieving Them

Following is an example of removing strings from a string sorter after retrieving them (during graphics band processing):

 ProcessBand( PDDC pddc, ... )
 {
   // After retrieving all strings from band and sending them to printer
   // See the previous example

   // We can remove these strings from the sorter
   // since we do not need to process them again
   apiret = GplStringSorterRemoveStrings( hSorter,
                                          STR_INCL_TOP_LEFT_IN_BAND,
                                          rectlBand );
 } /* end ProcessBand */
Resetting and Destroying the String Sorter During Escape Processing

Following is an example of resetting and destroying the string sorter during escape processing in a printer driver:

 Escape( PDDC pddc, ... )
 {
   APIRET apiret;

   case DEVESC_NEWFRAME:
   {
     // Process all graphics bands on page

     // Reset sorter for next page
     apiret = GplStringSorterReset( hSorter );

    } /* end case */
    break;

    case DEVESC_ENDDOC:
    {
      // Process the last page (implied NewFrame)

      // Sorter no longer needed for this print job
      apiret = GplStringSorterDeleteInstance( hSorter );

    } /* end case */
    break;
 } /* end Escape */