/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// use this define to disable the DJP support
// #define NO_DJP

#define INCL_DOSMODULEMGR
#define INCL_DEV
#define INCL_SPL
#define INCL_SPLERRORS
#define INCL_SPLDOSPRINT
#define INCL_DEVDJP

#define INCL_GPI
#define INCL_DOSSEMAPHORES
#define INCL_PM
#include <svpm.h>
#include <pmdjp.h>

#include <string.h>

#include <osl/module.h>

#include <tools/urlobj.hxx>
#include <tools/svwin.h>
#ifdef __MINGW32__
#include <excpt.h>
#endif

#include <os2/saldata.hxx>
#include <os2/salinst.h>
#include <os2/salgdi.h>
#include <os2/salframe.h>
#include <os2/salprn.h>

#include <salptype.hxx>
#include <print.h>
#include <jobset.h>

#include <malloc.h>

#ifndef __H_FT2LIB
#include <os2/wingdi.h>
#include <ft2lib.h>
#endif

// =======================================================================

// -----------------------
// - struct ImplFormInfo -
// -----------------------

struct ImplFormInfo
{
	long					mnPaperWidth;
	long					mnPaperHeight;
#ifndef NO_DJP
	DJPT_PAPERSIZE			mnId;
#endif
};

// =======================================================================

// -----------------------
// - struct ImplTrayInfo -
// -----------------------

struct ImplTrayInfo
{
	CHAR			maName[32];
	CHAR			maDisplayName[64];
	DJPT_TRAYTYPE	mnId;

	ImplTrayInfo( const char* pTrayName,
				  const char* pTrayDisplayName ) 
	{
		strcpy( maName, pTrayName);
		strcpy( maDisplayName, pTrayDisplayName);
	}
};

// =======================================================================

struct ImplQueueSalSysData
{
	ByteString		maPrinterName;			// pszPrinters
	ByteString		maName; 				// pszName bzw. LogAddress
	ByteString		maOrgDriverName;		// pszDriverName (maDriverName.maDeviceName)
	ByteString		maDriverName;			// pszDriverName bis .
	ByteString		maDeviceName;			// pszDriverName nach .
	PDRIVDATA		mpDrivData;

					ImplQueueSalSysData( const ByteString& rPrinterName,
										 const ByteString& rName,
										 const ByteString& rDriverName,
										 const ByteString& rDeviceName,
										 const ByteString& rOrgDriverName,
										 PDRIVDATA pDrivData  );
					~ImplQueueSalSysData();
};

// -----------------------------------------------------------------------

ImplQueueSalSysData::ImplQueueSalSysData( const ByteString& rPrinterName,
										  const ByteString& rName,
										  const ByteString& rOrgDriverName,
										  const ByteString& rDriverName,
										  const ByteString& rDeviceName,
										  PDRIVDATA pDrivData ) :
	maPrinterName( rPrinterName ),
	maName( rName ),
	maOrgDriverName( rName ),
	maDriverName( rDriverName ),
	maDeviceName( rDeviceName )
{
	if ( pDrivData )
	{
		mpDrivData = (PDRIVDATA)new PM_BYTE[pDrivData->cb];
		memcpy( mpDrivData, pDrivData, pDrivData->cb );
	}
	else
		mpDrivData = NULL;
}

// -----------------------------------------------------------------------

ImplQueueSalSysData::~ImplQueueSalSysData()
{
	delete mpDrivData;
}

// =======================================================================

static ULONG ImplPMQueueStatusToSal( USHORT nPMStatus )
{
	ULONG nStatus = 0;
	if ( nPMStatus & PRQ3_PAUSED )
		nStatus |= QUEUE_STATUS_PAUSED;
	if ( nPMStatus & PRQ3_PENDING )
		nStatus |= QUEUE_STATUS_PENDING_DELETION;
	if ( !nStatus )
		nStatus |= QUEUE_STATUS_READY;
	return nStatus;
}

// -----------------------------------------------------------------------

void Os2SalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
{
	APIRET rc;
	ULONG  nNeeded;
	ULONG  nReturned;
	ULONG  nTotal;

	// query needed size of the buffer for the QueueInfo
	rc = SplEnumQueue( (PSZ)NULL, 3, NULL, 0, &nReturned, &nTotal, &nNeeded, NULL );
	if( nNeeded == 0 )
		return;

	// create the buffer for the QueueInfo
	PCHAR pQueueData = new CHAR[nNeeded];

	// query QueueInfos
	rc = SplEnumQueue( (PSZ)NULL, 3, pQueueData, nNeeded, &nReturned, &nTotal, &nNeeded, NULL );

	PPRQINFO3 pPrqInfo = (PPRQINFO3)pQueueData;
	for ( int i = 0; i < nReturned; i++ )
	{
		// create entry for the QueueInfo array
		SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;

		ByteString aOrgDriverName( pPrqInfo->pszDriverName);
		ByteString aName( pPrqInfo->pszName);
#if OSL_DEBUG_LEVEL>0
		printf("GetPrinterQueueInfo pszDriverName %s\n", pPrqInfo->pszDriverName);
		printf("GetPrinterQueueInfo pszName %s\n", pPrqInfo->pszDriverName);
#endif
		pInfo->maDriver 	 = ::rtl::OStringToOUString (aOrgDriverName, gsl_getSystemTextEncoding());
		pInfo->maPrinterName = ::rtl::OStringToOUString (pPrqInfo->pszComment, gsl_getSystemTextEncoding());
		pInfo->maLocation	 = ::rtl::OStringToOUString (aName, gsl_getSystemTextEncoding());
		pInfo->mnStatus 	 = ImplPMQueueStatusToSal( pPrqInfo->fsStatus );
		pInfo->mnJobs		 = pPrqInfo->cJobs;
		// pInfo->maComment = !!!

		// Feststellen, ob Name doppelt
		PPRQINFO3 pTempPrqInfo = (PPRQINFO3)pQueueData;
		for ( int j = 0; j < nReturned; j++ )
		{
			// Wenn Name doppelt, erweitern wir diesen um die Location
			if ( (j != i) &&
				 (strcmp( pPrqInfo->pszComment, pTempPrqInfo->pszComment ) == 0) )
			{
				pInfo->maPrinterName += ';';
				pInfo->maPrinterName += pInfo->maLocation;
			}
			pTempPrqInfo++;
		}

		// pszDriver in DriverName (bis .) und DeviceName (nach .) aufsplitten
		PSZ pDriverName;
		PSZ pDeviceName;
		if ( (pDriverName = strchr( pPrqInfo->pszDriverName, '.' )) != 0 )
		{
		   *pDriverName = 0;
		   pDeviceName	= pDriverName + 1;
		}
		else
			pDeviceName = NULL;

		// Alle Bytes hinter dem DeviceNamen auf 0 initialisieren, damit
		// ein memcmp vom JobSetup auch funktioniert
		if ( pPrqInfo->pDriverData &&
			 (pPrqInfo->pDriverData->cb >= sizeof( pPrqInfo->pDriverData )) )
		{
			int nDeviceNameLen = strlen( pPrqInfo->pDriverData->szDeviceName );
			memset( pPrqInfo->pDriverData->szDeviceName+nDeviceNameLen,
					0,
					sizeof( pPrqInfo->pDriverData->szDeviceName )-nDeviceNameLen );
		}

		// save driver data and driver names
		ByteString aPrinterName( pPrqInfo->pszPrinters);
		ByteString aDriverName( pPrqInfo->pszDriverName);
		ByteString aDeviceName;
		if ( pDeviceName )
			aDeviceName = pDeviceName;
		pInfo->mpSysData = new ImplQueueSalSysData( aPrinterName, aName,
													aOrgDriverName,
													aDriverName, aDeviceName,
													pPrqInfo->pDriverData );

		// add queue to the list
		pList->Add( pInfo );

		// increment to next element of the QueueInfo array
		pPrqInfo++;
	}

	delete [] pQueueData;
}

// -----------------------------------------------------------------------

