home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
tolkit45.zip
/
os2tk45
/
samples
/
pm
/
print
/
prtobj.c
< prev
next >
Wrap
C/C++ Source or Header
|
1999-05-11
|
50KB
|
1,581 lines
/**************************************************************************
* OS/2 Sample Print Application PRTSAMP
*
* Name: prtobj.c
*
* Description: The object window procedure on thread 2.
*
* Tasks asked of the object window are not bound by 1/10
* second rule. Tasks are given to the object window to
* perform via WM_USER_* messages.
*
* When tasks are completed, the object window posts a
* WM_USER_ACK message to the window that requested the task.
*
* This source file contains the following functions:
*
* threadmain(pv)
* ObjectWinProc(hwnd, msg, mp1, mp2)
* CalculateDrawingArea(pmp, flag, pptlBegin, pptlEnd)
* DrawBorder(hps, pmp, ptlBegin, ptlEnd)
* FixedPointsToTwips(fxPointSize)
* FixedInchesToTwips(fxInch)
* LoadBitmap( pmp, flag )
* MMToTwips(lMM)
* PaintBitmap(pmp, flag)
* PaintDefaultScreen(pmp, flag)
* PaintMetaFile(pmp, flag)
* PaintText(pmp, flag)
* SetClipPath(hps, pmp, ptlBegin, ptlEnd)
* trim(s)
*
* Concepts: second thread, object window
*
* API's: WinInitialize
* WinCreateMsgQueu
* WinRegisterClass
* WinCreateWindow
* WinPostMsg
* WinGetMsg
* WinDispatchMsg
* WinDestroyWindow
* WinDestroyMsgQueue
* WinTerminate
* WinQueryAnchorBlock
* WinQueryWindowULong
* WinSetWindowULong
* DosOpen
* DosQueryFileInfo
* DosRead
* DosClose
* DevOpenDC
* GpiCreatePS
* GpiCreateBitmap
* GpiSetBitmap
* GpiWCBitBlt
* GpiQueryDefaultViewMatrix
* GpiSetDrawControl
* GpiResetBoundaryData
* GpiPlayMetaFile
* GpiScale
* GpiTranslate
* GpiSetViewingTransformMatrix
* GpiResetPS
* GpiSetBitmapBits
* WinInvalidateRect
* GpiLoadMetaFile
* WinSendMsg
* GpiSetDrawingMode
* WinGetLastError
* DosSleep
* GpiErase
* GpiDeleteSegment
* GpiOpenSegment
* GpiSetCurrentPosition
* GpiBox
* GpiSetColor
* GpiCharString
* GpiCloseSegment
* WinQueryWindowRect
* GpiQueryBitmapParameters
* GpiBitBlt
* GpiQueryPS
* GpiSetPS
* WinDefWindowProc
* GpiSetCharSet
* GpiDeleteSetId
* GpiCreateLogFont
* GpiQueryFontMetrics
* GpiSetCharBox
*
* Files : OS2.H, PRTSAMP.H, PRTSDLG.H, PMASSERT.H
*
* Copyright (C) 1991-1993 IBM Corporation
*
* DISCLAIMER OF WARRANTIES. The following [enclosed] code is
* sample code created by IBM Corporation. This sample code is not
* part of any standard or IBM product and is provided to you solely
* for the purpose of assisting you in the development of your
* applications. The code is provided "AS IS", without
* warranty of any kind. IBM shall not be liable for any damages
* arising out of your use of the sample code, even if they have been
* advised of the possibility of such damages. *
*
****************************************************************************/
/* os2 includes */
#define INCL_DEV
#define INCL_DOSFILEMGR
#define INCL_DOSPROCESS
#define INCL_GPIBITMAPS
#define INCL_GPICONTROL
#define INCL_GPICORRELATION
#define INCL_GPIERRORS
#define INCL_GPILCIDS
#define INCL_GPIMETAFILES
#define INCL_GPIPATHS
#define INCL_GPIPRIMITIVES
#define INCL_GPISEGMENTS
#define INCL_GPITRANSFORMS
#define INCL_SPL
#define INCL_SPLDOSPRINT
#define INCL_WINDIALOGS
#define INCL_WINERRORS
#define INCL_WINMLE
#define INCL_WINSTDFILE
#define INCL_WINSTDFONT
#define INCL_WINWINDOWMGR
#include <os2.h>
/* c language includes */
#include <ctype.h>
#include <memory.h>
#include <process.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys\stat.h>
#include <sys\types.h>
/* application includes */
#include "pmassert.h"
#include "prtsamp.h"
#include "prtsdlg.h"
#include "prtshlp.h"
/* local function prototypes */
VOID CalculateDrawingArea( PMAIN_PARM pmp, ULONG flag,
PPOINTL pptlBegin, PPOINTL pptlEnd );
VOID DrawBorder( HPS hps, PMAIN_PARM pmp, POINTL ptlBegin, POINTL ptlEnd );
ULONG LoadBitmap( PMAIN_PARM pmp, ULONG flag );
LONG MMToTwips( LONG lMM );
VOID PaintDefaultScreen( PMAIN_PARM pmp, ULONG flag );
ULONG PaintText( PMAIN_PARM pmp, ULONG flag );
VOID SetClipPath( HPS hps, PMAIN_PARM pmp, POINTL ptlBegin, POINTL ptlEnd );
/*************************************************************************
*
* Name: threadmain
*
* Description: Similar in nature to main(), except this occurs on thread 2.
* Called by _beginthread() in prtcreat.c
*
* API's: WinInitialize
* WinCreateMsgQueu
* WinRegisterClass
* WinCreateWindow
* WinPostMsg
* WinGetMsg
* WinDispatchMsg
* WinDestroyWindow
* WinDestroyMsgQueue
* WinTerminate
*
* Parameters: pv, a pointer to the main block of program parameters
*
* Return: [none]
*
**************************************************************************/
void _Optlink threadmain( void *pv )
{
PMAIN_PARM pmp;
BOOL bOK;
HAB hab;
HMQ hmq;
QMSG qmsg;
/* copy and convert pvoid parmeter to a pointer to the main parameter block */
pmp = (PMAIN_PARM) pv;
/* thread initialization */
hab = WinInitialize( 0 );
hmq = WinCreateMsgQueue( hab, 0 );
/*
* ensure that object window does not receive a WM_QUIT on system shutdown
*/
WinCancelShutdown(hmq, TRUE);
bOK = WinRegisterClass( hab,
OBJECTCLASSNAME,
ObjectWinProc,
0L,
sizeof( PMAIN_PARM ) );
pmassert( hab, bOK );
/*
* create a worker window where the parent is the PM object window,
* it operates on thread 2,
* has no visible windows on the desktop,
* and is not bound by the 1/10 second message processing rule.
*/
pmp->hwndObject = WinCreateWindow(
HWND_OBJECT, /* parent */
OBJECTCLASSNAME, /* class name */
"", /* no caption needed */
0L, /* style */
0L, /* x */
0L, /* y */
0L, /* cx */
0L, /* cy */
HWND_OBJECT, /* owner */
HWND_BOTTOM, /* position (nop) */
0L, /* id (nop) */
(PVOID)pmp, /* parms passed to wm_create case */
(PVOID)NULL); /* presparams */
pmassert( hab, pmp->hwndObject );
/*
* at this point, application has completely initialized
* wm_create processing in the object window procedure has completed.
* let client window know about it
*/
bOK = WinPostMsg( pmp->hwndClient, WM_USER_ACK, (MPARAM)0L, (MPARAM)0L );
pmassert( pmp->hab, bOK );
/* The create processing put values in for next mode and next filename.
Post this message to activate those new settings */
bOK = WinPostMsg( pmp->hwndClient, WM_USER_NEW_MODE, 0, 0 );
pmassert( pmp->hab, bOK );
/*
* dispatch messages; these messages will be mostly user-defined messages
* processed on thread 2
*/
while( WinGetMsg ( hab, &qmsg, (HWND)NULLHANDLE, 0L, 0L ))
{
WinDispatchMsg ( hab, &qmsg );
}
/* Use the new-mode processing to close current file before exiting */
pmp->ulNextMode = MODE_UNKNOWN;
WinPostMsg( pmp->hwndClient, WM_USER_NEW_MODE, 0, 0 );
/* make thread one terminate */
WinPostMsg( pmp->hwndClient, WM_QUIT, (MPARAM)0L, (MPARAM)0L );
/* clean up */
WinDestroyWindow( pmp->hwndObject );
WinDestroyMsgQueue( hmq );
WinTerminate( hab );
/* end this thread */
_endthread();
} /* end of threadmain() */
/**************************************************************************
*
* Name: ObjectWinProc
*
* Description: a window procedure like most others, except it is not
* responsible for any visible windows or presentation. It
* exists to perform lengthy tasks on thread2 of the
* application. WM_* messages appear here in alphabetical order.
*
* API's: GpiLoadMetaFile
* WinDefWindowProc
* WinInvalidateRect
* WinPostMsg
* WinQueryWindowULong
* WinSendMsg
* WinSetWindowULong
* WinQueryAnchorBlock
*
* Parameters: mp1 = window handle to acknowledge upon completion of the task
* (hwndToAck)
* mp2 is an extra parameter and depends on the task
*
* returns: an acknowlegement of completion of the task using WinPostMsg
* if success: WinPostMsg( hwndToAck, WM_USER_ACK, msg, rc );
* return rc;
*
* if not: WinPostMsg( hwndToAck, WM_NACK_* , msg, rc );
* return rc;
*
* where msg was the WM_USER_* message that was posted to the object window
* and rc is a return code. Depending on the hwndToAck, returning
* the result code can be as important as posting it; in particular,
* the object window may send synchronous messages to itself and may
* check the result code via the return code of WinSendMsg()
*
***************************************************************************/
MRESULT EXPENTRY ObjectWinProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
HAB hab;
HWND hwndToAck;
LONG lRC;
PMAIN_PARM pmp;
ULONG nackmsg;
/* store the handle of the window to ack upon task completion; */
hwndToAck = (HWND)mp1;
hab = WinQueryAnchorBlock( hwnd );
pmp = (PMAIN_PARM) WinQueryWindowULong( hwnd, QWL_USER );
switch( msg )
{
case WM_CREATE:
/* mp1 is pointer to main paramters; save it in object window words */
pmp = (PMAIN_PARM)mp1;
WinSetWindowULong( hwnd, QWL_USER, (ULONG) pmp );
/* do more startup processing whilst on thread 2 */
/* Read .ini for filename, mode, printer, and driver data */
GetProfile( pmp );
/* if filename supplied, use it instead */
if (pmp->szArgFilename && *pmp->szArgFilename)
{
ValidateFilename( pmp, pmp->szArgFilename, (HWND)NULLHANDLE);
}
/*
* Try to set the print destination to that which was stored in INI.
* Parm 2 == FALSE means do not display a dialog
*/
QueryPrintQueue( pmp, FALSE );
/*
* Try to set the form to that which was stored in INI.
* Parm 2 == FALSE means do not display a dialog
*/
QueryForm( pmp, FALSE );
return (MRESULT)NULL;
case WM_USER_CLOSE:
/* write settings to ini */
SaveProfile(pmp);
/*
* post a quit to object window;
* when msg loop falls out in threadmain, then post wm_quit to client
*/
WinPostMsg( hwnd, WM_QUIT, 0, 0 );
break;
case WM_USER_LOAD_BITMAP:
/*
* THIS MESSAGE HAS A PARAMETER:
* mp2 is a flag that indicates whether the memory ps for the bitmap
* is to be compatible with the screen or the current printer setup
*/
/* load new bitmap */
nackmsg = LoadBitmap( pmp, (ULONG)mp2 );
if (! nackmsg )
{
WinPostMsg( hwndToAck, nackmsg, (MPARAM)msg, (MPARAM)0L );
}
/* invalidate client area to force a paint */
WinInvalidateRect( pmp->hwndClient, NULL, FALSE );
/* done loading bitmap */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, (MPARAM)0L );
return (MRESULT)TRUE;
case WM_USER_LOAD_METAFILE:
/* load new metafile */
pmp->hmf = GpiLoadMetaFile( hab, pmp->szFilename );
if( GPI_ERROR == pmp->hmf )
{
/* load error */
pmp->hmf = (HMF)NULLHANDLE;
WinPostMsg( hwndToAck, WM_NACK_FILE_READING_ERROR,
(MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
}
/* invalidate client area to force a paint */
WinInvalidateRect( pmp->hwndClient, NULL, FALSE );
/* done loading metafile */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
case WM_USER_LOAD_TEXT:
pmp->f = fopen( pmp->szFilename, "r" );
if( !pmp->f )
{
WinPostMsg( hwndToAck, WM_NACK_FILE_READING_ERROR,
(MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
}
/* has user visited the font dialog? */
if( pmp->fontdlg.fAttrs.usRecordLength == (USHORT)0 )
{
/* no font selected yet; chain to font selection dialog now
* after user selects a font (prtmenu.c), the program invokes
* the pagination routine */
WinPostMsg( pmp->hwndClient, WM_COMMAND, (MPARAM)IDM_SETFONT, (MPARAM)0L);
}
else
{
/* a font is already selected; invoke the pagination routine */
WinSendMsg( hwnd, WM_USER_PAGINATE, (MPARAM)hwnd,
(MPARAM)FLAGS_SCREEN );
}
/* done loading text */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
case WM_USER_PAGINATE:
/* draw text into presentation space */
lRC = PaintText( pmp, (ULONG)mp2 );
/* ack and return with result code */
WinPostMsg( hwndToAck, WM_USER_ACK,(MPARAM)msg,(MPARAM)lRC );
return (MRESULT) lRC;
case WM_USER_PAINT_DEFAULT_SCREEN:
/* draw default screen into presentation space */
PaintDefaultScreen( pmp, FLAGS_SCREEN );
/* ack and return with result code */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, 0 );
return (MRESULT)NULL;
case WM_USER_PAINT_BITMAP:
/* draw bitmap into presentation space */
PaintBitmap( pmp, FLAGS_SCREEN );
/* ack and return with result code */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, 0 );
return (MRESULT)NULL;
case WM_USER_PAINT_METAFILE:
/* Play metafile into client area */
PaintMetaFile( pmp, FLAGS_SCREEN );
/* ack and return with result code */
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
case WM_USER_PRINT:
ProcessUserPrint(hwnd, pmp);
WinPostMsg( hwndToAck, WM_USER_ACK, (MPARAM)msg, (MPARAM)0L );
return (MRESULT)NULL;
}
/* default: */
return WinDefWindowProc( hwnd, msg, mp1, mp2 );
} /* end of ObjectWinProc() */
/*************************************************************************
*
* name: CalculateDrawingArea
*
* Description: Calculate the drawing area of the screen or printer
*
* API's: [none]
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
* PPOINTL pointer to begin point of drawing area
* PPOINTL pointer to end point of drawing area
*
* Return: [none]
*
**************************************************************************/
VOID CalculateDrawingArea( PMAIN_PARM pmp, ULONG flag,
PPOINTL pptlBegin, PPOINTL pptlEnd )
{
/* define drawing area on screen or printer */
pptlBegin->x = FixedInchesToTwips(pmp->form.fxLeftMargin);
pptlBegin->y = FixedInchesToTwips(pmp->form.fxBottomMargin);
pptlEnd->x = pptlBegin->x + pmp->sizelPage.cx;
pptlEnd->y = pptlBegin->y + pmp->sizelPage.cy;
if (flag != FLAGS_SCREEN)
{
/* take hardware clip limits into account */
pptlBegin->x -= MMToTwips( pmp->hcinfo[pmp->iForm].xLeftClip );
pptlBegin->x = max( pptlBegin->x, 0 );
pptlBegin->y -= MMToTwips( pmp->hcinfo[pmp->iForm].yBottomClip );
pptlBegin->y = max( pptlBegin->y, 0 );
pptlEnd->x -= MMToTwips( pmp->hcinfo[pmp->iForm].xLeftClip );
pptlEnd->y -= MMToTwips( pmp->hcinfo[pmp->iForm].yBottomClip );
}
} /* end of CalculateDrawingArea */
/*************************************************************************
*
* name: DrawBorder
*
* Description: Clears drawing area and draws form border
*
* API's: [none]
*
* Parameters: HPS presentation space to use
* PMAIN_PARM pointer to the global data structure
* POINTL pointer to begin point of drawing area
* POINTL pointer to end point of drawing area
*
* Return: [none]
*
**************************************************************************/
VOID DrawBorder( HPS hps, PMAIN_PARM pmp, POINTL ptlBegin, POINTL ptlEnd )
{
BOOL bOK;
ULONG lRC;
HAB hab;
POINTL pointl;
hab = pmp->hab;
/* white out area of presentation space */
bOK = GpiSetColor( hps, CLR_WHITE );
pmassert( hab, bOK );
bOK = GpiSetPattern( hps, PATSYM_SOLID );
pmassert( hab, bOK );
pointl.x = -65535;
pointl.y = -65535;
bOK = GpiSetCurrentPosition( hps, &pointl );
pmassert( hab, bOK );
pointl.x = 65535;
pointl.y = 65535;
lRC = GpiBox( hps, DRO_FILL, &pointl, 0L, 0L );
pmassert( hab, lRC != GPI_ERROR );
/* draw margins of form as a red border */
bOK = GpiBeginPath( hps, 1);
pmassert( hab, bOK );
bOK = GpiSetColor( hps, CLR_RED);
pmassert( hab, bOK );
bOK = GpiSetPattern( hps, PATSYM_DENSE4 );
pmassert( hab, bOK );
pointl.x = 0;
pointl.y = 0;
bOK = GpiSetCurrentPosition( hps, &pointl );
pmassert( hab, bOK );
pointl.x = FixedInchesToTwips(pmp->form.fxxWidth);
pointl.y = FixedInchesToTwips(pmp->form.fxyHeight);
lRC = GpiBox( hps, DRO_OUTLINE, &pointl, 0L, 0L );
pmassert( hab, lRC != GPI_ERROR );
bOK = GpiSetCurrentPosition( hps, &ptlBegin );
pmassert( hab, bOK );
lRC = GpiBox( hps, DRO_OUTLINE, &ptlEnd, 0L, 0L );
pmassert( hab, lRC != GPI_ERROR );
bOK = GpiEndPath( hps );
pmassert( hab, bOK );
bOK = GpiFillPath( hps, 1L, FPATH_ALTERNATE );
pmassert( hab, bOK );
/* draw a black box around the page */
bOK = GpiSetColor( hps, CLR_BLACK);
pmassert( hab, bOK );
pointl.x = 0;
pointl.y = 0;
bOK = GpiSetCurrentPosition( hps, &pointl );
pmassert( hab, bOK );
pointl.x = FixedInchesToTwips(pmp->form.fxxWidth);
pointl.y = FixedInchesToTwips(pmp->form.fxyHeight);
lRC = GpiBox( hps, DRO_OUTLINE, &pointl, 0L, 0L );
pmassert( hab, lRC != GPI_ERROR );
} /* end of DrawBorder */
/*************************************************************************
*
* name: FixedInchesToTwips
*
* Description: converts inches to TWIPS
*
* API's: [none]
*
* Parameters: fxInch = the size in inches as a fixed number
*
* Return: Twips as a ULONG number
*
**************************************************************************/
ULONG FixedInchesToTwips(FIXED fxInch)
{
ULONG integer;
ULONG fraction;
integer = FIXEDINT(fxInch) * 1440L;
fraction = FIXEDFRAC(fxInch);
fraction = (ULONG)(((LONG)fraction) * 1440L / 0x10000);
return (integer+fraction);
} /* end of FixedInchesToTwips() */
/*************************************************************************
*
* name: FixedPointsToTwips
*
* Description: converts a point size
*
* API's: [none]
*
* Parameters: fxPointSize = the point size as a fixed number
*
* Return: Twips as a fixed number
*
**************************************************************************/
FIXED FixedPointsToTwips(FIXED fxPointSize)
{
USHORT integer;
USHORT fraction;
integer = FIXEDINT(fxPointSize);
integer *= 20;
fraction = FIXEDFRAC(fxPointSize);
fraction = (USHORT)(((LONG)fraction) * 20L / 0x10000);
integer += fraction;
return MAKEFIXED(integer, 0);
} /* end of FixedPointsToTwips() */
/*************************************************************************
*
* name: LoadBitmap
*
* Description: loads a bitmap into a screen or printer PS
*
* API's: [none]
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
*
* Return: 0 is sucessful otherwise a NACK message
*
**************************************************************************/
ULONG LoadBitmap( PMAIN_PARM pmp, ULONG flag )
{
FILESTATUS3 filestatus3;
HBITMAP hbm;
HDC hdcCompatible;
HFILE hfile;
PBITMAPFILEHEADER2 pbmpfileheader2;
PBITMAPFILEHEADER2 pbmpfileheader2Work;
PBITMAPINFOHEADER2 pbmp2;
PBYTE pBits;
PBYTE pBitsAligned;
PHBITMAP phbm;
PHDC phdc;
PHPS phps;
SIZEL sizel;
ULONG rc, ulAction, cBytes, cScans;
ULONG cbImageData;
LONG lRC;
HAB hab;
hab = pmp->hab;
/* open bitmap file */
rc = DosOpen( pmp->szFilename,
&hfile,
&ulAction,
0L,
FILE_NORMAL,
FILE_OPEN,
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE,
NULL );
if( rc != 0 )
{
return WM_NACK_FILE_READING_ERROR;
}
/* query file size; returned in filestatus3.cbFile */
rc = DosQueryFileInfo( hfile, FIL_STANDARD,
(PVOID)&filestatus3, sizeof( filestatus3 ));
pmassert( hab, rc == 0 );
/* alloc memory for bitmap file read */
pbmpfileheader2 = (PBITMAPFILEHEADER2) malloc( filestatus3.cbFile );
pmassert( hab, pbmpfileheader2 );
/* read bitmap file into memory */
rc = DosRead( hfile, (PVOID)pbmpfileheader2, filestatus3.cbFile, &cBytes );
if( rc != 0 || cBytes == 0 )
{
free( pbmpfileheader2 );
DosClose( hfile );
return WM_NACK_FILE_READING_ERROR;
}
DosClose( hfile );
switch( pbmpfileheader2->usType )
{
case BFT_BITMAPARRAY:
/* default to the first bitmap in the array */
pbmpfileheader2Work = &((PBITMAPARRAYFILEHEADER2)pbmpfileheader2)->bfh2;
pmassert( hab, pbmpfileheader2Work );
pbmp2 = &pbmpfileheader2Work->bmp2;
cbImageData = filestatus3.cbFile - pbmpfileheader2Work->offBits;
pBits = (PBYTE)pbmpfileheader2 + pbmpfileheader2Work->offBits;
break;
case BFT_BMAP:
/* set pbmp2 to point to the BITMAPINFO2 structure within the file */
pbmp2 = &pbmpfileheader2->bmp2;
cbImageData = filestatus3.cbFile - pbmpfileheader2->offBits;
pBits = (PBYTE)pbmpfileheader2 + pbmpfileheader2->offBits;
break;
case BFT_ICON:
case BFT_POINTER:
case BFT_COLORICON:
case BFT_COLORPOINTER:
/* not supported by print sample */
free( pbmpfileheader2 );
return WM_NACK_BITMAP_NOT_SUPPORTED;
default:
/* invalid file format */
free( pbmpfileheader2 );
return WM_NACK_BITMAP_ERROR;
}
/* pBits should be sensible */
pmassert( hab, pbmp2 );
pmassert( hab, pBits );
/* open a new memory dc with compatibility as required per mp2 argument */
switch( flag )
{
case FLAGS_SCREEN:
hdcCompatible = pmp->hdcClient;
phdc = &pmp->hdcMemory;
phps = &pmp->hpsMemory;
phbm = &pmp->hbm;
break;
case FLAGS_PRINTER:
hdcCompatible = pmp->hdcPrinter;
phdc = &pmp->hdcMem4Printer;
phps = &pmp->hpsMem4Printer;
phbm = &pmp->hbm4Printer;
break;
default:
/* this assert will fail and remind the programmer of a problem */
pmassert( hab, NULL == "expected FLAGS_*" );
}
/* previous uses of hbm, hps, and hdc are properly closed and tidy */
pmassert( hab, *phps == (HPS)NULLHANDLE );
pmassert( hab, *phdc == (HDC)NULLHANDLE );
pmassert( hab, *phbm == (HBITMAP)NULLHANDLE );
*phdc = DevOpenDC(pmp->hab, OD_MEMORY, "*",0, NULL, hdcCompatible);
pmassert( hab, *phdc );
/* create the ps in memory to hold the bitmap */
sizel.cx = 0;
sizel.cy = 0;
*phps = GpiCreatePS( pmp->hab, *phdc, &sizel,
PU_PELS | GPIA_ASSOC | GPIT_NORMAL );
pmassert( hab, *phps );
/* create a bitmap */
*phbm = GpiCreateBitmap( *phps,
pbmp2,
0, /* options */
(PBYTE)NULL, /* init table */
(PBITMAPINFO2)NULL ); /* bitmap info */
/* check that bitmap has been created properly */
if (*phbm == GPI_ERROR)
{
/* error creating bitmap */
GpiAssociate( pmp->hpsMem4Printer, (HDC)NULLHANDLE );
GpiDestroyPS( pmp->hpsMem4Printer );
DevCloseDC( pmp->hdcMem4Printer );
return WM_NACK_BITMAP_ERROR;
}
/* set bitmap into the memory ps */
hbm = GpiSetBitmap( *phps, *phbm );
pmassert( hab, hbm != HBM_ERROR );
/*
* determine if this is a 1.x or 2.x bitmap
* by looking at the length of the BITMAPINFOHEADER structure
*/
if( pbmp2->cbFix == sizeof(BITMAPINFOHEADER))
{
/* this is an OS2 PM 1.x bitmap */
cScans = (ULONG) ((PBITMAPINFOHEADER)pbmp2)->cy;
pmp->cyBitmap = ((PBITMAPINFOHEADER)pbmp2)->cy;
pmp->cxBitmap = ((PBITMAPINFOHEADER)pbmp2)->cx;
pmp->cxBitmapRes = pmp->cyBitmapRes = 0;
}
else
{
/* default to Windows and OS2 2.x PM bitmap */
cScans = pbmp2->cy;
pmp->cyBitmap = pbmp2->cy;
pmp->cxBitmap = pbmp2->cx;
pmp->cxBitmapRes = pbmp2->cxResolution;
pmp->cyBitmapRes = pbmp2->cyResolution;
}
/*
* Some print drivers expect the bit image data to be dword
* aligned. Allocate a new memory object and copy bit data
* to it.
*/
pBitsAligned = (PBYTE) malloc( cbImageData );
pmassert( hab, pBitsAligned );
pmassert( hab, ((ULONG)pBitsAligned & 3 ) == 0 );
memcpy( pBitsAligned, pBits, cbImageData );
/* take bitmap bits from file buffer and place into memory ps for bitmap */
lRC = GpiSetBitmapBits( *phps,
0,
(LONG)cScans,
pBitsAligned,
(PBITMAPINFO2)pbmp2 );
pmassert( hab, lRC != GPI_ERROR );
/* clean up */
free( pBitsAligned );
free( pbmpfileheader2 );
return 0;
} /* end of LoadBitmap */
/*************************************************************************
* Function : MMToTwips
*
* Description: Convert Millimetres to Twips
*
* Parameters : LONG millimetres
*
* API's : [none]
*
* Return : LONG twips
*
*************************************************************************/
LONG MMToTwips( LONG lMM )
{
return ( (LONG)( ((float)lMM) / 25.4 * 1440.0) );
} /* end of MMToTwips */
/*************************************************************************
*
* name: PaintBitmap
*
* Description: Paints a loaded bitmap onto the screen or printer
*
* API's: GpiSetBitmap
* GpiWCBitBlt
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
*
* Return: [none]
*
**************************************************************************/
VOID PaintBitmap( PMAIN_PARM pmp, ULONG flag )
{
HPS hps, hpsBitmap;
HBITMAP hbm, hbmOld;
POINTL aptl[4];
LONG lResult;
double xRatio, yRatio;
POINTL pointlBegin, pointlEnd;
pmassert( pmp->hab, pmp->hbm );
/* define drawing area on screen or printer */
CalculateDrawingArea( pmp, flag, &pointlBegin, &pointlEnd );
/* setup handles for screen or printer */
switch( flag )
{
case FLAGS_SCREEN:
hps = pmp->hpsClient;
hbm = pmp->hbm;
hpsBitmap = pmp->hpsMemory;
/* draw border around bitmap */
DrawBorder( hps, pmp, pointlBegin, pointlEnd );
break;
case FLAGS_PRINTER:
hps = pmp->hpsPrinter;
hbm = pmp->hbm4Printer;
hpsBitmap = pmp->hpsMem4Printer;
break;
default:
pmassert( pmp->hab, NULL == "bad flag in PaintBitmap" );
}
/*
* No clip path in defined for a bitmap as the bitmap is scaled to
* fit the available drawing area
*/
/* setup the source bitblt rectangle */
aptl[2].x = 0;
aptl[2].y = 0;
aptl[3].x = pmp->cxBitmap;
aptl[3].y = pmp->cyBitmap;
/* Set up target bitblt rectangles */
aptl[0].x = pointlBegin.x;
aptl[0].y = pointlBegin.y;
if (pmp->cxBitmapRes && pmp->cyBitmapRes)
{
/*
* Since the intended resolution for the bitmap is known,
* the actual size of the bitmap can be calculated, in
* twips by converting pels/meter to pels/twip
*/
aptl[1].x = ((double)pmp->cxBitmap)/((double)pmp->cxBitmapRes)
* 1000.0 / 25.4 * 1440.0;
aptl[1].y = ((double)pmp->cyBitmap)/((double)pmp->cyBitmapRes)
* 1000.0 / 25.4 * 1440.0;
}
else
{
/*
* Assume the bitmap aspect ratio is 1.0
*/
xRatio = ((double)pmp->sizelPage.cx) / ((double)aptl[3].x);
yRatio = ((double)pmp->sizelPage.cy) / ((double)aptl[3].y);
if (yRatio > xRatio)
{
aptl[1].x = xRatio * (double) (aptl[3].x);
aptl[1].y = xRatio * (double) (aptl[3].y);
}
else
{
aptl[1].x = yRatio * (double) (aptl[3].x);
aptl[1].y = yRatio * (double) (aptl[3].y);
}
}
/* move start position so bitmap is top of the page */
aptl[0].y = pointlEnd.y - aptl[1].y;
/* calculate final target top/right end-point */
aptl[1].x = aptl[0].x + aptl[1].x;
aptl[1].y = aptl[0].y + aptl[1].y;
/* Deselect bitmap from memory */
hbmOld = GpiSetBitmap( hpsBitmap, 0L );
pmassert( pmp->hab, hbmOld != HBM_ERROR );
/* Bitblt the source bitmap onto the printer presentation space */
lResult = GpiWCBitBlt( hps,
hbm,
4L,
(PPOINTL)aptl,
ROP_SRCCOPY,
BBO_IGNORE );
pmassert( pmp->hab, lResult == GPI_OK );
return;
} /* end of PaintBitmap */
/*************************************************************************
*
* name: PaintDefaultScreen
*
* Description: Paints the default screen
*
* API's: [none]
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
*
* Return: [none]
*
**************************************************************************/
VOID PaintDefaultScreen( PMAIN_PARM pmp, ULONG flag )
{
HPS hps;
POINTL pointlBegin, pointlEnd;
/* define drawing area on screen or printer */
CalculateDrawingArea( pmp, flag, &pointlBegin, &pointlEnd );
/* setup handles for screen or printer */
switch( flag )
{
case FLAGS_SCREEN:
hps = pmp->hpsClient;
/* draw border around bitmap */
DrawBorder( hps, pmp, pointlBegin, pointlEnd );
break;
default:
pmassert( pmp->hab, NULL == "bad flag in PaintDefaultScreen" );
}
return;
} /* end of PaintDefaultScreen */
/*************************************************************************
*
* name: PaintMetaFile
*
* Description: Paints a loaded metafile onto the screen or printer
*
* API's: GpiQueryDefaultViewMatrix
* GpiSetDrawControl
* GpiResetBoundaryData
* GpiPlayMetaFile
* GpiScale
* GpiTranslate
* GpiSetViewingTransformMatrix
* GpiResetPS
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
*
* Return: [none]
*
* Reference: Graham C.E. Winn, "OS/2 PM GPI", VNR Computer Library
*
**************************************************************************/
VOID PaintMetaFile( PMAIN_PARM pmp, ULONG flag )
{
HAB hab;
HPS hps;
LONG lRC;
BOOL bOK;
POINTL pointlBegin, pointlEnd, ptl;
BYTE abDesc[ LEN_PLAYMETAFILEDESCS ];
LONG alOptions[ LEN_PLAYMETAFILEOPTS ];
LONG lCount;
RECTL rclBoundary;
FIXED afxScale[2];
MATRIXLF matlfXform, matlfOld, matlfModel;
static MATRIXLF matlfIdentity = {0x10000,0,0,0,0x10000,0,0L,0L,1L };
pmassert( pmp->hab, pmp->hmf );
hab = pmp->hab;
/* define drawing area on screen or printer */
CalculateDrawingArea( pmp, flag, &pointlBegin, &pointlEnd );
/* setup handles for screen or printer */
switch( flag )
{
case FLAGS_SCREEN:
hps = pmp->hpsClient;
/* draw border around bitmap */
DrawBorder( hps, pmp, pointlBegin, pointlEnd );
break;
case FLAGS_PRINTER:
hps = pmp->hpsPrinter;
break;
default:
pmassert( hab, NULL == "bad flag in PaintMetaFile" );
}
/*
* No clip path in defined for a bitmap as the bitmap is scaled to
* fit the available drawing area
*/
/* query the default viewing transform */
bOK = GpiQueryDefaultViewMatrix( hps, 9, &matlfOld );
pmassert( hab, bOK );
/* set metafile play options */
memset( alOptions, 0, sizeof( alOptions ));
alOptions[ PMF_SEGBASE ] = 0;
alOptions[ PMF_LOADTYPE ] = LT_NOMODIFY;
alOptions[ PMF_RESOLVE ] = 0;
alOptions[ PMF_LCIDS ] = LC_LOADDISC;
alOptions[ PMF_RESET ] = RES_NORESET;
alOptions[ PMF_SUPPRESS ] = SUP_NOSUPPRESS;
alOptions[ PMF_COLORTABLES ] = CTAB_REPLACE;
alOptions[ PMF_COLORREALIZABLE ] = CREA_NOREALIZE;
alOptions[ PMF_DEFAULTS ] = DDEF_LOADDISC;
/* find out the metafile bounding rectangle */
bOK = GpiSetDrawControl( hps, DCTL_DISPLAY, DCTL_OFF );
pmassert( hab, bOK );
bOK = GpiSetDrawControl( hps, DCTL_BOUNDARY, DCTL_ON );
pmassert( hab, bOK );
bOK = GpiResetBoundaryData( hps );
pmassert( hab, bOK );
lRC = GpiPlayMetaFile( hps,
pmp->hmf,
LEN_PLAYMETAFILEOPTS,
alOptions,
&lCount,
LEN_PLAYMETAFILEDESCS,
abDesc );
pmassert( hab, lRC != GPI_ERROR );
bOK = GpiQueryBoundaryData( hps, &rclBoundary );
pmassert( hab, bOK );
/* set draw mode */
bOK = GpiSetDrawControl( hps, DCTL_DISPLAY, DCTL_ON );
pmassert( hab, bOK );
bOK = GpiSetDrawControl( hps, DCTL_BOUNDARY, DCTL_OFF);
pmassert( hab, bOK );
/*
* Determine scale parameters to scale from boundary dimensions to target
* dimensions about bottom left of boundary. Ensure that both scale
* factors are equal and set to the smaller of the two possible values
* (to preserve the aspect ratio of the metafile).
*/
if (rclBoundary.xRight > rclBoundary.xLeft)
{
afxScale[0] = ((double)pmp->sizelPage.cx) * 0x10000 /
((double)(rclBoundary.xRight - rclBoundary.xLeft));
}
else
{
afxScale[0] = 0x10000;
}
if (rclBoundary.yTop > rclBoundary.yBottom)
{
afxScale[1] = ((double)pmp->sizelPage.cy) * 0x10000 /
((double)(rclBoundary.yTop - rclBoundary.yBottom));
}
else
{
afxScale[1] = 0x10000;
}
if (afxScale[0] < afxScale[1])
{
afxScale[1] = afxScale[0];
}
else
{
afxScale[0] = afxScale[1];
}
/* scale metafile around boundary bottom,left corner */
ptl.x = rclBoundary.xLeft;
ptl.y = rclBoundary.yBottom;
bOK = GpiScale( hps, &matlfXform, TRANSFORM_REPLACE, afxScale, &ptl );
pmassert( hab, bOK );
/* translate metafile to correct position */
ptl.x = pointlBegin.x - rclBoundary.xLeft;
/* move start position so metafile is top of the page */
ptl.y = pointlEnd.y - rclBoundary.yBottom -
(LONG)( ((double)(afxScale[1])) / 65536.0 *
((double)(rclBoundary.yTop - rclBoundary.yBottom)) );
bOK = GpiTranslate( hps, &matlfXform, TRANSFORM_ADD, &ptl );
pmassert( hab, bOK );
/* set default view transform to scale metafile to PS */
bOK = GpiSetViewingTransformMatrix( hps, 9, &matlfXform,
TRANSFORM_REPLACE);
pmassert( hab, bOK );
/* draw the metafile */
alOptions[PMF_LCIDS] = LC_NOLOAD;
alOptions[PMF_DEFAULTS] = DDEF_IGNORE;
lRC = GpiPlayMetaFile( hps,
pmp->hmf,
LEN_PLAYMETAFILEOPTS,
alOptions,
&lCount, /* segment count */
LEN_PLAYMETAFILEDESCS,
abDesc );
pmassert( hab, lRC != GPI_ERROR );
if (flag == FLAGS_SCREEN)
{
bOK = GpiResetPS( hps, GRES_ALL );
pmassert( pmp->hab, bOK );
}
/* restore the default viewing transform */
bOK = GpiSetDefaultViewMatrix( hps, 9, &matlfOld, TRANSFORM_REPLACE);
pmassert( hab, bOK );
return;
} /* end of PaintMetaFile() */
/*************************************************************************
*
* name: PaintText
*
* Description: Paints the text to the screen or printer
*
* API's: [none]
*
* Parameters: PMAIN_PARM pointer to the global data structure
* ULONG flag to indicate screen or printer
*
* Return: EOF flag PAGINATE_EOF end of file, no text drawn
* PAGINATE_NOT_EOF not end of file, text drawn
* PAGINATE_EOF_PART_PAGE end of file, text drawn
*
**************************************************************************/
ULONG PaintText( PMAIN_PARM pmp, ULONG flag )
{
BOOL bOK;
CHAR szWork[ LEN_WORKSTRING ];
FONTMETRICS fm;
HAB hab;
HPS hps;
LONG lCharHeight, lLeading, cLinesOnPage;
LONG lRC;
POINTL pointl;
POINTL pointlBegin, pointlEnd;
PSZ pszOK;
SIZEF sizef;
hab = pmp->hab;
pmassert( hab, pmp->f );
pmassert( hab, pmp->ulMode == MODE_TEXT );
/* check for end of file */
if ( !fgets( szWork, LEN_WORKSTRING, pmp->f) )
{
if (flag == FLAGS_SCREEN)
{
/* for screen reset back the beginning and read first line */
fseek( pmp->f, 0, SEEK_SET );
fgets( szWork, LEN_WORKSTRING, pmp->f);
}
else
{
/* for printer, return PAGINATE_EOF */
return PAGINATE_EOF;
}
}
/* define drawing area on screen or printer */
CalculateDrawingArea( pmp, flag, &pointlBegin, &pointlEnd );
/* prepare to draw on screen or printer */
switch( flag )
{
case FLAGS_SCREEN:
hps = pmp->hpsClient;
/*
* Paginate the text into a retained segment. The text drawing
* orders can then be played back at WM_PAINT time with a call
* to GpiDrawChain.
*/
bOK = GpiSetDrawingMode( hps, DM_RETAIN );
pmassert( hab, bOK );
GpiErase( hps );
/* delete any previous segment; segment 1 is all we use */
GpiDeleteSegment( hps, 1 );
/* open a new segment for the text using segment ID 1 */
bOK = GpiOpenSegment( hps, 1 );
pmassert( hab, bOK );
/* clear PS and draw form border */
DrawBorder( hps, pmp, pointlBegin, pointlEnd );
break;
case FLAGS_PRINTER:
hps = pmp->hpsPrinter;
break;
case FLAGS_INFO:
hps = pmp->hpsPrinterInfo;
break;
default:
pmassert( hab, NULL == "bad flag in WM_USER_PAGINATE" );
}
pmassert( hab, hps );
/*
* Set clipping path to page size defined by user.
* If user picks a font too big, he'll know this visually.
*/
SetClipPath( hps, pmp, pointlBegin, pointlEnd );
/* use black color for text */
bOK = GpiSetColor( hps, CLR_BLACK );
pmassert( hab, bOK );
/* Discard LCID (logical character identifier) 1 if it exists. */
if( GpiSetCharSet( hps, 1 ))
{
/* select font 0 and delete font 1 */
GpiSetCharSet(hps, 0 );
GpiDeleteSetId(hps, 1 );
}
/* select default font 0 */
GpiSetCharSet(hps, 0 );
/*
* Create the logical font with fAttrs from the font dialog.
*
* This version of the print sample only requests vector (outline)
* fonts of the font dialog. (Reference: PRTCREAT.C, the
* initialization of the pmp->fontdlg structure.)
*
* Create and select the requested font into the PS and query its
* font metrics.
*/
lRC = GpiCreateLogFont( hps, NULL, 1, &pmp->fontdlg.fAttrs );
pmassert(hab, lRC != GPI_ERROR );
if( lRC == FONT_DEFAULT )
{
/*
* This can happen on Postscript device fonts which are outline fonts,
* and also LaserJet PCL 5 scalable printer device fonts. These
* have to be emulated on the screen. Give a warning to user that
* some default font is in use, and carry on.
*
* Only warn the user once after each visit to the font dialog.
* Don't re-enable the client yet. That message is still to come.
*/
if( pmp->fVisitedFontDialog )
{
/* only one warning per font dialog visit */
pmp->fVisitedFontDialog = FALSE;
bOK = WinLoadString( pmp->hab, (HMODULE)0,
ERRMSG_DEFAULT_FONT, LEN_WORKSTRING, szWork );
pmassert( pmp->hab, bOK );
WinMessageBox( HWND_DESKTOP,
pmp->hwndFrame,
szWork,
pmp->pszTitle,
(USHORT)0,
MB_OK | MB_MOVEABLE | MB_CUACRITICAL | MB_APPLMODAL);
}
}
bOK = GpiSetCharSet( hps, 1 );
pmassert( hab, bOK );
bOK = GpiQueryFontMetrics(hps, sizeof(FONTMETRICS), &fm );
pmassert(hab, bOK);
/*
* GpiSetCharBox
*
* "Char box" the font to the point size desired by the user.
* When sizef.cx == sizef.cy, the result is a normally-proportioned font.
* Setting sizef.cx to one-half of sizef.cy yields a compressed font,
* and setting sizef.cx to twice sizef.cy yields an expanded font.
* Experiment with these settings; this is one of the most flexible
* aspects of the Adobe font rasterizing technology in the OS2 graphics
* engine. It is true WYSIWYG and very device independent.
*
* Although not demonstrated in this version of the print sample,
* GpiSetCharBox will play an important role in emulating printer device
* fonts on the screen device for WYSIWYG output.
*/
sizef.cy = sizef.cx = FixedPointsToTwips( pmp->fontdlg.fxPointSize );
bOK = GpiSetCharBox(hps, &sizef);
pmassert(hab, bOK);
/*
* Set Line Space
*
* Here are three methods for determining the vertical
* line spacing (also known as line increment or leading)
* between lines of text.
*
* 1) Add two values from the fontmetrics structure:
* lMaxBaselineExt + lExternalLeading.
*
* 2) Use fontmetrics values lEmHeight * 1.20 + lExternalLeading
* lExternalLeading is zero for PM fonts.
*
* 3) The typesetters' rule of thumb: lead is pointsize plus two points.
*
* Method 1 and 2 require A) a re-read of the font metrics after the call
* to GpiSetCharBox because those metrics will change as a result of
* GpiSetCharBox, or B) some computation given the current font metrics.
* Method 3 is simplest because the only variable is point size, and that
* was specified by the user.
*
* 1 point = 20 twips.
*/
lCharHeight = 20 * FIXEDINT( pmp->fontdlg.fxPointSize ) +
20 * FIXEDFRAC( pmp->fontdlg.fxPointSize ) / 0x10000;
lLeading = lCharHeight + 40;
/* compute lines on page (leave one line at the bottom for descenders) */
cLinesOnPage = (pmp->sizelPage.cy / lLeading) - 1;
/* start at the upper left corner of page */
pointl.x = pointlBegin.x;
pointl.y = pointlEnd.y - lLeading;
/*
* Read from text file at current file position and draw as much of it
* as will fit on the page.
*/
do
{
/* remove trailing white space */
trim( szWork );
/* move to the spot on the page */
bOK = GpiSetCurrentPosition( hps, &pointl );
pmassert( hab, bOK );
lRC = GpiCharString( hps, strlen( szWork ), szWork );
pmassert( hab, lRC != GPI_ERROR );
/* move down the page */
pointl.y -= lLeading;
/* have reached bottom of page? */
if( --cLinesOnPage == 0 )
{
/* yes, bottom of page; break out of this while loop */
break;
}
pszOK = fgets( szWork, LEN_WORKSTRING, pmp->f );
} while( pszOK );
/* reset the clip path */
bOK = GpiSetClipPath( hps, 0L, SCP_RESET );
pmassert( pmp->hab, bOK );
if( flag == FLAGS_SCREEN )
{
/* close the drawing segment; only the screen uses retained segments */
bOK = GpiCloseSegment( hps );
pmassert( hab, bOK );
bOK = GpiSetDrawingMode( hps, DM_DRAW );
pmassert( hab, bOK );
}
/* something WAS drawn on the page, yet could be at end of file now */
lRC = (pszOK) ? PAGINATE_NOT_EOF : PAGINATE_EOF_PART_PAGE;
return lRC;
}
/*************************************************************************
*
* name: SetClipPath
*
* Description: Sets the clipping path for the PS
*
* API's: [none]
*
* Parameters: HPS presentation space to use
* PMAIN_PARM pointer to the global data structure
* PPOINTL pointer to begin point of drawing area
* PPOINTL pointer to end point of drawing area
*
* Return: [none]
*
**************************************************************************/
VOID SetClipPath( HPS hps, PMAIN_PARM pmp, POINTL ptlBegin, POINTL ptlEnd )
{
BOOL bOK;
LONG lRC;
HAB hab;
POINTL ptl;
hab = pmp->hab;
bOK = GpiBeginPath( hps, 1);
pmassert( hab, bOK );
/* clip path is inclusive at bottom, left */
ptl.x = ptlBegin.x;
ptl.y = ptlBegin.y;
bOK = GpiSetCurrentPosition( hps, &ptl );
pmassert( hab, bOK );
/* clip path is inclusive at top, right */
ptl.x = ptlEnd.x;
ptl.y = ptlEnd.y;
lRC = GpiBox( hps, DRO_OUTLINE, &ptl, 0L, 0L );
pmassert( hab, lRC != GPI_ERROR );
bOK = GpiEndPath( hps );
pmassert( hab, bOK );
bOK = GpiSetClipPath( hps, 1L, SCP_AND );
pmassert( hab, bOK );
} /* end of SetClipPath */
/*************************************************************************
*
* Name: trim
*
* Description: removes trailing whitespace characters from a string
*
* Parameters: psz = a string
*
* Returns: a pointer to the same string passed in
*
**************************************************************************/
PSZ trim( PSZ s )
{
PCH p;
/* point p to null-terminator byte of string s */
p = s + strlen( s );
/* work backward and remove white space characters at end of string */
p--;
while( s <= p && ( *p == '\r' || *p == ' ' || *p == '\n' || *p == '\t' ))
{
*p = 0;
p--;
}
return s;
} /* end of trim() */
/*************************** End of prtobj.c ****************************/