void Os2SalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo )
{
	APIRET rc;
	ULONG  nNeeded;
	ULONG  nReturned;
	ULONG  nTotal;

	// query needed size of the buffer for the QueueInfo
	rc = SplEnumQueue( (PSZ)NULL, 3, NULL, 0, &nReturned, &nTotal, &nNeeded, NULL );
	if( nNeeded == 0 )
		return;

	// create the buffer for the QueueInfo
	PCHAR pQueueData = new CHAR[nNeeded];

	// query QueueInfos
	rc = SplEnumQueue( (PSZ)NULL, 3, pQueueData, nNeeded, &nReturned, &nTotal, &nNeeded, NULL );

	PPRQINFO3 pPrqInfo = (PPRQINFO3)pQueueData;
	for ( int i = 0; i < nReturned; i++ )
	{
		ImplQueueSalSysData* pSysData = (ImplQueueSalSysData*)(pInfo->mpSysData);
		if ( pSysData->maPrinterName.Equals( pPrqInfo->pszPrinters ) &&
			 pSysData->maName.Equals( pPrqInfo->pszName ) &&
			 pSysData->maOrgDriverName.Equals( pPrqInfo->pszDriverName ) )
		{
			pInfo->mnStatus = ImplPMQueueStatusToSal( pPrqInfo->fsStatus );
			pInfo->mnJobs	= pPrqInfo->cJobs;
			break;
		}

		// increment to next element of the QueueInfo array
		pPrqInfo++;
	}

	delete [] pQueueData;
}

// -----------------------------------------------------------------------

void Os2SalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
{
	delete ((ImplQueueSalSysData*)(pInfo->mpSysData));
	delete pInfo;
}

// -----------------------------------------------------------------------

XubString Os2SalInstance::GetDefaultPrinter()
{
	APIRET		rc;
	ULONG		nNeeded;
	ULONG		nReturned;
	ULONG		nTotal;
	char		szQueueName[255];
	XubString	aDefaultName;

	// query default queue
	if ( !PrfQueryProfileString( HINI_PROFILE, SPL_INI_SPOOLER, "QUEUE", 0, szQueueName, sizeof( szQueueName ) ) )
		return aDefaultName;

	// extract first queue name
	PSZ pStr;
	if ( (pStr = strchr( szQueueName, ';' )) != 0 )
		*pStr = 0;

	// query needed size of the buffer for the QueueInfo
	rc = SplEnumQueue( (PSZ)NULL, 3, NULL, 0, &nReturned, &nTotal, &nNeeded, NULL );
	if ( nNeeded == 0 )
		return aDefaultName;

	// create the buffer for the QueueInfo
	PCHAR pQueueData = new CHAR[ nNeeded ];

	// query QueueInfos
	rc = SplEnumQueue ((PSZ)NULL, 3, pQueueData, nNeeded, &nReturned, &nTotal, &nNeeded, NULL );

	// find printer name for default queue
	PPRQINFO3 pPrqInfo = (PPRQINFO3) pQueueData;
	for ( int i = 0; i < nReturned; i++ )
	{
		if ( strcmp( pPrqInfo->pszName, szQueueName ) == 0 )
		{
			aDefaultName = ::rtl::OStringToOUString (pPrqInfo->pszComment, gsl_getSystemTextEncoding());

			// Feststellen, ob Name doppelt
			PPRQINFO3 pTempPrqInfo = (PPRQINFO3)pQueueData;
			for ( int j = 0; j < nReturned; j++ )
			{
				// Wenn Name doppelt, erweitern wir diesen um die Location
				if ( (j != i) &&
					 (strcmp( pPrqInfo->pszComment, pTempPrqInfo->pszComment ) == 0) )
				{
					String pszName( ::rtl::OStringToOUString (pPrqInfo->pszName, gsl_getSystemTextEncoding()));
					aDefaultName += ';';
					aDefaultName += pszName;
				}
				pTempPrqInfo++;
			}
			break;
		}

		// increment to next element of the QueueInfo array
		pPrqInfo++;
	}

	delete [] pQueueData;

	return aDefaultName;
}

// =======================================================================

static void* ImplAllocPrnMemory( size_t n )
{
	return calloc( n, 1);
}

// -----------------------------------------------------------------------

inline void ImplFreePrnMemory( void* p )
{
	free( p );
}

// -----------------------------------------------------------------------

static PDRIVDATA ImplPrnDrivData( const ImplJobSetup* pSetupData )
{
	// Diese Funktion wird eingesetzt, damit Druckertreiber nicht auf
	// unseren Daten arbeiten, da es durch Konfigurationsprobleme
	// sein kann, das der Druckertreiber bei uns Daten ueberschreibt.
	// Durch diese vorgehensweise werden einige Abstuerze vermieden, bzw.
	// sind dadurch leichter zu finden

	if ( !pSetupData->mpDriverData )
		return NULL;

	DBG_ASSERT( ((PDRIVDATA)(pSetupData->mpDriverData))->cb == pSetupData->mnDriverDataLen,
				"ImplPrnDrivData() - SetupDataLen != DriverDataLen" );

	PDRIVDATA pDrivData = (PDRIVDATA)ImplAllocPrnMemory( pSetupData->mnDriverDataLen );
	memcpy( pDrivData, pSetupData->mpDriverData, pSetupData->mnDriverDataLen );
	return pDrivData;
}

// -----------------------------------------------------------------------

static void ImplUpdateSetupData( const PDRIVDATA pDrivData, ImplJobSetup* pSetupData )
{
	// Diese Funktion wird eingesetzt, damit Druckertreiber nicht auf
	// unseren Daten arbeiten, da es durch Konfigurationsprobleme
	// sein kann, das der Druckertreiber bei uns Daten ueberschreibt.
	// Durch diese vorgehensweise werden einige Abstuerze vermieden, bzw.
	// sind dadurch leichter zu finden

	if ( !pDrivData || !pDrivData->cb )
	{
		if ( pSetupData->mpDriverData )
			rtl_freeMemory( pSetupData->mpDriverData );
		pSetupData->mpDriverData = NULL;
		pSetupData->mnDriverDataLen = 0;
	}
	else
	{
		// Alle Bytes hinter dem DeviceNamen auf 0 initialisieren, damit
		// ein memcmp vom JobSetup auch funktioniert
		if ( pDrivData->cb >= sizeof( pDrivData ) )
		{
			int nDeviceNameLen = strlen( pDrivData->szDeviceName );
			memset( pDrivData->szDeviceName+nDeviceNameLen,
					0,
					sizeof( pDrivData->szDeviceName )-nDeviceNameLen );
		}

		if ( pSetupData->mpDriverData )
		{
			if ( pSetupData->mnDriverDataLen != pDrivData->cb )
				rtl_freeMemory( pSetupData->mpDriverData );
			pSetupData->mpDriverData = (sal_uInt8*)rtl_allocateMemory( pDrivData->cb);
		}
		else
			pSetupData->mpDriverData = (sal_uInt8*)rtl_allocateMemory( pDrivData->cb);
		pSetupData->mnDriverDataLen = pDrivData->cb;
		memcpy( pSetupData->mpDriverData, pDrivData, pDrivData->cb );
	}

	if ( pDrivData )
		ImplFreePrnMemory( pDrivData );
}

// -----------------------------------------------------------------------

static sal_Bool ImplPaperSizeEqual( long nPaperWidth1, long nPaperHeight1,
								long nPaperWidth2, long nPaperHeight2 )
{
	return (((nPaperWidth1 >= nPaperWidth2-1) && (nPaperWidth1 <= nPaperWidth2+1)) &&
			((nPaperHeight1 >= nPaperHeight2-1) && (nPaperHeight1 <= nPaperHeight2+1)));
}

// -----------------------------------------------------------------------

static sal_Bool ImplIsDriverDJPEnabled( HDC hDC )
{
#ifdef NO_DJP
	return FALSE;
#else
	// Ueber OS2-Ini kann DJP disablte werden
	if ( !PrfQueryProfileInt( HINI_PROFILE, SAL_PROFILE_APPNAME, SAL_PROFILE_USEDJP, 1 ) )
		return FALSE;

	// Testen, ob DJP-Interface am Drucker vorhanden
	LONG   lQuery;
	APIRET rc;

	lQuery = DEVESC_QUERYSIZE;
	rc = DevEscape( hDC,
					DEVESC_QUERYESCSUPPORT,
					sizeof( lQuery ),
					(PBYTE)&lQuery,
					0,
					(PBYTE)NULL );
	if ( DEV_OK != rc )
		return FALSE;

	lQuery = DEVESC_QUERYJOBPROPERTIES;
	rc = DevEscape( hDC,
					DEVESC_QUERYESCSUPPORT,
					sizeof( lQuery ),
					(PBYTE)&lQuery,
					0,
					(PBYTE)NULL );
	if ( DEV_OK != rc )
		return FALSE;

	lQuery = DEVESC_SETJOBPROPERTIES;
	rc = DevEscape( hDC,
					DEVESC_QUERYESCSUPPORT,
					sizeof( lQuery ),
					(PBYTE)&lQuery,
					0,
					(PBYTE)NULL );
	if ( DEV_OK != rc )
		return FALSE;

	return TRUE;
#endif
}

// -----------------------------------------------------------------------

static void ImplFormatInputList( PDJP_ITEM pDJP, PQUERYTUPLE pTuple )
{
   // Loop through the query elements
   sal_Bool fContinue = TRUE;
   do
   {
	  pDJP->cb			  = sizeof (DJP_ITEM);
	  pDJP->ulProperty	  = pTuple->ulProperty;
	  pDJP->lType		  = pTuple->lType;
	  pDJP->ulNumReturned = 0;
	  pDJP->ulValue 	  = DJP_NONE;

	  // at EOL?
	  fContinue = DJP_NONE != pTuple->ulProperty;

	  // Move to next item structure and tuplet
	  pDJP++;
	  pTuple++;
   }
   while ( fContinue );
}

// -----------------------------------------------------------------------

static void ImplFreeFormAndTrayList( Os2SalInfoPrinter* pOs2SalInfoPrinter )
{
	if ( pOs2SalInfoPrinter->mnFormCount )
	{
		for ( USHORT i = 0; i < pOs2SalInfoPrinter->mnFormCount; i++ )
			delete pOs2SalInfoPrinter->mpFormArray[i];
		delete [] pOs2SalInfoPrinter->mpFormArray;
		pOs2SalInfoPrinter->mnFormCount = 0;
	}

	if ( pOs2SalInfoPrinter->mnTrayCount )
	{
		for ( USHORT i = 0; i < pOs2SalInfoPrinter->mnTrayCount; i++ )
			delete pOs2SalInfoPrinter->mpTrayArray[i];
		delete [] pOs2SalInfoPrinter->mpTrayArray;
		pOs2SalInfoPrinter->mnTrayCount = 0;
	}
}

// -----------------------------------------------------------------------

static void ImplGetFormAndTrayList( Os2SalInfoPrinter* pOs2SalInfoPrinter, const ImplJobSetup* pSetupData )
{
	// if not defined, suppose default orientation is portrait
	Orientation orientation = ORIENTATION_PORTRAIT;

	ImplFreeFormAndTrayList( pOs2SalInfoPrinter );

	LONG alQuery[] =
	{
		0,					0,				// First two members of QUERYSIZE
		DJP_SJ_ORIENTATION, 	DJP_CURRENT,
		DJP_CJ_FORM,		DJP_ALL,
		DJP_CJ_TRAYNAME,	DJP_ALL,
		DJP_NONE,			DJP_NONE		// EOL marker
	};

	APIRET		rc;
	PQUERYSIZE	pQuerySize			= (PQUERYSIZE)alQuery;
	PBYTE		pBuffer 			= NULL;
	LONG		nAlloc				= 0;
	PDRIVDATA	pCopyDrivData		= ImplPrnDrivData( pSetupData );
	LONG		nDrivDataSize		= pCopyDrivData->cb;
	PBYTE		pDrivData			= (PBYTE)pCopyDrivData;

	// find out how many bytes to allocate
	pQuerySize->cb = sizeof( alQuery );
	rc = DevEscape( pOs2SalInfoPrinter->mhDC,
					DEVESC_QUERYSIZE,
					sizeof( alQuery ),
					(PBYTE)pQuerySize,
					&nDrivDataSize,
					pDrivData );
	if ( DEV_OK != rc )
	{
		ImplFreePrnMemory( pCopyDrivData );
		return;
	}

	// allocate the memory
	nAlloc = pQuerySize->ulSizeNeeded;
	pBuffer = (PBYTE)new PM_BYTE[nAlloc];

	// set up the input
	PDJP_ITEM pDJP = (PDJP_ITEM)pBuffer;
	ImplFormatInputList( pDJP, pQuerySize->aTuples );

	// do it!
	rc = DevEscape( pOs2SalInfoPrinter->mhDC,
					DEVESC_QUERYJOBPROPERTIES,
					nAlloc,
					pBuffer,
					&nDrivDataSize,
					pDrivData );
	ImplFreePrnMemory( pCopyDrivData );

	if ( (DEV_OK == rc) || (DEV_WARNING == rc) )
	{
		// Loop through the query elements
		PQUERYTUPLE pTuple = pQuerySize->aTuples;
		while ( DJP_NONE != pTuple->ulProperty )
		{
			if ( pDJP->ulProperty == DJP_SJ_ORIENTATION )
			{
				if ( pDJP->ulNumReturned )
				{
					PDJPT_ORIENTATION pElm = DJP_ELEMENTP( *pDJP, DJPT_ORIENTATION );
					if ( (DJP_ORI_PORTRAIT == *pElm) || (DJP_ORI_REV_PORTRAIT == *pElm) )
						orientation = ORIENTATION_PORTRAIT;
					else
						orientation = ORIENTATION_LANDSCAPE;
				}
			}
			else if ( pDJP->ulProperty == DJP_CJ_FORM )
			{
				if ( pDJP->ulNumReturned )
				{
					PDJPT_FORM pElm = DJP_ELEMENTP( *pDJP, DJPT_FORM );

					pOs2SalInfoPrinter->mnFormCount = pDJP->ulNumReturned;
					pOs2SalInfoPrinter->mpFormArray = new PIMPLFORMINFO[pOs2SalInfoPrinter->mnFormCount];
					for( int i = 0; i < pDJP->ulNumReturned; i++, pElm++ )
					{
						ImplFormInfo* pInfo 	= new ImplFormInfo;
						// AOO expects form size always in portrait mode
						if (orientation == ORIENTATION_PORTRAIT)
						{
							pInfo->mnPaperWidth 	= pElm->hcInfo.cx;
							pInfo->mnPaperHeight	= pElm->hcInfo.cy;
						}
						else
						{
							pInfo->mnPaperWidth 	= pElm->hcInfo.cy;
							pInfo->mnPaperHeight	= pElm->hcInfo.cx;
						}
#if OSL_DEBUG_LEVEL>0
						debug_printf("ImplGetFormAndTrayList #%d: %d x %d", 
									 i, pInfo->mnPaperWidth, pInfo->mnPaperHeight);
#endif
						pInfo->mnId 			= pElm->djppsFormID;
						pOs2SalInfoPrinter->mpFormArray[i] = pInfo;
					}
				}
			}
			else if ( pDJP->ulProperty == DJP_CJ_TRAYNAME )
			{
				if ( pDJP->ulNumReturned )
				{
					PDJPT_TRAYNAME pElm = DJP_ELEMENTP( *pDJP, DJPT_TRAYNAME );

					pOs2SalInfoPrinter->mnTrayCount = pDJP->ulNumReturned;
					pOs2SalInfoPrinter->mpTrayArray = new PIMPLTRAYINFO[pOs2SalInfoPrinter->mnTrayCount];
					for( int i = 0; i < pDJP->ulNumReturned; i++, pElm++ )
					{
						ImplTrayInfo* pInfo 	= new ImplTrayInfo( pElm->szTrayname, pElm->szDisplayTrayname );
						pInfo->mnId 			= pElm->djpttTrayID;
						pOs2SalInfoPrinter->mpTrayArray[i] = pInfo;
					}
				}
			}

			pDJP = DJP_NEXT_STRUCTP( pDJP );
			pTuple++;
		}
	}

	delete [] pBuffer;
}

// -----------------------------------------------------------------------

static sal_Bool ImplGetCurrentSettings( Os2SalInfoPrinter* pOs2SalInfoPrinter, ImplJobSetup* pSetupData )
{
	// Um den aktuellen Tray zu ermitteln, brauchen wir auch die Listen dazu
	if ( !pOs2SalInfoPrinter->mnFormCount )
		ImplGetFormAndTrayList( pOs2SalInfoPrinter, pSetupData );

	LONG alQuery[] =
	{
		0,						0,				// First two members of QUERYSIZE
		DJP_SJ_ORIENTATION, 	DJP_CURRENT,
		DJP_CJ_FORM,			DJP_CURRENT,
		DJP_NONE,				DJP_NONE		// EOL marker
	};

	APIRET		rc;
	PQUERYSIZE	pQuerySize			= (PQUERYSIZE)alQuery;
	PBYTE		pBuffer 			= NULL;
	LONG		nAlloc				= 0;
	PDRIVDATA	pCopyDrivData		= ImplPrnDrivData( pSetupData );
	LONG		nDrivDataSize		= pCopyDrivData->cb;
	PBYTE		pDrivData			= (PBYTE)pCopyDrivData;
	sal_Bool		bResult;

	// find out how many bytes to allocate
	pQuerySize->cb = sizeof( alQuery );
	rc = DevEscape( pOs2SalInfoPrinter->mhDC,
					DEVESC_QUERYSIZE,
					sizeof( alQuery ),
					(PBYTE)pQuerySize,
					&nDrivDataSize,
					pDrivData );
	if ( DEV_OK != rc )
	{
		ImplFreePrnMemory( pCopyDrivData );
		return FALSE;
	}

	// allocate the memory
	nAlloc = pQuerySize->ulSizeNeeded;
	pBuffer = (PBYTE)new PM_BYTE[nAlloc];

	// set up the input
	PDJP_ITEM pDJP = (PDJP_ITEM)pBuffer;
	ImplFormatInputList( pDJP, pQuerySize->aTuples );

	rc = DevEscape( pOs2SalInfoPrinter->mhDC,
					DEVESC_QUERYJOBPROPERTIES,
					nAlloc,
					pBuffer,
					&nDrivDataSize,
					pDrivData );
	if ( (DEV_OK == rc) || (DEV_WARNING == rc) )
	{
		// aktuelle Setup-Daten uebernehmen
		ImplUpdateSetupData( pCopyDrivData, pSetupData );

		// Loop through the query elements
		PQUERYTUPLE pTuple = pQuerySize->aTuples;
		while ( DJP_NONE != pTuple->ulProperty )
		{
			if ( pDJP->ulProperty == DJP_SJ_ORIENTATION )
			{
				if ( pDJP->ulNumReturned )
				{
					PDJPT_ORIENTATION pElm = DJP_ELEMENTP( *pDJP, DJPT_ORIENTATION );
					if ( (DJP_ORI_PORTRAIT == *pElm) || (DJP_ORI_REV_PORTRAIT == *pElm) )
						pSetupData->meOrientation = ORIENTATION_PORTRAIT;
					else
						pSetupData->meOrientation = ORIENTATION_LANDSCAPE;
				}
			}
			else if ( pDJP->ulProperty == DJP_CJ_FORM )
			{
				if ( pDJP->ulNumReturned )
				{
					PDJPT_FORM pElm = DJP_ELEMENTP( *pDJP, DJPT_FORM );

					pSetupData->mnPaperWidth  = pElm->hcInfo.cx*100;
					pSetupData->mnPaperHeight = pElm->hcInfo.cy*100;
					switch( pElm->djppsFormID )
					{
						case DJP_PSI_A3:
							pSetupData->mePaperFormat = PAPER_A3;
							break;

						case DJP_PSI_A4:
							pSetupData->mePaperFormat = PAPER_A4;
							break;

						case DJP_PSI_A5:
							pSetupData->mePaperFormat = PAPER_A5;
							break;

						case DJP_PSI_B4:
							pSetupData->mePaperFormat = PAPER_B4_JIS;
							break;

						case DJP_PSI_B5:
							pSetupData->mePaperFormat = PAPER_B5_JIS;
							break;

						case DJP_PSI_LETTER:
							pSetupData->mePaperFormat = PAPER_LETTER;
							break;

						case DJP_PSI_LEGAL:
							pSetupData->mePaperFormat = PAPER_LEGAL;
							break;

						case DJP_PSI_TABLOID:
							pSetupData->mePaperFormat = PAPER_TABLOID;
							break;

						default:
							pSetupData->mePaperFormat = PAPER_USER;
							break;
					}

					// Wir suchen zuerst ueber den Namen/Id und dann ueber die Id
					sal_Bool	bTrayFound = FALSE;
					USHORT	j;
					for ( j = 0; j < pOs2SalInfoPrinter->mnTrayCount; j++ )
					{
						if ( (pOs2SalInfoPrinter->mpTrayArray[j]->mnId == pElm->djpttTrayID) &&
							 (pOs2SalInfoPrinter->mpTrayArray[j]->maName == pElm->szTrayname) )
						{
							pSetupData->mnPaperBin = j;
							bTrayFound = TRUE;
							break;
						}
					}
					if ( !bTrayFound )
					{
						for ( j = 0; j < pOs2SalInfoPrinter->mnTrayCount; j++ )
						{
							if ( pOs2SalInfoPrinter->mpTrayArray[j]->mnId == pElm->djpttTrayID )
							{
								pSetupData->mnPaperBin = j;
								bTrayFound = TRUE;
								break;
							}
						}
					}
					// Wenn wir Ihn immer noch nicht gefunden haben, setzen
					// wir ihn auf DontKnow
					if ( !bTrayFound )
						pSetupData->mnPaperBin = 0xFFFF;
				}
			}

			pDJP = DJP_NEXT_STRUCTP( pDJP );
			pTuple++;
		}

		bResult = TRUE;
	}
	else
	{
		ImplFreePrnMemory( pCopyDrivData );
		bResult = FALSE;
	}

	delete [] pBuffer;

	return bResult;
}

// -----------------------------------------------------------------------

static sal_Bool ImplSetOrientation( HDC hPrinterDC, PDRIVDATA pDriverData,
								Orientation eOrientation )
{
	LONG alQuery[] =
	{
		0,						0,				// First two members of QUERYSIZE
		DJP_SJ_ORIENTATION, 	DJP_CURRENT,
		DJP_NONE,				DJP_NONE		// EOL marker
	};
#if OSL_DEBUG_LEVEL>0
	debug_printf( "ImplSetOrientation mhDC %x, %d", hPrinterDC, eOrientation);
#endif

	APIRET		rc;
	PQUERYSIZE	pQuerySize		= (PQUERYSIZE)alQuery;
	PBYTE		pBuffer 		= NULL;
	LONG		nAlloc			= 0;
	LONG		nDrivDataSize	= pDriverData->cb;

	// find out how many bytes to allocate
	pQuerySize->cb = sizeof( alQuery );
	rc = DevEscape( hPrinterDC,
					DEVESC_QUERYSIZE,
					sizeof( alQuery ),
					(PBYTE)pQuerySize,
					&nDrivDataSize,
					(PBYTE)pDriverData );
	if ( DEV_OK != rc )
		return FALSE;

	// allocate the memory
	nAlloc = pQuerySize->ulSizeNeeded;
	pBuffer = (PBYTE)new PM_BYTE[nAlloc];

	// set up the input
	PDJP_ITEM pDJP = (PDJP_ITEM)pBuffer;
	ImplFormatInputList( pDJP, pQuerySize->aTuples );

	pDJP->cb		 = sizeof( DJP_ITEM );
	pDJP->ulProperty = DJP_SJ_ORIENTATION;
	pDJP->lType 	 = DJP_CURRENT;
	pDJP->ulValue	 = (eOrientation == ORIENTATION_PORTRAIT)
						   ? DJP_ORI_PORTRAIT
						   : DJP_ORI_LANDSCAPE;

	// do it!
	rc = DevEscape( hPrinterDC,
					DEVESC_SETJOBPROPERTIES,
					nAlloc,
					pBuffer,
					&nDrivDataSize,
					(PBYTE)pDriverData );

	delete [] pBuffer;

	return ((DEV_OK == rc) || (DEV_WARNING == rc));
}

// -----------------------------------------------------------------------

static sal_Bool ImplSetPaperSize( HDC hPrinterDC, PDRIVDATA pDriverData,
							  DJPT_PAPERSIZE nOS2PaperFormat )
{
	LONG alQuery[] =
	{
		0,						0,				// First two members of QUERYSIZE
		DJP_SJ_PAPERSIZE,		DJP_CURRENT,
		DJP_NONE,				DJP_NONE		// EOL marker
	};

	APIRET		rc;
	PQUERYSIZE	pQuerySize		= (PQUERYSIZE)alQuery;
	PBYTE		pBuffer 		= NULL;
	LONG		nAlloc			= 0;
	LONG		nDrivDataSize	= pDriverData->cb;

	// find out how many bytes to allocate
	pQuerySize->cb = sizeof( alQuery );
	rc = DevEscape( hPrinterDC,
					DEVESC_QUERYSIZE,
					sizeof( alQuery ),
					(PBYTE)pQuerySize,
					&nDrivDataSize,
					(PBYTE)pDriverData );
	if ( DEV_OK != rc )
		return FALSE;

	// allocate the memory
	nAlloc = pQuerySize->ulSizeNeeded;
	pBuffer = (PBYTE)new PM_BYTE[nAlloc];

	// set up the input
	PDJP_ITEM pDJP = (PDJP_ITEM)pBuffer;
	PDJP_ITEM pStartDJP = pDJP;
	ImplFormatInputList( pDJP, pQuerySize->aTuples );

	// Neue Daten zuweisen
	pDJP->cb		 = sizeof( DJP_ITEM );
	pDJP->ulProperty = DJP_SJ_PAPERSIZE;
	pDJP->lType 	 = DJP_CURRENT;
	pDJP->ulValue	 = nOS2PaperFormat;

	// und setzen
	rc = DevEscape( hPrinterDC,
					DEVESC_SETJOBPROPERTIES,
					nAlloc,
					pBuffer,
					&nDrivDataSize,
					(PBYTE)pDriverData );

	delete [] pBuffer;

	return ((DEV_OK == rc) || (DEV_WARNING == rc));
}

// -----------------------------------------------------------------------

static sal_Bool ImplSetPaperBin( HDC hPrinterDC, PDRIVDATA pDriverData,
							 ImplTrayInfo* pTrayInfo )
{
	LONG alQuery[] =
	{
		0,						0,				// First two members of QUERYSIZE
		DJP_SJ_TRAYTYPE,		DJP_CURRENT,
		DJP_NONE,				DJP_NONE		// EOL marker
	};

	APIRET		rc;
	PQUERYSIZE	pQuerySize		= (PQUERYSIZE)alQuery;
	PBYTE		pBuffer 		= NULL;
	LONG		nAlloc			= 0;
	LONG		nDrivDataSize	= pDriverData->cb;

	// find out how many bytes to allocate
	pQuerySize->cb = sizeof( alQuery );
	rc = DevEscape( hPrinterDC,
					DEVESC_QUERYSIZE,
					sizeof( alQuery ),
					(PBYTE)pQuerySize,
					&nDrivDataSize,
					(PBYTE)pDriverData );
	if ( DEV_OK != rc )
		return FALSE;

	// allocate the memory
	nAlloc = pQuerySize->ulSizeNeeded;
	pBuffer = (PBYTE)new PM_BYTE[nAlloc];

	// set up the input
	PDJP_ITEM pDJP = (PDJP_ITEM)pBuffer;
	ImplFormatInputList( pDJP, pQuerySize->aTuples );

	// Neue Daten zuweisen
	pDJP->cb		 = sizeof( DJP_ITEM );
	pDJP->ulProperty = DJP_SJ_TRAYTYPE;
	pDJP->lType 	 = DJP_CURRENT;
	pDJP->ulValue	 = pTrayInfo->mnId;

	// und setzen
	rc = DevEscape( hPrinterDC,
					DEVESC_SETJOBPROPERTIES,
					nAlloc,
					pBuffer,
					&nDrivDataSize,
					(PBYTE)pDriverData );

	delete [] pBuffer;

	return ((DEV_OK == rc) || (DEV_WARNING == rc));
}

// =======================================================================

static sal_Bool ImplSalCreateInfoPrn( Os2SalInfoPrinter* pPrinter, PDRIVDATA pDriverData,
								  HDC& rDC, HPS& rPS )
{
	SalData* pSalData = GetSalData();

	// create info context
	DEVOPENSTRUC  devOpenStruc;
	memset( &devOpenStruc, 0, sizeof( devOpenStruc ) );
	devOpenStruc.pszLogAddress		= (char*)pPrinter->maName.GetBuffer();
	devOpenStruc.pszDriverName		= (char*)pPrinter->maDriverName.GetBuffer();
	devOpenStruc.pdriv				= pDriverData;
	devOpenStruc.pszDataType		= "PM_Q_STD";

	HDC hDC = DevOpenDC( pSalData->mhAB, OD_INFO, "*",
						 4, (PDEVOPENDATA)&devOpenStruc, (HDC)NULL);
	if ( !hDC )
		return FALSE;

	// create presentation space
	SIZEL sizel;
	sizel.cx = 0;
	sizel.cy = 0;
	HPS hPS = Ft2CreatePS( pSalData->mhAB, hDC, &sizel, GPIA_ASSOC | GPIT_MICRO | PU_PELS );
	if ( !hPS )
	{
		DevCloseDC( hDC );
		return FALSE;
	}

	rDC = hDC;
	rPS = hPS;
	return TRUE;
}

// -----------------------------------------------------------------------

static void ImplSalDestroyInfoPrn( Os2SalInfoPrinter* pPrinter )
{
	ImplSalDeInitGraphics( pPrinter->mpGraphics);
	Ft2Associate( pPrinter->mhPS, 0 );
	Ft2DestroyPS( pPrinter->mhPS );
	DevCloseDC( pPrinter->mhDC );
}

// =======================================================================

SalInfoPrinter* Os2SalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
												ImplJobSetup* pSetupData )
{
	ImplQueueSalSysData* pSysQueueData = (ImplQueueSalSysData*)(pQueueInfo->mpSysData);
	Os2SalInfoPrinter* pPrinter = new Os2SalInfoPrinter;
	pPrinter->maPrinterName	= pSysQueueData->maPrinterName;
	pPrinter->maName			= pSysQueueData->maName;
	pPrinter->maDriverName	= pSysQueueData->maDriverName;
	pPrinter->maDeviceName	= pSysQueueData->maDeviceName;

	// Nur Setup-Daten uebernehmen, wenn Treiber und Laenge der Treiberdaten
	// uebereinstimmt
	PDRIVDATA	pDriverData;
	sal_Bool		bUpdateDriverData;
	if ( pSetupData->mpDriverData && pSysQueueData->mpDrivData &&
		 (pSetupData->mnSystem == JOBSETUP_SYSTEM_OS2) &&
		 (pSetupData->mnDriverDataLen == pSysQueueData->mpDrivData->cb) &&
		 (strcmp( ((PDRIVDATA)pSetupData->mpDriverData)->szDeviceName,
				  pSysQueueData->mpDrivData->szDeviceName ) == 0) )
	{
		pDriverData = PDRIVDATA( pSetupData->mpDriverData );
		bUpdateDriverData = FALSE;
	}
	else
	{
		pDriverData = pSysQueueData->mpDrivData;
		bUpdateDriverData = TRUE;
	}
	if ( pDriverData )
		pPrinter->maJobSetupDeviceName = pDriverData->szDeviceName;

	if ( !ImplSalCreateInfoPrn( pPrinter, pDriverData,
								pPrinter->mhDC,
								pPrinter->mhPS ) )
	{
		delete pPrinter;
		return NULL;
	}

	// create graphics object for output
	Os2SalGraphics* pGraphics = new Os2SalGraphics;
	pGraphics->mhDC				= pPrinter->mhDC;
	pGraphics->mhPS				= pPrinter->mhPS;
	pGraphics->mhWnd 			= 0;
	pGraphics->mbPrinter 		= TRUE;
	pGraphics->mbVirDev			= FALSE;
	pGraphics->mbWindow			= FALSE;
	pGraphics->mbScreen			= FALSE;

	ImplSalInitGraphics( pGraphics );
	pPrinter->mpGraphics			= pGraphics;

	// check printer driver for DJP support
	pPrinter->mbDJPSupported = ImplIsDriverDJPEnabled( pPrinter->mhDC );

	if ( bUpdateDriverData )
	{
		if ( pSetupData->mpDriverData )
			rtl_freeMemory( pSetupData->mpDriverData);
		pSetupData->mpDriverData = (sal_uInt8*)rtl_allocateMemory( pDriverData->cb);
		memcpy( pSetupData->mpDriverData, pDriverData, pDriverData->cb );
		pSetupData->mnDriverDataLen = pDriverData->cb;
	}

	// retrieve current settings from printer driver and store them to system independend data!
	if ( pPrinter->mbDJPSupported )
		ImplGetCurrentSettings( pPrinter, pSetupData );
	pSetupData->mnSystem = JOBSETUP_SYSTEM_OS2;

	return pPrinter;
}

// -----------------------------------------------------------------------

void Os2SalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
{
	delete pPrinter;
}

// =======================================================================

Os2SalInfoPrinter::Os2SalInfoPrinter()
{
	mhDC					= 0;
	mhPS					= 0;
	mpGraphics			= NULL;
	mbGraphics			= FALSE;
	mbDJPSupported		= FALSE;
	mnFormCount			= 0;
	mpFormArray			= NULL;
	mnTrayCount			= 0;
	mpTrayArray			= NULL;
	m_bPapersInit		= FALSE;
}

// -----------------------------------------------------------------------

Os2SalInfoPrinter::~Os2SalInfoPrinter()
{
	if ( mpGraphics )
	{
		ImplSalDestroyInfoPrn( this );
		delete mpGraphics;
	}

	ImplFreeFormAndTrayList( this );
}

// -----------------------------------------------------------------------

void Os2SalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData )
{
#if OSL_DEBUG_LEVEL>0
	debug_printf( "Os2SalInfoPrinter::InitPaperFormats pSetupData %x", 
				  pSetupData);
#endif

	m_aPaperFormats.clear();
	m_bPapersInit = true;

	// init paperbinlist if empty
	if ( !mnTrayCount )
		ImplGetFormAndTrayList( this, pSetupData );

	for( int i = 0; i < mnFormCount; i++)
	{
		PaperInfo aInfo( mpFormArray[i]->mnPaperWidth * 100,
						 mpFormArray[i]->mnPaperHeight * 100);
#if OSL_DEBUG_LEVEL>0
		debug_printf( "Os2SalInfoPrinter::InitPaperFormats #%d: %d x %d", 
					  i, mpFormArray[i]->mnPaperWidth * 100,
					  mpFormArray[i]->mnPaperHeight * 100);
#endif
		m_aPaperFormats.push_back( aInfo );
	}
}

// -----------------------------------------------------------------------

int Os2SalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* pSetupData )
{
	return 900;
}

// -----------------------------------------------------------------------

SalGraphics* Os2SalInfoPrinter::GetGraphics()
{
	if ( mbGraphics )
		return NULL;

	if ( mpGraphics )
		mbGraphics = TRUE;

	return mpGraphics;
}

// -----------------------------------------------------------------------

void Os2SalInfoPrinter::ReleaseGraphics( SalGraphics* )
{
	mbGraphics = FALSE;
}

// -----------------------------------------------------------------------

sal_Bool Os2SalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pSetupData )
{
	PDRIVDATA pDrivData = ImplPrnDrivData( pSetupData );
	if ( !pDrivData )
		return FALSE;

	APIRET rc = DevPostDeviceModes( GetSalData()->mhAB, pDrivData,
									maDriverName.GetBuffer(),
									maDeviceName.GetBuffer(),
									maPrinterName.GetBuffer(),
									DPDM_POSTJOBPROP );
	if ( rc == DEV_OK )
	{
		ImplUpdateSetupData( pDrivData, pSetupData );

		// update DC and PS
		HDC hDC;
		HPS hPS;
		if ( !ImplSalCreateInfoPrn( this, (PDRIVDATA)(pSetupData->mpDriverData), hDC, hPS ) )
			return FALSE;

		// Alten Printer DC/PS zerstoeren
		ImplSalDestroyInfoPrn( this );

		// Neue Daten setzen und initialisieren
		mhDC = hDC;
		mhPS = hPS;
		mpGraphics->mhDC = mhDC;
		mpGraphics->mhPS = mhPS;
		ImplSalInitGraphics( mpGraphics );

		// retrieve current settings from printer driver and store them to system independend data!
		ImplFreeFormAndTrayList( this );
		if ( mbDJPSupported )
			ImplGetCurrentSettings( this, pSetupData );

		return TRUE;
	}
	else
	{
		ImplFreePrnMemory( pDrivData );
		return FALSE;
	}
}

// -----------------------------------------------------------------------

sal_Bool Os2SalInfoPrinter::SetPrinterData( ImplJobSetup* pSetupData )
{
	// Wir koennen nur Treiberdaten von OS2 setzen
	if ( pSetupData->mnSystem != JOBSETUP_SYSTEM_OS2 )
		return FALSE;

	PDRIVDATA pNewDrivData = (PDRIVDATA)(pSetupData->mpDriverData);
	if ( !pNewDrivData )
		return FALSE;

	// Testen, ob Printerdaten fuer den gleichen Printer uebergeben werden,
	// da einige Treiber zu Abstuerzen neigen, wenn Daten von einem anderen
	// Printer gesetzt werden
	if ( !maJobSetupDeviceName.Equals( pNewDrivData->szDeviceName ))
		return FALSE;

	// update DC and PS
	HDC hDC;
	HPS hPS;
	if ( !ImplSalCreateInfoPrn( this, pNewDrivData, hDC, hPS ) )
		return FALSE;

	// Alten Printer DC/PS zerstoeren
	ImplSalDestroyInfoPrn( this );

	// Neue Daten setzen und initialisieren
	mhDC = hDC;
	mhPS = hPS;
	mpGraphics->mhDC = mhDC;
	mpGraphics->mhPS = mhPS;
	ImplSalInitGraphics( mpGraphics );

	// retrieve current settings from printer driver and store them to system independend data!
	ImplFreeFormAndTrayList( this );
	if ( mbDJPSupported )
		ImplGetCurrentSettings( this, pSetupData );

	return TRUE;
}

// -----------------------------------------------------------------------

sal_Bool Os2SalInfoPrinter::SetData( ULONG nFlags, ImplJobSetup* pSetupData )
{
#if OSL_DEBUG_LEVEL>0
	debug_printf( "Os2SalInfoPrinter::SetData nFlags %x, pSetupData %x", 
				  nFlags, pSetupData);
#endif

	// needs DJP support
	if ( !mbDJPSupported )
		return FALSE;

	PDRIVDATA pDrivData = ImplPrnDrivData( pSetupData );

	if ( !pDrivData )
		return FALSE;

	sal_Bool bOK = FALSE;

	// set orientation
	if ( nFlags & SAL_JOBSET_ORIENTATION )
	{
#if OSL_DEBUG_LEVEL>0
		debug_printf( "Os2SalInfoPrinter::SetData meOrientation %d", pSetupData->meOrientation);
#endif
		if ( ImplSetOrientation( mhDC, pDrivData, pSetupData->meOrientation ) )
			bOK = TRUE;
	}

	// set paper size
	if ( nFlags & SAL_JOBSET_PAPERSIZE )
	{
		// Papierformat ermitteln
		DJPT_PAPERSIZE nOS2PaperFormat;
		switch ( pSetupData->mePaperFormat )
		{
			case PAPER_A3:
				nOS2PaperFormat = DJP_PSI_A3;
				break;

			case PAPER_A4:
				nOS2PaperFormat = DJP_PSI_A4;
				break;

			case PAPER_A5:
				nOS2PaperFormat = DJP_PSI_A5;
				break;

			case PAPER_B4_JIS:
				nOS2PaperFormat = DJP_PSI_B4;
				break;

			case PAPER_B5_JIS:
				nOS2PaperFormat = DJP_PSI_B5;
				break;

			case PAPER_LETTER:
				nOS2PaperFormat = DJP_PSI_LETTER;
				break;

			case PAPER_LEGAL:
				nOS2PaperFormat = DJP_PSI_LEGAL;
				break;

			case PAPER_TABLOID:
				nOS2PaperFormat = DJP_PSI_TABLOID;
				break;

			default:
				{
				nOS2PaperFormat = DJP_PSI_NONE;
				// OS2 rechnet in Millimetern
				long nPaperWidth	 = pSetupData->mnPaperWidth  / 100;
				long nPaperHeight	 = pSetupData->mnPaperHeight / 100;
				// Ansonsten ueber die Papiergroesse suchen
				for( int i = 0; i < mnFormCount; i++ )
				{
					ImplFormInfo* pFormInfo = mpFormArray[i];
					if ( ImplPaperSizeEqual( nPaperWidth, nPaperHeight,
											 pFormInfo->mnPaperWidth, pFormInfo->mnPaperHeight ) )
					{
						nOS2PaperFormat = pFormInfo->mnId;
						break;
					}
				}
				}
				break;
		}

		if ( nOS2PaperFormat != DJP_PSI_NONE )
		{
			if ( ImplSetPaperSize( mhDC, pDrivData, nOS2PaperFormat ) )
				bOK = TRUE;
		}
	}

	// set paper tray
	if ( (nFlags & SAL_JOBSET_PAPERBIN) && (pSetupData->mnPaperBin < mnTrayCount) )
	{
		if ( ImplSetPaperBin( mhDC, pDrivData,
							  mpTrayArray[pSetupData->mnPaperBin] ) )
			bOK = TRUE;
	}

	if ( bOK )
	{
		ImplUpdateSetupData( pDrivData, pSetupData );

		// query current driver settings
		ImplFreeFormAndTrayList( this );
		if ( ImplGetCurrentSettings( this, pSetupData ) )
		{
			// update DC and PS
			HDC hDC;
			HPS hPS;
			if ( ImplSalCreateInfoPrn( this, (PDRIVDATA)(pSetupData->mpDriverData), hDC, hPS ) )
			{
				// Alten Printer DC/PS zerstoeren
				ImplSalDestroyInfoPrn( this );

				// Neue Daten setzen und initialisieren
				mhDC = hDC;
				mhPS = hPS;
				mpGraphics->mhDC = mhDC;
				mpGraphics->mhPS = mhPS;
				ImplSalInitGraphics( mpGraphics );
			}
			else
				bOK = FALSE;
		}
		else
			bOK = FALSE;
	}

	return bOK;
}

// -----------------------------------------------------------------------

ULONG Os2SalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
{
	if ( !mbDJPSupported )
		return 1;

	// init paperbinlist if empty
	if ( !mnTrayCount )
		ImplGetFormAndTrayList( this, pJobSetup );

	// Wir haben immer einen PaperTray und wenn, das eben einen ohne
	// Namen
	if ( !mnTrayCount )
		return 1;
	else
		return mnTrayCount;
}

// -----------------------------------------------------------------------

XubString Os2SalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup,
										  ULONG nPaperBin )
{
	XubString aPaperBinName;

	if ( mbDJPSupported )
	{
		// init paperbinlist if empty
		if ( !mnTrayCount )
			ImplGetFormAndTrayList( this, pJobSetup );

		if ( nPaperBin < mnTrayCount )
			aPaperBinName = ::rtl::OStringToOUString (mpTrayArray[nPaperBin]->maDisplayName, gsl_getSystemTextEncoding());
	}

	return aPaperBinName;
}

// -----------------------------------------------------------------------

ULONG Os2SalInfoPrinter::GetCapabilities( const ImplJobSetup*, USHORT nType )
{
	switch ( nType )
	{
		case PRINTER_CAPABILITIES_SUPPORTDIALOG:
			return TRUE;
		case PRINTER_CAPABILITIES_COPIES:
			return 0xFFFF;
		case PRINTER_CAPABILITIES_COLLATECOPIES:
			return 0;
		case PRINTER_CAPABILITIES_SETORIENTATION:
		case PRINTER_CAPABILITIES_SETPAPERBIN:
		case PRINTER_CAPABILITIES_SETPAPERSIZE:
		case PRINTER_CAPABILITIES_SETPAPER:
			return mbDJPSupported;
	}

	return 0;
}

// -----------------------------------------------------------------------

void Os2SalInfoPrinter::GetPageInfo( const ImplJobSetup*,
								  long& rOutWidth, long& rOutHeight,
								  long& rPageOffX, long& rPageOffY,
								  long& rPageWidth, long& rPageHeight )
{
	HDC hDC = mhDC;

	// search current form
	HCINFO	aInfo;
	int nForms = DevQueryHardcopyCaps( hDC, 0, 0, &aInfo );
	for( int i = 0; i < nForms; i++ )
	{
		if ( DevQueryHardcopyCaps( hDC, i, 1, &aInfo ) >= 0 )
		{
			if ( aInfo.flAttributes & HCAPS_CURRENT )
			{
				// query resolution
				long nXResolution;
				long nYResolution;
				DevQueryCaps( hDC, CAPS_HORIZONTAL_RESOLUTION, 1, &nXResolution );
				DevQueryCaps( hDC, CAPS_VERTICAL_RESOLUTION, 1, &nYResolution );
				rPageOffX	= aInfo.xLeftClip * nXResolution / 1000;
				rPageOffY	= (aInfo.cy-aInfo.yTopClip) * nYResolution / 1000;
				rPageWidth	= aInfo.cx * nXResolution / 1000;
				rPageHeight = aInfo.cy * nYResolution / 1000;
				rOutWidth	= aInfo.xPels;
				rOutHeight	= aInfo.yPels;
				return;
			}
		}
	}

	// use device caps if no form selected/found
	long lCapsWidth = 0;
	long lCapsHeight = 0;
	DevQueryCaps( hDC, CAPS_WIDTH, 1L, &lCapsWidth );
	DevQueryCaps( hDC, CAPS_HEIGHT, 1L, &lCapsHeight );
	rPageOffX	 = 0;
	rPageOffY	 = 0;
	rOutWidth	 = lCapsWidth;
	rOutHeight	 = lCapsHeight;
	rPageWidth	 = rOutWidth;
	rPageHeight  = rOutHeight;
}

// =======================================================================

static sal_Bool ImplIsDriverPrintDJPEnabled( HDC hDC )
{
#ifdef NO_DJP
	return FALSE;
#else
	// Ueber OS2-Ini kann DJP disablte werden
	if ( !PrfQueryProfileInt( HINI_PROFILE, SAL_PROFILE_APPNAME, SAL_PROFILE_PRINTDJP, 1 ) )
		return FALSE;

	// Testen, ob DJP-Interface am Drucker vorhanden
	LONG   lQuery;
	APIRET rc;

	lQuery = DEVESC_QUERYSIZE;
	rc = DevEscape( hDC,
					DEVESC_QUERYESCSUPPORT,
					sizeof( lQuery ),
					(PBYTE)&lQuery,
					0,
					(PBYTE)NULL );
	if ( DEV_OK != rc )
		return FALSE;

	return TRUE;
#endif
}

// =======================================================================

SalPrinter* Os2SalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
{
	Os2SalPrinter* pPrinter = new Os2SalPrinter;
	pPrinter->mpInfoPrinter = static_cast<Os2SalInfoPrinter*>(pInfoPrinter);
	return pPrinter;
}

// -----------------------------------------------------------------------

void Os2SalInstance::DestroyPrinter( SalPrinter* pPrinter )
{
	delete pPrinter;
}

// =======================================================================

Os2SalPrinter::Os2SalPrinter()
{
	mhDC					= 0;
	mhPS					= 0;
	mpGraphics			= NULL;
	mbAbort				= FALSE;
	mbPrintDJPSupported	= FALSE;
}

// -----------------------------------------------------------------------

Os2SalPrinter::~Os2SalPrinter()
{
}

// -----------------------------------------------------------------------

sal_Bool Os2SalPrinter::StartJob( const XubString* pFileName,
						   const XubString& rJobName,
						   const XubString& rAppName,
						   ULONG nCopies,
                           bool bCollate,
                           bool bDirect,
						   ImplJobSetup* pSetupData )
{
	DEVOPENSTRUC	aDevOpenStruc;
	LONG			lType;
	APIRET			rc;

	// prepare queue information
	memset( &aDevOpenStruc, 0, sizeof( aDevOpenStruc ) );
	aDevOpenStruc.pszDriverName = (PSZ)(mpInfoPrinter->maDriverName.GetBuffer());

	// print into file?
	if ( pFileName )
	{
		aDevOpenStruc.pszLogAddress = (PSZ)pFileName->GetBuffer();
		aDevOpenStruc.pszDataType = "PM_Q_RAW";
		lType = OD_DIRECT;
	}
	else
	{
		aDevOpenStruc.pszLogAddress = (PSZ)(mpInfoPrinter->maName.GetBuffer());
		if ( PrfQueryProfileInt( HINI_PROFILE, SAL_PROFILE_APPNAME, SAL_PROFILE_PRINTRAW, 0 ) )
			aDevOpenStruc.pszDataType = "PM_Q_RAW";
		else
			aDevOpenStruc.pszDataType = "PM_Q_STD";
		lType = OD_QUEUED;
	}

#if 0 // YD FIXME
	// Set comment (AppName nur bis zum 1. Space-Zeichen nehmen)
	const xub_Unicode*	pComment = rAppName;
	USHORT			nCommentLen = 0;
	memset( maCommentBuf, 0, sizeof( maCommentBuf ) );
	while ( (nCommentLen < 32) &&
			(((*pComment >= 'a') && (*pComment <= 'z')) ||
			 ((*pComment >= 'A') && (*pComment <= 'Z')) ||
			 ((*pComment >= '0') && (*pComment <= '9')) ||
			 (*pComment == '-')))
	{
		maCommentBuf[nCommentLen] = (char)(*pComment);
		nCommentLen++;
		pComment++;
	}
	aDevOpenStruc.pszComment = (PSZ)maCommentBuf;
#endif
	ByteString jobName( rJobName, gsl_getSystemTextEncoding());
	aDevOpenStruc.pszComment = (PSZ)jobName.GetBuffer();

	// Kopien
	if ( nCopies > 1 )
	{
		// OS2 kann maximal 999 Kopien
		if ( nCopies > 999 )
			nCopies = 999;
		sprintf( maCopyBuf, "COP=%d", nCopies);
		aDevOpenStruc.pszQueueProcParams = (PSZ)maCopyBuf;
	}

	// open device context
	SalData*	pSalData = GetSalData();
	HAB 		hAB = pSalData->mhAB;
	aDevOpenStruc.pdriv = (PDRIVDATA)pSetupData->mpDriverData;
	mhDC = DevOpenDC( hAB,
									lType,
									"*",
									7,
									(PDEVOPENDATA)&aDevOpenStruc,
									0 );
	if ( mhDC == 0 )
	{
		ERRORID nLastError = WinGetLastError( hAB );
		if ( (nLastError & 0xFFFF) == PMERR_SPL_PRINT_ABORT )
			mnError = SAL_PRINTER_ERROR_ABORT;
		else
			mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return FALSE;
	}

	// open presentation space
	SIZEL sizel;
	sizel.cx = 0;
	sizel.cy = 0;
	mhPS = Ft2CreatePS( hAB, mhDC, &sizel, GPIA_ASSOC | GPIT_MICRO | PU_PELS );
	if ( !mhPS )
	{
		DevCloseDC( mhDC );
		mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return NULL;
	}

	// Can we print with DJP
	mbPrintDJPSupported = ImplIsDriverPrintDJPEnabled( mhDC );
#if OSL_DEBUG_LEVEL>0
	debug_printf( "mbPrintDJPSupported %d", mbPrintDJPSupported);
#endif

	// JobName ermitteln und Job starten
	PSZ pszJobName = NULL;
	int nJobNameLen = 0;
	if ( jobName.Len() > 0 )
	{
		pszJobName = (PSZ)jobName.GetBuffer();
		nJobNameLen = jobName.Len();
	}
	rc = DevEscape( mhDC,
					mbPrintDJPSupported ? DEVESC_STARTDOC_WPROP : DEVESC_STARTDOC,
					nJobNameLen, (PBYTE)pszJobName,
					&((PDRIVDATA)(pSetupData->mpDriverData))->cb, 
					(PBYTE)(pSetupData->mpDriverData));

	if ( rc != DEV_OK )
	{
		ERRORID nLastError = WinGetLastError( hAB );
		if ( (nLastError & 0xFFFF) == PMERR_SPL_PRINT_ABORT )
			mnError = SAL_PRINTER_ERROR_ABORT;
		else
			mnError = SAL_PRINTER_ERROR_GENERALERROR;
		Ft2Associate( mhPS, NULL );
		Ft2DestroyPS( mhPS );
		DevCloseDC( mhDC );
		return FALSE;
	}

	// init for first page
	mbFirstPage = TRUE;
	mnError = 0;

	return TRUE;
}

// -----------------------------------------------------------------------

sal_Bool Os2SalPrinter::EndJob()
{
	APIRET rc;
	rc = DevEscape( mhDC,
					DEVESC_ENDDOC,
					0, NULL,
					0, NULL);

	// destroy presentation space and device context
	Ft2Associate( mhPS, NULL );
	Ft2DestroyPS( mhPS );
	DevCloseDC( mhDC );
	return TRUE;
}

// -----------------------------------------------------------------------

sal_Bool Os2SalPrinter::AbortJob()
{
	APIRET rc;

	rc = DevEscape( mhDC,
					DEVESC_ABORTDOC,
					0, NULL,
					0, NULL );

	// destroy SalGraphics
	if ( mpGraphics )
	{
		ImplSalDeInitGraphics( mpGraphics );
		delete mpGraphics;
		mpGraphics = NULL;
	}

	// destroy presentation space and device context
	Ft2Associate( mhPS, NULL );
	Ft2DestroyPS( mhPS );
	DevCloseDC( mhDC );
	return TRUE;
}

// -----------------------------------------------------------------------

SalGraphics* Os2SalPrinter::StartPage( ImplJobSetup* pSetupData, sal_Bool bNewJobSetup )
{
	APIRET rc;

#if OSL_DEBUG_LEVEL>0
	debug_printf( "Os2SalPrinter::StartPage mhDC %x, mbFirstPage %d, bNewJobSetup %d", 
				  mhDC, mbFirstPage, bNewJobSetup);
	debug_printf( "Os2SalPrinter::StartPage pSetupData %x", 
				  pSetupData);
#endif

	if ( mbFirstPage )
		mbFirstPage = FALSE;
	else
	{
		PBYTE	pJobData;
		LONG	nJobDataSize;
		LONG	nEscape;
		if ( mbPrintDJPSupported && bNewJobSetup )
		{
			nEscape 		= DEVESC_NEWFRAME_WPROP;
			nJobDataSize	= ((PDRIVDATA)(pSetupData->mpDriverData))->cb;
			pJobData		= (PBYTE)(pSetupData->mpDriverData);
		}
		else
		{
			nEscape 		= DEVESC_NEWFRAME;
			nJobDataSize	= 0;
			pJobData		= NULL;
		}
		rc = DevEscape( mhDC,
						nEscape,
						0, NULL,
						&nJobDataSize, pJobData );

		if ( rc != DEV_OK )
		{
			DevEscape( mhDC, DEVESC_ENDDOC, 0, NULL, 0, NULL);
			Ft2Associate( mhPS, NULL );
			Ft2DestroyPS( mhPS );
			DevCloseDC( mhDC );
			mnError = SAL_PRINTER_ERROR_GENERALERROR;
			return NULL;
		}
	}

	// create SalGraphics with copy of hPS
	Os2SalGraphics* pGraphics = new Os2SalGraphics;
	pGraphics->mhDC				= mhDC;
	pGraphics->mhPS				= mhPS;
	pGraphics->mhWnd 			= 0;
	pGraphics->mbPrinter 		= TRUE;
	pGraphics->mbVirDev			= FALSE;
	pGraphics->mbWindow			= FALSE;
	pGraphics->mbScreen			= FALSE;
	pGraphics->mnHeight			= 0;
	// search current form for actual page height
	HCINFO	aInfo;
	int 	nForms = DevQueryHardcopyCaps( mhDC, 0, 0, &aInfo );
	for( int i = 0; i < nForms; i++ )
	{
		if ( DevQueryHardcopyCaps( mhDC, i, 1, &aInfo ) >= 0 )
		{
			if ( aInfo.flAttributes & HCAPS_CURRENT )
				pGraphics->mnHeight	= aInfo.yPels;
		}
	}
	// use device caps if no form selected/found
	if ( !pGraphics->mnHeight )
		DevQueryCaps( mhDC, CAPS_HEIGHT, 1L, &pGraphics->mnHeight );

	ImplSalInitGraphics( pGraphics );
	mpGraphics = pGraphics;

	return pGraphics;
}

// -----------------------------------------------------------------------

sal_Bool Os2SalPrinter::EndPage()
{
#if OSL_DEBUG_LEVEL>0
	debug_printf( "Os2SalPrinter::EndPage mhDC %x", mhDC);
#endif

	if ( mpGraphics )
	{
		// destroy SalGraphics
		ImplSalDeInitGraphics( mpGraphics );
		delete mpGraphics;
		mpGraphics = NULL;
	}

	return TRUE;
}

// -----------------------------------------------------------------------

ULONG Os2SalPrinter::GetErrorCode()
{
	return mnError;
}
