/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <string.h>

#include <osl/module.h>

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

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

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

#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
#include <com/sun/star/ui/dialogs/XFilePicker.hpp>
#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <comphelper/processfactory.hxx>

#include <malloc.h>

#ifdef __MINGW32__
#define CATCH_DRIVER_EX_BEGIN                                               \
    jmp_buf jmpbuf;                                                         \
    __SEHandler han;                                                        \
    if (__builtin_setjmp(jmpbuf) == 0)                                      \
    {                                                                       \
        han.Set(jmpbuf, NULL, (__SEHandler::PF)EXCEPTION_EXECUTE_HANDLER)
        
#define CATCH_DRIVER_EX_END(mes, p)                                         \
    }                                                                       \
    han.Reset()
#define CATCH_DRIVER_EX_END_2(mes)                                            \
    }                                                                       \
    han.Reset()
#else
#define CATCH_DRIVER_EX_BEGIN                                               \
    __try                                                                   \
    {
#define CATCH_DRIVER_EX_END(mes, p)                                         \
    }                                                                       \
    __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
    {                                                                       \
        DBG_ERROR( mes );                                                   \
        p->markInvalid();                                                   \
    }
#define CATCH_DRIVER_EX_END_2(mes)                                         \
    }                                                                       \
    __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
    {                                                                       \
        DBG_ERROR( mes );                                                   \
    }
#endif


using namespace com::sun::star;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star::ui::dialogs;
using namespace rtl;

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

static char aImplWindows[] = "windows";
static char aImplDevices[] = "devices";
static char aImplDevice[]  = "device";

static LPDEVMODEA SAL_DEVMODE_A( const ImplJobSetup* pSetupData )
{
    LPDEVMODEA pRet = NULL;
    SalDriverData* pDrv = (SalDriverData*)pSetupData->mpDriverData;
    if( pDrv->mnVersion == SAL_DRIVERDATA_VERSION_A &&
        pSetupData->mnDriverDataLen >= sizeof(DEVMODEA)+sizeof(SalDriverData)-1
        )
        pRet = ((LPDEVMODEA)((pSetupData->mpDriverData) + (pDrv->mnDriverOffset)));
    return pRet;
}

static LPDEVMODEW SAL_DEVMODE_W( const ImplJobSetup* pSetupData )
{
    LPDEVMODEW pRet = NULL;
    SalDriverData* pDrv = (SalDriverData*)pSetupData->mpDriverData;
    if( pDrv->mnVersion == SAL_DRIVERDATA_VERSION_W &&
        pSetupData->mnDriverDataLen >= sizeof(DEVMODEW)+sizeof(SalDriverData)-1
        )
        pRet = ((LPDEVMODEW)((pSetupData->mpDriverData) + (pDrv->mnDriverOffset)));
    return pRet;
}

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

static sal_uLong ImplWinQueueStatusToSal( DWORD nWinStatus )
{
	sal_uLong nStatus = 0;
	if ( nWinStatus & PRINTER_STATUS_PAUSED )
		nStatus |= QUEUE_STATUS_PAUSED;
	if ( nWinStatus & PRINTER_STATUS_ERROR )
		nStatus |= QUEUE_STATUS_ERROR;
	if ( nWinStatus & PRINTER_STATUS_PENDING_DELETION )
		nStatus |= QUEUE_STATUS_PENDING_DELETION;
	if ( nWinStatus & PRINTER_STATUS_PAPER_JAM )
		nStatus |= QUEUE_STATUS_PAPER_JAM;
	if ( nWinStatus & PRINTER_STATUS_PAPER_OUT )
		nStatus |= QUEUE_STATUS_PAPER_OUT;
	if ( nWinStatus & PRINTER_STATUS_MANUAL_FEED )
		nStatus |= QUEUE_STATUS_MANUAL_FEED;
	if ( nWinStatus & PRINTER_STATUS_PAPER_PROBLEM )
		nStatus |= QUEUE_STATUS_PAPER_PROBLEM;
	if ( nWinStatus & PRINTER_STATUS_OFFLINE )
		nStatus |= QUEUE_STATUS_OFFLINE;
	if ( nWinStatus & PRINTER_STATUS_IO_ACTIVE )
		nStatus |= QUEUE_STATUS_IO_ACTIVE;
	if ( nWinStatus & PRINTER_STATUS_BUSY )
		nStatus |= QUEUE_STATUS_BUSY;
	if ( nWinStatus & PRINTER_STATUS_PRINTING )
		nStatus |= QUEUE_STATUS_PRINTING;
	if ( nWinStatus & PRINTER_STATUS_OUTPUT_BIN_FULL )
		nStatus |= QUEUE_STATUS_OUTPUT_BIN_FULL;
	if ( nWinStatus & PRINTER_STATUS_WAITING )
		nStatus |= QUEUE_STATUS_WAITING;
	if ( nWinStatus & PRINTER_STATUS_PROCESSING )
		nStatus |= QUEUE_STATUS_PROCESSING;
	if ( nWinStatus & PRINTER_STATUS_INITIALIZING )
		nStatus |= QUEUE_STATUS_INITIALIZING;
	if ( nWinStatus & PRINTER_STATUS_WARMING_UP )
		nStatus |= QUEUE_STATUS_WARMING_UP;
	if ( nWinStatus & PRINTER_STATUS_TONER_LOW )
		nStatus |= QUEUE_STATUS_TONER_LOW;
	if ( nWinStatus & PRINTER_STATUS_NO_TONER )
		nStatus |= QUEUE_STATUS_NO_TONER;
	if ( nWinStatus & PRINTER_STATUS_PAGE_PUNT )
		nStatus |= QUEUE_STATUS_PAGE_PUNT;
	if ( nWinStatus & PRINTER_STATUS_USER_INTERVENTION )
		nStatus |= QUEUE_STATUS_USER_INTERVENTION;
	if ( nWinStatus & PRINTER_STATUS_OUT_OF_MEMORY )
		nStatus |= QUEUE_STATUS_OUT_OF_MEMORY;
	if ( nWinStatus & PRINTER_STATUS_DOOR_OPEN )
		nStatus |= QUEUE_STATUS_DOOR_OPEN;
	if ( nWinStatus & PRINTER_STATUS_SERVER_UNKNOWN )
		nStatus |= QUEUE_STATUS_SERVER_UNKNOWN;
	if ( nWinStatus & PRINTER_STATUS_POWER_SAVE )
		nStatus |= QUEUE_STATUS_POWER_SAVE;
	if ( !nStatus && !(nWinStatus & PRINTER_STATUS_NOT_AVAILABLE) )
		nStatus |= QUEUE_STATUS_READY;
	return nStatus;
}

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

static void getPrinterQueueInfoOldStyle( ImplPrnQueueList* pList )
{
	DWORD			i;
	DWORD			n;
	DWORD			nBytes = 0;
	DWORD			nInfoPrn2;
	sal_Bool			bFound = FALSE;
	PRINTER_INFO_2* pWinInfo2 = NULL;
	PRINTER_INFO_2* pGetInfo2;
	EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, NULL, 0, &nBytes, &nInfoPrn2 );
	if ( nBytes )
	{
		pWinInfo2 = (PRINTER_INFO_2*) rtl_allocateMemory( nBytes );
		if ( EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes, &nInfoPrn2 ) )
		{
			pGetInfo2 = pWinInfo2;
			for ( i = 0; i < nInfoPrn2; i++ )
			{
				SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
				pInfo->maPrinterName = ImplSalGetUniString( pGetInfo2->pPrinterName );
				pInfo->maDriver 	 = ImplSalGetUniString( pGetInfo2->pDriverName );
				XubString aPortName;
				if ( pGetInfo2->pPortName )
					aPortName = ImplSalGetUniString( pGetInfo2->pPortName );
				// pLocation can be 0 (the Windows docu doesn't describe this)
				if ( pGetInfo2->pLocation && strlen( pGetInfo2->pLocation ) )
					pInfo->maLocation = ImplSalGetUniString( pGetInfo2->pLocation );
				else
					pInfo->maLocation = aPortName;
				// pComment can be 0 (the Windows docu doesn't describe this)
				if ( pGetInfo2->pComment )
					pInfo->maComment = ImplSalGetUniString( pGetInfo2->pComment );
				pInfo->mnStatus 	 = ImplWinQueueStatusToSal( pGetInfo2->Status );
				pInfo->mnJobs		 = pGetInfo2->cJobs;
				pInfo->mpSysData	 = new XubString( aPortName );
				pList->Add( pInfo );
				pGetInfo2++;
			}

			bFound = TRUE;
		}
	}

	// read printers from win.ini
	// TODO: MSDN: GetProfileString() should not be called from server
	// code because it is just there for WIN16 compatibility
	UINT	nSize = 4096;
	char*	pBuf = new char[nSize];
	UINT	nRead = GetProfileStringA( aImplDevices, NULL, "", pBuf, nSize );
	while ( nRead >= nSize-2 )
	{
		nSize += 2048;
		delete []pBuf;
		pBuf = new char[nSize];
		nRead = GetProfileStringA( aImplDevices, NULL, "", pBuf, nSize );
	}

	// extract printer names from buffer and fill list
	char* pName = pBuf;
	while ( *pName )
	{
		char*	pPortName;
		char*	pTmp;
		char	aPortBuf[256];
		GetProfileStringA( aImplDevices, pName, "", aPortBuf, sizeof( aPortBuf ) );

		pPortName = aPortBuf;

		// create name
		xub_StrLen nNameLen = sal::static_int_cast<xub_StrLen>(strlen( pName ));
		XubString aName( ImplSalGetUniString( pName, nNameLen ) );

		// get driver name
		pTmp = pPortName;
		while ( *pTmp != ',' )
			pTmp++;
		XubString aDriver( ImplSalGetUniString( pPortName, (sal_uInt16)(pTmp-pPortName) ) );
		pPortName = pTmp;

		// get port names
		do
		{
			pPortName++;
			pTmp = pPortName;
			while ( *pTmp && (*pTmp != ',') )
				pTmp++;

			String aPortName( ImplSalGetUniString( pPortName, (sal_uInt16)(pTmp-pPortName) ) );

			// create new entry
            // look up if printer was already found in first loop
			sal_Bool bAdd = TRUE;
			if ( pWinInfo2 )
			{
				pGetInfo2 = pWinInfo2;
				for ( n = 0; n < nInfoPrn2; n++ )
				{
					if ( aName.EqualsIgnoreCaseAscii( pGetInfo2->pPrinterName ) )
					{
						bAdd = FALSE;
						break;
					}
					pGetInfo2++;
				}
			}
			// if it's a new printer, add it
			if ( bAdd )
			{
				SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
				pInfo->maPrinterName = aName;
				pInfo->maDriver 	 = aDriver;
				pInfo->maLocation	 = aPortName;
				pInfo->mnStatus 	 = 0;
				pInfo->mnJobs		 = QUEUE_JOBS_DONTKNOW;
				pInfo->mpSysData	 = new XubString( aPortName );
				pList->Add( pInfo );
			}
		}
		while ( *pTmp == ',' );

		pName += nNameLen + 1;
	}

	delete []pBuf;
	rtl_freeMemory( pWinInfo2 );
}

void WinSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
{
    if( ! aSalShlData.mbWPrinter )
    {
        getPrinterQueueInfoOldStyle( pList );
        return;
    }
	DWORD			i;
	DWORD			nBytes = 0;
	DWORD			nInfoPrn4 = 0;
	PRINTER_INFO_4W* pWinInfo4 = NULL;
	EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, NULL, 0, &nBytes, &nInfoPrn4 );
	if ( nBytes )
	{
		pWinInfo4 = (PRINTER_INFO_4W*) rtl_allocateMemory( nBytes );
		if ( EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, NULL, 4, (LPBYTE)pWinInfo4, nBytes, &nBytes, &nInfoPrn4 ) )
		{
			for ( i = 0; i < nInfoPrn4; i++ )
			{
				SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
				pInfo->maPrinterName = UniString( reinterpret_cast< const sal_Unicode* >(pWinInfo4[i].pPrinterName) );
				pInfo->mnStatus 	 = 0;
				pInfo->mnJobs		 = 0;
				pInfo->mpSysData	 = NULL;
				pList->Add( pInfo );
			}
		}
        rtl_freeMemory( pWinInfo4 );
	}
}

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

static void getPrinterQueueStateOldStyle( SalPrinterQueueInfo* pInfo )
{
	DWORD				nBytes = 0;
	DWORD				nInfoRet;
	PRINTER_INFO_2* 	pWinInfo2;
	EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, NULL, 0, &nBytes, &nInfoRet );
	if ( nBytes )
	{
		pWinInfo2 = (PRINTER_INFO_2*) rtl_allocateMemory( nBytes );
		if ( EnumPrintersA( PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes, &nInfoRet ) )
		{
			PRINTER_INFO_2* pGetInfo2 = pWinInfo2;
			for ( DWORD i = 0; i < nInfoRet; i++ )
			{
				if ( pInfo->maPrinterName.EqualsAscii( pGetInfo2->pPrinterName ) &&
					 ( pInfo->maDriver.Len() == 0 ||
                       pInfo->maDriver.EqualsAscii( pGetInfo2->pDriverName ) )
                       )
				{
                    XubString aPortName;
                    if ( pGetInfo2->pPortName )
                        aPortName = ImplSalGetUniString( pGetInfo2->pPortName );
                    // pLocation can be 0 (the Windows docu doesn't describe this)
                    if ( pGetInfo2->pLocation && strlen( pGetInfo2->pLocation ) )
                        pInfo->maLocation = ImplSalGetUniString( pGetInfo2->pLocation );
                    else
                        pInfo->maLocation = aPortName;
                    // pComment can be 0 (the Windows docu doesn't describe this)
                    if ( pGetInfo2->pComment )
                        pInfo->maComment = ImplSalGetUniString( pGetInfo2->pComment );
                    pInfo->mnStatus 	 = ImplWinQueueStatusToSal( pGetInfo2->Status );
                    pInfo->mnJobs		 = pGetInfo2->cJobs;
                    if( ! pInfo->mpSysData )
                        pInfo->mpSysData	 = new XubString( aPortName );
					break;
				}

				pGetInfo2++;
			}
		}

		rtl_freeMemory( pWinInfo2 );
	}
}

void WinSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo )
{
    if( ! aSalShlData.mbWPrinter )
    {
        getPrinterQueueStateOldStyle( pInfo );
        return;
    }
    
    HANDLE hPrinter = 0;
    LPWSTR pPrnName = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pInfo->maPrinterName.GetBuffer()));
    if( OpenPrinterW( pPrnName, &hPrinter, NULL ) )
    {
        DWORD				nBytes = 0;
        GetPrinterW( hPrinter, 2, NULL, 0, &nBytes );
        if( nBytes )
        {
            PRINTER_INFO_2W* pWinInfo2 = (PRINTER_INFO_2W*)rtl_allocateMemory(nBytes);
            if( GetPrinterW( hPrinter, 2, (LPBYTE)pWinInfo2, nBytes, &nBytes ) )
            {
                if( pWinInfo2->pDriverName )
                    pInfo->maDriver = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pDriverName) );
                XubString aPortName;
                if ( pWinInfo2->pPortName )
                    aPortName = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pPortName) );
                // pLocation can be 0 (the Windows docu doesn't describe this)
                if ( pWinInfo2->pLocation && *pWinInfo2->pLocation )
                    pInfo->maLocation = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pLocation) );
                else
                    pInfo->maLocation = aPortName;
                // pComment can be 0 (the Windows docu doesn't describe this)
                if ( pWinInfo2->pComment )
                    pInfo->maComment = String( reinterpret_cast< const sal_Unicode* >(pWinInfo2->pComment) );
                pInfo->mnStatus 	 = ImplWinQueueStatusToSal( pWinInfo2->Status );
                pInfo->mnJobs		 = pWinInfo2->cJobs;
                if( ! pInfo->mpSysData )
                    pInfo->mpSysData	 = new XubString( aPortName );
            }
            rtl_freeMemory(pWinInfo2);
        }
        ClosePrinter( hPrinter );
    }
}

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

void WinSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
{
	delete (String*)(pInfo->mpSysData);
	delete pInfo;
}

// -----------------------------------------------------------------------
XubString WinSalInstance::GetDefaultPrinter()
{
    static bool bGetDefPrtAPI = true;
    static sal_Bool(WINAPI*pGetDefaultPrinter)(LPWSTR,LPDWORD) = NULL;
    // try to use GetDefaultPrinter API (not available prior to W2000)
    if( bGetDefPrtAPI )
    {
        bGetDefPrtAPI = false;
        // check for W2k and XP
        if( aSalShlData.maVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && aSalShlData.maVersionInfo.dwMajorVersion >= 5 )
        {
            oslModule pLib = osl_loadAsciiModule( "winspool.drv", SAL_LOADMODULE_DEFAULT );
            oslGenericFunction pFunc = NULL;
            if( pLib )
                pFunc = osl_getAsciiFunctionSymbol( pLib, "GetDefaultPrinterW" );

            pGetDefaultPrinter = (sal_Bool(WINAPI*)(LPWSTR,LPDWORD)) pFunc;
        }
    }
    if( pGetDefaultPrinter )
    {
        DWORD   nChars = 0;
        pGetDefaultPrinter( NULL, &nChars );
        if( nChars )
        {
            LPWSTR  pStr = (LPWSTR)rtl_allocateMemory(nChars*sizeof(WCHAR));
            XubString aDefPrt;
            if( pGetDefaultPrinter( pStr, &nChars ) )
            {
                aDefPrt = reinterpret_cast<sal_Unicode* >(pStr);
            }
            rtl_freeMemory( pStr );
            if( aDefPrt.Len() )
                return aDefPrt;
        }
    }

	// get default printer from win.ini
	char szBuffer[256];
	GetProfileStringA( aImplWindows, aImplDevice, "", szBuffer, sizeof( szBuffer ) );
	if ( szBuffer[0] )
	{
		// Printername suchen
		char* pBuf = szBuffer;
		char* pTmp = pBuf;
		while ( *pTmp && (*pTmp != ',') )
			pTmp++;
		return ImplSalGetUniString( pBuf, (xub_StrLen)(pTmp-pBuf) );
	}
	else
		return XubString();
}

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

static DWORD ImplDeviceCaps( WinSalInfoPrinter* pPrinter, WORD nCaps,
							 BYTE* pOutput, const ImplJobSetup* pSetupData )
{
    if( aSalShlData.mbWPrinter )
    {
        DEVMODEW* pDevMode;
        if ( !pSetupData || !pSetupData->mpDriverData )
            pDevMode = NULL;
        else
            pDevMode = SAL_DEVMODE_W( pSetupData );
    
        return DeviceCapabilitiesW( reinterpret_cast<LPCWSTR>(pPrinter->maDeviceName.GetBuffer()),
                                    reinterpret_cast<LPCWSTR>(pPrinter->maPortName.GetBuffer()),
                                    nCaps, (LPWSTR)pOutput, pDevMode );
    }
    else
    {
        DEVMODEA* pDevMode;
        if ( !pSetupData || !pSetupData->mpDriverData )
            pDevMode = NULL;
        else
            pDevMode = SAL_DEVMODE_A( pSetupData );
    
        return DeviceCapabilitiesA( ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
                                ImplSalGetWinAnsiString( pPrinter->maPortName, TRUE ).GetBuffer(),
                                nCaps, (LPSTR)pOutput, pDevMode );
    }
}

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

static sal_Bool ImplTestSalJobSetup( WinSalInfoPrinter* pPrinter,
								 ImplJobSetup* pSetupData, sal_Bool bDelete )
{
	if ( pSetupData && pSetupData->mpDriverData )
	{
        // signature and size must fit to avoid using
        // JobSetups from a wrong system

        // initialize versions from jobsetup
        // those will be overwritten with driver's version
        DEVMODEA* pDevModeA = NULL;
        DEVMODEW* pDevModeW = NULL;
        LONG dmSpecVersion = -1;
        LONG dmDriverVersion = -1;
        SalDriverData* pSalDriverData = (SalDriverData*)pSetupData->mpDriverData;
        BYTE* pDriverData = ((BYTE*)pSalDriverData) + pSalDriverData->mnDriverOffset;
        if( pSalDriverData->mnVersion == SAL_DRIVERDATA_VERSION_W )
        {
            if( aSalShlData.mbWPrinter )
                pDevModeW = (DEVMODEW*)pDriverData;
        }
        else if( pSalDriverData->mnVersion == SAL_DRIVERDATA_VERSION_A )
        {
            if( ! aSalShlData.mbWPrinter )
                pDevModeA = (DEVMODEA*)pDriverData;
        }

        long nSysJobSize = -1;
        if( pPrinter && ( pDevModeA || pDevModeW ) )
        {
            // just too many driver crashes in that area -> check the dmSpecVersion and dmDriverVersion fields always !!!
            // this prevents using the jobsetup between different Windows versions (eg from XP to 9x) but we
            // can avoid potential driver crashes as their jobsetups are often not compatible
            // #110800#, #111151#, #112381#, #i16580#, #i14173# and perhaps #112375#
            ByteString aPrinterNameA= ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE );
            HANDLE hPrn;
            LPWSTR pPrinterNameW = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pPrinter->maDeviceName.GetBuffer()));
            if ( ! aSalShlData.mbWPrinter )
            {
                if ( !OpenPrinterA( (LPSTR)aPrinterNameA.GetBuffer(), &hPrn, NULL ) )
                    return FALSE;
            }
            else
                if ( !OpenPrinterW( pPrinterNameW, &hPrn, NULL ) )
                    return FALSE;

            // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
            if( hPrn == HGDI_ERROR )
                return FALSE;

            if( aSalShlData.mbWPrinter )
            {
                nSysJobSize = DocumentPropertiesW( 0, hPrn,
                                                   pPrinterNameW,
                                                   NULL, NULL, 0 );
            }
            else
            {
                nSysJobSize = DocumentPropertiesA( 0, hPrn,
                                                   (LPSTR)aPrinterNameA.GetBuffer(),
                                                   NULL, NULL, 0 );
            }
            
            if( nSysJobSize < 0 )
            {
                ClosePrinter( hPrn );
                return FALSE;
            }
            BYTE *pBuffer = (BYTE*)_alloca( nSysJobSize );
            LONG nRet = -1;
            if( aSalShlData.mbWPrinter )
            {
                nRet = DocumentPropertiesW( 0, hPrn,
                                            pPrinterNameW,
                                            (LPDEVMODEW)pBuffer, NULL, DM_OUT_BUFFER );
            }
            else
            {
                nRet = DocumentPropertiesA( 0, hPrn,
                                            (LPSTR)aPrinterNameA.GetBuffer(),
                                            (LPDEVMODEA)pBuffer, NULL, DM_OUT_BUFFER );
            }
            if( nRet < 0 )
            {
                ClosePrinter( hPrn );
                return FALSE;
            }

            // the spec version differs between the windows platforms, ie 98,NT,2000/XP
            // this allows us to throw away printer settings from other platforms that might crash a buggy driver
            // we check the driver version as well
            dmSpecVersion = aSalShlData.mbWPrinter ? ((DEVMODEW*)pBuffer)->dmSpecVersion : ((DEVMODEA*)pBuffer)->dmSpecVersion;
            dmDriverVersion = aSalShlData.mbWPrinter ? ((DEVMODEW*)pBuffer)->dmDriverVersion : ((DEVMODEA*)pBuffer)->dmDriverVersion;

            ClosePrinter( hPrn );
        }
        SalDriverData* pSetupDriverData = (SalDriverData*)(pSetupData->mpDriverData);
		if ( (pSetupData->mnSystem == JOBSETUP_SYSTEM_WINDOWS) &&
             (pPrinter->maDriverName == pSetupData->maDriver) &&
			 (pSetupData->mnDriverDataLen > sizeof( SalDriverData )) &&
             (long)(pSetupData->mnDriverDataLen - pSetupDriverData->mnDriverOffset) == nSysJobSize &&
			 pSetupDriverData->mnSysSignature == SAL_DRIVERDATA_SYSSIGN )
        {
            if( pDevModeA &&
                (dmSpecVersion == pDevModeA->dmSpecVersion) &&
                (dmDriverVersion == pDevModeA->dmDriverVersion) )
                return TRUE;
            if( pDevModeW &&
                (dmSpecVersion == pDevModeW->dmSpecVersion) &&
                (dmDriverVersion == pDevModeW->dmDriverVersion) )
                return TRUE;
        }
		if ( bDelete )
		{
			rtl_freeMemory( pSetupData->mpDriverData );
			pSetupData->mpDriverData = NULL;
			pSetupData->mnDriverDataLen = 0;
		}
	}

	return FALSE;
}

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

static sal_Bool ImplUpdateSalJobSetup( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData,
								   sal_Bool bIn, WinSalFrame* pVisibleDlgParent )
{
    ByteString aPrinterNameA = ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE );
    HANDLE hPrn;
    LPWSTR pPrinterNameW = reinterpret_cast<LPWSTR>(const_cast<sal_Unicode*>(pPrinter->maDeviceName.GetBuffer()));
    if( aSalShlData.mbWPrinter )
    {
        if ( !OpenPrinterW( pPrinterNameW, &hPrn, NULL ) )
            return FALSE;
    }
    else
    {
        if ( !OpenPrinterA( (LPSTR)aPrinterNameA.GetBuffer(), &hPrn, NULL ) )
            return FALSE;
    }
    // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
    if( hPrn == HGDI_ERROR )
        return FALSE;

	LONG			nRet;
	LONG			nSysJobSize = -1;
	HWND			hWnd = 0;
	DWORD			nMode = DM_OUT_BUFFER;
	sal_uLong			nDriverDataLen = 0;
	SalDriverData*	pOutBuffer = NULL;
    BYTE*           pInBuffer = NULL;

    if( aSalShlData.mbWPrinter )
    {
        nSysJobSize = DocumentPropertiesW( hWnd, hPrn,
                                           pPrinterNameW,
                                           NULL, NULL, 0 );
    }
    else
        nSysJobSize = DocumentPropertiesA( hWnd, hPrn,
                                           (LPSTR)ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
                                           NULL, NULL, 0 );
	if ( nSysJobSize < 0 )
	{
		ClosePrinter( hPrn );
		return FALSE;
	}

	// Outputbuffer anlegen
	nDriverDataLen				= sizeof(SalDriverData) + nSysJobSize-1;
	pOutBuffer					= (SalDriverData*)rtl_allocateZeroMemory( nDriverDataLen );
	pOutBuffer->mnSysSignature	= SAL_DRIVERDATA_SYSSIGN;
	pOutBuffer->mnVersion		= aSalShlData.mbWPrinter ? SAL_DRIVERDATA_VERSION_W : SAL_DRIVERDATA_VERSION_A;
    // calculate driver data offset including structure padding
	pOutBuffer->mnDriverOffset	= sal::static_int_cast<sal_uInt16>(
                                    (char*)pOutBuffer->maDriverData -
                                    (char*)pOutBuffer );

	// Testen, ob wir einen geeigneten Inputbuffer haben
	if ( bIn && ImplTestSalJobSetup( pPrinter, pSetupData, FALSE ) )
	{
		pInBuffer = (BYTE*)pSetupData->mpDriverData + ((SalDriverData*)pSetupData->mpDriverData)->mnDriverOffset;
		nMode |= DM_IN_BUFFER;
	}

	// Testen, ob Dialog angezeigt werden soll
	if ( pVisibleDlgParent )
	{
		hWnd = pVisibleDlgParent->mhWnd;
		nMode |= DM_IN_PROMPT;
	}

	// Release mutex, in the other case we don't get paints and so on
    sal_uLong nMutexCount=0;
    if ( pVisibleDlgParent )
        nMutexCount = ImplSalReleaseYieldMutex();
    
    BYTE* pOutDevMode = (((BYTE*)pOutBuffer) + pOutBuffer->mnDriverOffset);
    if( aSalShlData.mbWPrinter )
    {
        nRet = DocumentPropertiesW( hWnd, hPrn,
                                    pPrinterNameW,
                                    (LPDEVMODEW)pOutDevMode, (LPDEVMODEW)pInBuffer, nMode );
    }
    else
    {
        nRet = DocumentPropertiesA( hWnd, hPrn,
                                    (LPSTR)ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ).GetBuffer(),
                                    (LPDEVMODEA)pOutDevMode, (LPDEVMODEA)pInBuffer, nMode );
    }
    if ( pVisibleDlgParent )
        ImplSalAcquireYieldMutex( nMutexCount );
	ClosePrinter( hPrn );

	if( (nRet < 0) || (pVisibleDlgParent && (nRet == IDCANCEL)) )
	{
		rtl_freeMemory( pOutBuffer );
		return FALSE;
	}

    // fill up string buffers with 0 so they do not influence a JobSetup's memcmp
    if( aSalShlData.mbWPrinter )
    {
        if( ((LPDEVMODEW)pOutDevMode)->dmSize >= 64 )
        {
            sal_Int32 nLen = rtl_ustr_getLength( (const sal_Unicode*)((LPDEVMODEW)pOutDevMode)->dmDeviceName );
            if ( nLen < sizeof( ((LPDEVMODEW)pOutDevMode)->dmDeviceName )/sizeof(sal_Unicode) )
                memset( ((LPDEVMODEW)pOutDevMode)->dmDeviceName+nLen, 0, sizeof( ((LPDEVMODEW)pOutDevMode)->dmDeviceName )-(nLen*sizeof(sal_Unicode)) );
        }
        if( ((LPDEVMODEW)pOutDevMode)->dmSize >= 166 )
        {
            sal_Int32 nLen = rtl_ustr_getLength( (const sal_Unicode*)((LPDEVMODEW)pOutDevMode)->dmFormName );
            if ( nLen < sizeof( ((LPDEVMODEW)pOutDevMode)->dmFormName )/sizeof(sal_Unicode) )
                memset( ((LPDEVMODEW)pOutDevMode)->dmFormName+nLen, 0, sizeof( ((LPDEVMODEW)pOutDevMode)->dmFormName )-(nLen*sizeof(sal_Unicode)) );
        }
    }
    else
    {
        if( ((LPDEVMODEA)pOutDevMode)->dmSize >= 32 )
        {
            sal_Int32 nLen = strlen( (const char*)((LPDEVMODEA)pOutDevMode)->dmDeviceName );
            if ( nLen < sizeof( ((LPDEVMODEA)pOutDevMode)->dmDeviceName ) )
                memset( ((LPDEVMODEA)pOutDevMode)->dmDeviceName+nLen, 0, sizeof( ((LPDEVMODEA)pOutDevMode)->dmDeviceName )-nLen );
        }
        if( ((LPDEVMODEA)pOutDevMode)->dmSize >= 102 )
        {
            sal_Int32 nLen = strlen( (const char*)((LPDEVMODEA)pOutDevMode)->dmFormName );
            if ( nLen < sizeof( ((LPDEVMODEA)pOutDevMode)->dmFormName ) )
                memset( ((LPDEVMODEA)pOutDevMode)->dmFormName+nLen, 0, sizeof( ((LPDEVMODEA)pOutDevMode)->dmFormName )-nLen );
        }
    }

	// update data
	if ( pSetupData->mpDriverData )
		rtl_freeMemory( pSetupData->mpDriverData );
	pSetupData->mnDriverDataLen = nDriverDataLen;
	pSetupData->mpDriverData	= (BYTE*)pOutBuffer;
	pSetupData->mnSystem		= JOBSETUP_SYSTEM_WINDOWS;

	return TRUE;
}

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

#define DECLARE_DEVMODE( i )\
    DEVMODEA* pDevModeA = SAL_DEVMODE_A(i);\
    DEVMODEW* pDevModeW = SAL_DEVMODE_W(i);\
    if( pDevModeA == NULL && pDevModeW == NULL )\
        return

#define CHOOSE_DEVMODE(i)\
    (pDevModeW ? pDevModeW->i : pDevModeA->i)

static void ImplDevModeToJobSetup( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData, sal_uLong nFlags )
{
	if ( !pSetupData || !pSetupData->mpDriverData )
		return;

    DECLARE_DEVMODE( pSetupData );

	// Orientation
	if ( nFlags & SAL_JOBSET_ORIENTATION )
	{
		if ( CHOOSE_DEVMODE(dmOrientation) == DMORIENT_PORTRAIT )
			pSetupData->meOrientation = ORIENTATION_PORTRAIT;
		else if ( CHOOSE_DEVMODE(dmOrientation) == DMORIENT_LANDSCAPE )
			pSetupData->meOrientation = ORIENTATION_LANDSCAPE;
	}

	// PaperBin
	if ( nFlags & SAL_JOBSET_PAPERBIN )
	{
		sal_uLong nCount = ImplDeviceCaps( pPrinter, DC_BINS, NULL, pSetupData );

		if ( nCount && (nCount != GDI_ERROR) )
		{
			WORD* pBins = (WORD*)rtl_allocateZeroMemory( nCount*sizeof(WORD) );
			ImplDeviceCaps( pPrinter, DC_BINS, (BYTE*)pBins, pSetupData );
			pSetupData->mnPaperBin = 0;

			// search the right bin and assign index to mnPaperBin
			for( sal_uLong i = 0; i < nCount; i++ )
			{
				if( CHOOSE_DEVMODE(dmDefaultSource) == pBins[ i ] )
				{
					pSetupData->mnPaperBin = (sal_uInt16)i;
					break;
				}
			}

			rtl_freeMemory( pBins );
		}
	}

	// PaperSize
	if ( nFlags & SAL_JOBSET_PAPERSIZE )
	{
		if( (CHOOSE_DEVMODE(dmFields) & (DM_PAPERWIDTH|DM_PAPERLENGTH)) == (DM_PAPERWIDTH|DM_PAPERLENGTH) )
		{
		    pSetupData->mnPaperWidth  = CHOOSE_DEVMODE(dmPaperWidth)*10;
		    pSetupData->mnPaperHeight = CHOOSE_DEVMODE(dmPaperLength)*10;
		}
		else
		{
			sal_uLong	nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, NULL, pSetupData );
			WORD*	pPapers = NULL;
			sal_uLong	nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, NULL, pSetupData );
			POINT*	pPaperSizes = NULL;
			if ( nPaperCount && (nPaperCount != GDI_ERROR) )
			{
				pPapers = (WORD*)rtl_allocateZeroMemory(nPaperCount*sizeof(WORD));
				ImplDeviceCaps( pPrinter, DC_PAPERS, (BYTE*)pPapers, pSetupData );
			}
			if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
			{
				pPaperSizes = (POINT*)rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT));
				ImplDeviceCaps( pPrinter, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );
			}
			if( nPaperSizeCount == nPaperCount && pPaperSizes && pPapers )
			{
				for( sal_uLong i = 0; i < nPaperCount; i++ )
				{
					if( pPapers[ i ] == CHOOSE_DEVMODE(dmPaperSize) )
					{
						pSetupData->mnPaperWidth  = pPaperSizes[ i ].x*10;
						pSetupData->mnPaperHeight = pPaperSizes[ i ].y*10;
						break;
					}
				}
			}
			if( pPapers )
				rtl_freeMemory( pPapers );
			if( pPaperSizes )
				rtl_freeMemory( pPaperSizes );
		}
		switch( CHOOSE_DEVMODE(dmPaperSize) )
		{
			case( DMPAPER_LETTER ):
				pSetupData->mePaperFormat = PAPER_LETTER;
				break;
			case( DMPAPER_TABLOID ):
				pSetupData->mePaperFormat = PAPER_TABLOID;
				break;
			case( DMPAPER_LEDGER ):
				pSetupData->mePaperFormat = PAPER_LEDGER;
				break;
			case( DMPAPER_LEGAL ):
				pSetupData->mePaperFormat = PAPER_LEGAL;
				break;
			case( DMPAPER_STATEMENT ):
				pSetupData->mePaperFormat = PAPER_STATEMENT;
				break;
			case( DMPAPER_EXECUTIVE ):
				pSetupData->mePaperFormat = PAPER_EXECUTIVE;
				break;
			case( DMPAPER_A3 ):
				pSetupData->mePaperFormat = PAPER_A3;
				break;
			case( DMPAPER_A4 ):
				pSetupData->mePaperFormat = PAPER_A4;
				break;
			case( DMPAPER_A5 ):
				pSetupData->mePaperFormat = PAPER_A5;
				break;
			//See http://wiki.services.openoffice.org/wiki/DefaultPaperSize
			//i.e.
			//http://msdn.microsoft.com/en-us/library/dd319099(VS.85).aspx
			//DMPAPER_B4	12	B4 (JIS) 257 x 364 mm
			//http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf
			//also says that the MS DMPAPER_B4 is JIS, which makes most sense. And
			//matches our Excel filter's belief about the matching XlPaperSize
			//enumeration.
			//
			//http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx said
			////"DMPAPER_B4 	12 	B4 (JIS) 250 x 354"
			//which is bogus as it's either JIS 257 × 364 or ISO 250 × 353
			//(cmc)
			case( DMPAPER_B4 ):
				pSetupData->mePaperFormat = PAPER_B4_JIS;
				break;
			case( DMPAPER_B5 ):
				pSetupData->mePaperFormat = PAPER_B5_JIS;
				break;
			case( DMPAPER_QUARTO ):
				pSetupData->mePaperFormat = PAPER_QUARTO;
				break;
			case( DMPAPER_10X14 ):
				pSetupData->mePaperFormat = PAPER_10x14;
				break;
			case( DMPAPER_NOTE ):
				pSetupData->mePaperFormat = PAPER_LETTER;
				break;
			case( DMPAPER_ENV_9 ):
				pSetupData->mePaperFormat = PAPER_ENV_9;
				break;
			case( DMPAPER_ENV_10 ):
				pSetupData->mePaperFormat = PAPER_ENV_10;
				break;
			case( DMPAPER_ENV_11 ):
				pSetupData->mePaperFormat = PAPER_ENV_11;
				break;
			case( DMPAPER_ENV_12 ):
				pSetupData->mePaperFormat = PAPER_ENV_12;
				break;
			case( DMPAPER_ENV_14 ):
				pSetupData->mePaperFormat = PAPER_ENV_14;
				break;
			case( DMPAPER_CSHEET ):
				pSetupData->mePaperFormat = PAPER_C;
				break;
			case( DMPAPER_DSHEET ):
				pSetupData->mePaperFormat = PAPER_D;
				break;
			case( DMPAPER_ESHEET ):
				pSetupData->mePaperFormat = PAPER_E;
				break;
			case( DMPAPER_ENV_DL):
				pSetupData->mePaperFormat = PAPER_ENV_DL;
				break;
			case( DMPAPER_ENV_C5):
				pSetupData->mePaperFormat = PAPER_ENV_C5;
				break;
			case( DMPAPER_ENV_C3):
				pSetupData->mePaperFormat = PAPER_ENV_C3;
				break;
			case( DMPAPER_ENV_C4):
				pSetupData->mePaperFormat = PAPER_ENV_C4;
				break;
			case( DMPAPER_ENV_C6):
				pSetupData->mePaperFormat = PAPER_ENV_C6;
				break;
			case( DMPAPER_ENV_C65):
				pSetupData->mePaperFormat = PAPER_ENV_C65;
				break;
			case( DMPAPER_ENV_ITALY ):
				pSetupData->mePaperFormat = PAPER_ENV_ITALY;
				break;
			case( DMPAPER_ENV_MONARCH ):
				pSetupData->mePaperFormat = PAPER_ENV_MONARCH;
				break;
			case( DMPAPER_ENV_PERSONAL ):
				pSetupData->mePaperFormat = PAPER_ENV_PERSONAL;
				break;
			case( DMPAPER_FANFOLD_US ):
				pSetupData->mePaperFormat = PAPER_FANFOLD_US;
				break;
			case( DMPAPER_FANFOLD_STD_GERMAN ):
				pSetupData->mePaperFormat = PAPER_FANFOLD_DE;
				break;
			case( DMPAPER_FANFOLD_LGL_GERMAN ):
				pSetupData->mePaperFormat = PAPER_FANFOLD_LEGAL_DE;
				break;
			case( DMPAPER_ISO_B4 ):
				pSetupData->mePaperFormat = PAPER_B4_ISO;
				break;
			case( DMPAPER_JAPANESE_POSTCARD ):
				pSetupData->mePaperFormat = PAPER_POSTCARD_JP;
				break;
			case( DMPAPER_9X11 ):
				pSetupData->mePaperFormat = PAPER_9x11;
				break;
			case( DMPAPER_10X11 ):
				pSetupData->mePaperFormat = PAPER_10x11;
				break;
			case( DMPAPER_15X11 ):
				pSetupData->mePaperFormat = PAPER_15x11;
				break;
			case( DMPAPER_ENV_INVITE ):
				pSetupData->mePaperFormat = PAPER_ENV_INVITE;
				break;
			case( DMPAPER_A_PLUS ):
				pSetupData->mePaperFormat = PAPER_A_PLUS;
				break;
			case( DMPAPER_B_PLUS ):
				pSetupData->mePaperFormat = PAPER_B_PLUS;
				break;
			case( DMPAPER_LETTER_PLUS ):
				pSetupData->mePaperFormat = PAPER_LETTER_PLUS;
				break;
			case( DMPAPER_A4_PLUS ):
				pSetupData->mePaperFormat = PAPER_A4_PLUS;
				break;
			case( DMPAPER_A2 ):
				pSetupData->mePaperFormat = PAPER_A2;
				break;
			case( DMPAPER_DBL_JAPANESE_POSTCARD ):
				pSetupData->mePaperFormat = PAPER_DOUBLEPOSTCARD_JP;
				break;
			case( DMPAPER_A6 ):
				pSetupData->mePaperFormat = PAPER_A6;
				break;
			case( DMPAPER_B6_JIS ):
				pSetupData->mePaperFormat = PAPER_B6_JIS;
				break;
			case( DMPAPER_12X11 ):
				pSetupData->mePaperFormat = PAPER_12x11;
				break;
			default:
				pSetupData->mePaperFormat = PAPER_USER;
				break;
		}
	}
    
    if( nFlags & SAL_JOBSET_DUPLEXMODE )
    {
        DuplexMode eDuplex = DUPLEX_UNKNOWN;
        if( (CHOOSE_DEVMODE(dmFields) & DM_DUPLEX) )
        {
            if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_SIMPLEX )
                eDuplex = DUPLEX_OFF;
            else if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_VERTICAL )
                eDuplex = DUPLEX_LONGEDGE;
            else if( CHOOSE_DEVMODE(dmDuplex) == DMDUP_HORIZONTAL )
                eDuplex = DUPLEX_SHORTEDGE;
        }
        pSetupData->meDuplexMode = eDuplex;
    }
}

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

static void ImplJobSetupToDevMode( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData, sal_uLong nFlags )
{
	if ( !pSetupData || !pSetupData->mpDriverData )
		return;

    DECLARE_DEVMODE( pSetupData );

	// Orientation
	if ( nFlags & SAL_JOBSET_ORIENTATION )
	{
		CHOOSE_DEVMODE(dmFields) |= DM_ORIENTATION;
		if ( pSetupData->meOrientation == ORIENTATION_PORTRAIT )
			CHOOSE_DEVMODE(dmOrientation) = DMORIENT_PORTRAIT;
		else
			CHOOSE_DEVMODE(dmOrientation) = DMORIENT_LANDSCAPE;
	}

	// PaperBin
	if ( nFlags & SAL_JOBSET_PAPERBIN )
	{
		sal_uLong nCount = ImplDeviceCaps( pPrinter, DC_BINS, NULL, pSetupData );

		if ( nCount && (nCount != GDI_ERROR) )
		{
			WORD* pBins = (WORD*)rtl_allocateZeroMemory(nCount*sizeof(WORD));
			ImplDeviceCaps( pPrinter, DC_BINS, (BYTE*)pBins, pSetupData );
			CHOOSE_DEVMODE(dmFields) |= DM_DEFAULTSOURCE;
			CHOOSE_DEVMODE(dmDefaultSource) = pBins[ pSetupData->mnPaperBin ];
			rtl_freeMemory( pBins );
		}
	}

	// PaperSize
	if ( nFlags & SAL_JOBSET_PAPERSIZE )
	{
		CHOOSE_DEVMODE(dmFields)		|= DM_PAPERSIZE;
		CHOOSE_DEVMODE(dmPaperWidth)	 = 0;
		CHOOSE_DEVMODE(dmPaperLength)    = 0;

		switch( pSetupData->mePaperFormat )
		{
			case( PAPER_A2 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A2;
				break;
			case( PAPER_A3 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A3;
				break;
			case( PAPER_A4 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A4;
				break;
			case( PAPER_A5 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A5;
				break;
			case( PAPER_B4_ISO):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ISO_B4;
				break;
			case( PAPER_LETTER ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LETTER;
				break;
			case( PAPER_LEGAL ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LEGAL;
				break;
			case( PAPER_TABLOID ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_TABLOID;
				break;
#if 0
			//http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx
			//DMPAPER_ENV_B6 is documented as:
			//"DMPAPER_ENV_B6 	35 	Envelope B6 176 x 125 mm"
			//which is the wrong way around, it is surely 125 x 176, i.e.
			//compare DMPAPER_ENV_B4 and DMPAPER_ENV_B4 as
			//DMPAPER_ENV_B4 	33 	Envelope B4 250 x 353 mm
			//DMPAPER_ENV_B5 	34 	Envelope B5 176 x 250 mm
			case( PAPER_B6_ISO ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_B6;
				break;
#endif
			case( PAPER_ENV_C4 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C4;
				break;
			case( PAPER_ENV_C5 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C5;
				break;
			case( PAPER_ENV_C6 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C6;
				break;
			case( PAPER_ENV_C65 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C65;
				break;
			case( PAPER_ENV_DL ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_DL;
				break;
			case( PAPER_C ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_CSHEET;
				break;
			case( PAPER_D ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_DSHEET;
				break;
			case( PAPER_E ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ESHEET;
				break;
			case( PAPER_EXECUTIVE ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_EXECUTIVE;
				break;
			case( PAPER_FANFOLD_LEGAL_DE ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_LGL_GERMAN;
				break;
			case( PAPER_ENV_MONARCH ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_MONARCH;
				break;
			case( PAPER_ENV_PERSONAL ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_PERSONAL;
				break;
			case( PAPER_ENV_9 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_9;
				break;
			case( PAPER_ENV_10 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_10;
				break;
			case( PAPER_ENV_11 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_11;
				break;
			case( PAPER_ENV_12 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_12;
				break;
			//See the comments on DMPAPER_B4 above
			case( PAPER_B4_JIS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B4;
				break;
			case( PAPER_B5_JIS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B5;
				break;
			case( PAPER_B6_JIS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B6_JIS;
				break;
			case( PAPER_LEDGER ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LEDGER;
				break;
			case( PAPER_STATEMENT ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_STATEMENT;
				break;
			case( PAPER_10x14 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_10X14;
				break;
			case( PAPER_ENV_14 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_14;
				break;
			case( PAPER_ENV_C3 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_C3;
				break;
			case( PAPER_ENV_ITALY ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_ITALY;
				break;
			case( PAPER_FANFOLD_US ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_US;
				break;
			case( PAPER_FANFOLD_DE ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_FANFOLD_STD_GERMAN;
				break;
			case( PAPER_POSTCARD_JP ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_JAPANESE_POSTCARD;
				break;
			case( PAPER_9x11 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_9X11;
				break;
			case( PAPER_10x11 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_10X11;
				break;
			case( PAPER_15x11 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_15X11;
				break;
			case( PAPER_ENV_INVITE ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_ENV_INVITE;
				break;
			case( PAPER_A_PLUS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A_PLUS;
				break;
			case( PAPER_B_PLUS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_B_PLUS;
				break;
			case( PAPER_LETTER_PLUS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_LETTER_PLUS;
				break;
			case( PAPER_A4_PLUS ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A4_PLUS;
				break;
			case( PAPER_DOUBLEPOSTCARD_JP ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_DBL_JAPANESE_POSTCARD;
				break;
			case( PAPER_A6 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_A6;
				break;
			case( PAPER_12x11 ):
				CHOOSE_DEVMODE(dmPaperSize) = DMPAPER_12X11;
				break;
			default:
			{
				short	nPaper = 0;
				sal_uLong	nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, NULL, pSetupData );
				WORD*	pPapers = NULL;
				sal_uLong	nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, NULL, pSetupData );
				POINT*	pPaperSizes = NULL;
				DWORD	nLandscapeAngle = ImplDeviceCaps( pPrinter, DC_ORIENTATION, NULL, pSetupData );
				if ( nPaperCount && (nPaperCount != GDI_ERROR) )
				{
					pPapers = (WORD*)rtl_allocateZeroMemory(nPaperCount*sizeof(WORD));
					ImplDeviceCaps( pPrinter, DC_PAPERS, (BYTE*)pPapers, pSetupData );
				}
				if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
				{
					pPaperSizes = (POINT*)rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT));
					ImplDeviceCaps( pPrinter, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );
				}
				if ( (nPaperSizeCount == nPaperCount) && pPapers && pPaperSizes )
				{
                    PaperInfo aInfo(pSetupData->mnPaperWidth, pSetupData->mnPaperHeight);
					// compare paper formats and select a good match
					for ( sal_uLong i = 0; i < nPaperCount; i++ )
					{
						if ( aInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)))
						{
							nPaper = pPapers[i];
							break;
						}
					}

					// If the printer supports landscape orientation, check paper sizes again
					// with landscape orientation. This is necessary as a printer driver provides
					// all paper sizes with portrait orientation only!!
					if ( !nPaper && nLandscapeAngle != 0 )
					{
                        PaperInfo aRotatedInfo(pSetupData->mnPaperHeight, pSetupData->mnPaperWidth);
						for ( sal_uLong i = 0; i < nPaperCount; i++ )
						{
							if ( aRotatedInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)) )
							{
								nPaper = pPapers[i];
								break;
							}
						}
					}

					if ( nPaper )
						CHOOSE_DEVMODE(dmPaperSize) = nPaper;
				}

				if ( !nPaper )
				{
					CHOOSE_DEVMODE(dmFields)	   |= DM_PAPERLENGTH | DM_PAPERWIDTH;
					CHOOSE_DEVMODE(dmPaperSize)    	= DMPAPER_USER;
					CHOOSE_DEVMODE(dmPaperWidth)	= (short)(pSetupData->mnPaperWidth/10);
					CHOOSE_DEVMODE(dmPaperLength)   = (short)(pSetupData->mnPaperHeight/10);
				}

				if ( pPapers )
					rtl_freeMemory(pPapers);
				if ( pPaperSizes )
					rtl_freeMemory(pPaperSizes);

				break;
			}
		}
	}
    if( (nFlags & SAL_JOBSET_DUPLEXMODE) )
    {
        switch( pSetupData->meDuplexMode )
        {
        case DUPLEX_OFF:
            CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
            CHOOSE_DEVMODE(dmDuplex) = DMDUP_SIMPLEX;
            break;
        case DUPLEX_SHORTEDGE:
            CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
            CHOOSE_DEVMODE(dmDuplex) = DMDUP_HORIZONTAL;
            break;
        case DUPLEX_LONGEDGE:
            CHOOSE_DEVMODE(dmFields) |= DM_DUPLEX;
            CHOOSE_DEVMODE(dmDuplex) = DMDUP_VERTICAL;
            break;
        case DUPLEX_UNKNOWN:
            break;
        }
    }
}

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

static HDC ImplCreateICW_WithCatch( LPWSTR pDriver,
	                                LPCWSTR pDevice,
                                    LPDEVMODEW pDevMode )
{
    HDC hDC = 0;
	CATCH_DRIVER_EX_BEGIN;
    hDC = CreateICW( pDriver, pDevice, 0, pDevMode );
	CATCH_DRIVER_EX_END_2( "exception in CreateICW" );
    return hDC;
}

static HDC ImplCreateICA_WithCatch( char* pDriver,
	                                char* pDevice,
                                    LPDEVMODEA pDevMode )
{
    HDC hDC = 0;
	CATCH_DRIVER_EX_BEGIN;
    hDC = CreateICA( pDriver, pDevice, 0, pDevMode );
	CATCH_DRIVER_EX_END_2( "exception in CreateICW" );
    return hDC;
}


static HDC ImplCreateSalPrnIC( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData )
{
    HDC hDC = 0;
    if( aSalShlData.mbWPrinter )
    {
        LPDEVMODEW pDevMode;
        if ( pSetupData && pSetupData->mpDriverData )
            pDevMode = SAL_DEVMODE_W( pSetupData );
        else
            pDevMode = NULL;
        // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateIC, although declared const - so provide some space
        // pl: does this hold true for Unicode functions ?
        if( pPrinter->maDriverName.Len() > 2048 || pPrinter->maDeviceName.Len() > 2048 )
            return 0;
        sal_Unicode pDriverName[ 4096 ];
        sal_Unicode pDeviceName[ 4096 ];
        rtl_copyMemory( pDriverName, pPrinter->maDriverName.GetBuffer(), pPrinter->maDriverName.Len()*sizeof(sal_Unicode));
        memset( pDriverName+pPrinter->maDriverName.Len(), 0, 32 );
        rtl_copyMemory( pDeviceName, pPrinter->maDeviceName.GetBuffer(), pPrinter->maDeviceName.Len()*sizeof(sal_Unicode));
        memset( pDeviceName+pPrinter->maDeviceName.Len(), 0, 32 );
        hDC = ImplCreateICW_WithCatch( reinterpret_cast< LPWSTR >(pDriverName),
                                       reinterpret_cast< LPCWSTR >(pDeviceName),
                                       pDevMode );
    }
    else
    {
        LPDEVMODEA pDevMode;
        if ( pSetupData && pSetupData->mpDriverData )
            pDevMode = SAL_DEVMODE_A( pSetupData );
        else
            pDevMode = NULL;
        // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateIC, although declared const - so provide some space
        ByteString aDriver ( ImplSalGetWinAnsiString( pPrinter->maDriverName, TRUE ) );
        ByteString aDevice ( ImplSalGetWinAnsiString( pPrinter->maDeviceName, TRUE ) );
        int n = aDriver.Len() > aDevice.Len() ? aDriver.Len() : aDevice.Len();
            // #125813# under some circumstances many printer drivers really
        // seem to have a problem with the names and their conversions.
        // We need to get on to of this, but haven't been able to reproduce
        // the problem yet. Put the names on the stack so we get them
        // with an eventual crash report.
        if( n >= 2048 )
            return 0;
        n += 2048;
        char lpszDriverName[ 4096 ];
        char lpszDeviceName[ 4096 ];
        strncpy( lpszDriverName, aDriver.GetBuffer(), n );
        strncpy( lpszDeviceName, aDevice.GetBuffer(), n );
        // HDU: the crashes usually happen in a MBCS to unicode conversion,
        // so I suspect the MBCS string's end is not properly recognized.
        // The longest MBCS encoding I'm aware of has six bytes per code
        // => add a couple of zeroes...
        memset( lpszDriverName+aDriver.Len(), 0, 16 );
        memset( lpszDeviceName+aDevice.Len(), 0, 16 );
        hDC = ImplCreateICA_WithCatch( lpszDriverName,
                                       lpszDeviceName,
                                       pDevMode );
    }
    return hDC;
}

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

static WinSalGraphics* ImplCreateSalPrnGraphics( HDC hDC )
{
	WinSalGraphics* pGraphics = new WinSalGraphics;
    pGraphics->SetLayout( 0 );
	pGraphics->mhDC		= hDC;
	pGraphics->mhWnd 	= 0;
	pGraphics->mbPrinter = TRUE;
	pGraphics->mbVirDev	= FALSE;
	pGraphics->mbWindow	= FALSE;
	pGraphics->mbScreen	= FALSE;
	ImplSalInitGraphics( pGraphics );
	return pGraphics;
}

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

static sal_Bool ImplUpdateSalPrnIC( WinSalInfoPrinter* pPrinter, ImplJobSetup* pSetupData )
{
	HDC hNewDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
	if ( !hNewDC )
		return FALSE;

	if ( pPrinter->mpGraphics )
	{
		ImplSalDeInitGraphics( pPrinter->mpGraphics );
		DeleteDC( pPrinter->mpGraphics->mhDC );
		delete pPrinter->mpGraphics;
	}

	pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hNewDC );
	pPrinter->mhDC		= hNewDC;

	return TRUE;
}

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

SalInfoPrinter* WinSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
                                                   ImplJobSetup* pSetupData )
{
	WinSalInfoPrinter* pPrinter = new WinSalInfoPrinter;
    if( ! pQueueInfo->mpSysData )
        GetPrinterQueueState( pQueueInfo );
	pPrinter->maDriverName	= pQueueInfo->maDriver;
	pPrinter->maDeviceName	= pQueueInfo->maPrinterName;
	pPrinter->maPortName	= pQueueInfo->mpSysData ? 
                                *(String*)(pQueueInfo->mpSysData)
                              : String();

    // check if the provided setup data match the actual printer
	ImplTestSalJobSetup( pPrinter, pSetupData, TRUE );

	HDC hDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
	if ( !hDC )
	{
		delete pPrinter;
		return NULL;
	}

    pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hDC );
	pPrinter->mhDC		= hDC;
	if ( !pSetupData->mpDriverData )
		ImplUpdateSalJobSetup( pPrinter, pSetupData, FALSE, NULL );
	ImplDevModeToJobSetup( pPrinter, pSetupData, SAL_JOBSET_ALL );
	pSetupData->mnSystem = JOBSETUP_SYSTEM_WINDOWS;

	return pPrinter;
}

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

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

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

WinSalInfoPrinter::WinSalInfoPrinter() :
    mpGraphics( NULL ),
    mhDC( 0 ),
    mbGraphics( FALSE )
{
    m_bPapersInit = FALSE;
}

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

WinSalInfoPrinter::~WinSalInfoPrinter()
{
	if ( mpGraphics )
	{
		ImplSalDeInitGraphics( mpGraphics );
		DeleteDC( mpGraphics->mhDC );
		delete mpGraphics;
	}
}

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

void WinSalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData )
{
    m_aPaperFormats.clear();

    DWORD nCount = ImplDeviceCaps( this, DC_PAPERSIZE, NULL, pSetupData );
    if( nCount == GDI_ERROR )
        nCount = 0;

    POINT* pPaperSizes = NULL;
    if( nCount )
	{
		pPaperSizes = (POINT*)rtl_allocateZeroMemory(nCount*sizeof(POINT));
		ImplDeviceCaps( this, DC_PAPERSIZE, (BYTE*)pPaperSizes, pSetupData );

        if( aSalShlData.mbWPrinter )
        {
            sal_Unicode* pNamesBuffer = (sal_Unicode*)rtl_allocateMemory(nCount*64*sizeof(sal_Unicode));
            ImplDeviceCaps( this, DC_PAPERNAMES, (BYTE*)pNamesBuffer, pSetupData );
            for( DWORD i = 0; i < nCount; ++i )
            {
                PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10);
                m_aPaperFormats.push_back( aInfo );
            }
            rtl_freeMemory( pNamesBuffer );
        }
        else
        {
            char* pNamesBuffer = (char*)rtl_allocateMemory(nCount*64);
            ImplDeviceCaps( this, DC_PAPERNAMES, (BYTE*)pNamesBuffer, pSetupData );
            for( DWORD i = 0; i < nCount; ++i )
            {
                PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10);
                m_aPaperFormats.push_back( aInfo );
            }
            rtl_freeMemory( pNamesBuffer );
        }
        rtl_freeMemory( pPaperSizes );
	}

    m_bPapersInit = true;
}

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

int WinSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* pSetupData )
{
    int nRet = ImplDeviceCaps( this, DC_ORIENTATION, NULL, pSetupData );

    if( nRet != GDI_ERROR )
        return nRet * 10;
    else
        return 900; // guess
}

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

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

	if ( mpGraphics )
		mbGraphics = TRUE;

	return mpGraphics;
}

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

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

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

sal_Bool WinSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pSetupData )
{
	if ( ImplUpdateSalJobSetup( this, pSetupData, TRUE, static_cast<WinSalFrame*>(pFrame) ) )
	{
		ImplDevModeToJobSetup( this, pSetupData, SAL_JOBSET_ALL );
		return ImplUpdateSalPrnIC( this, pSetupData );
	}

	return FALSE;
}

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

sal_Bool WinSalInfoPrinter::SetPrinterData( ImplJobSetup* pSetupData )
{
	if ( !ImplTestSalJobSetup( this, pSetupData, FALSE ) )
		return FALSE;
	return ImplUpdateSalPrnIC( this, pSetupData );
}

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

sal_Bool WinSalInfoPrinter::SetData( sal_uLong nFlags, ImplJobSetup* pSetupData )
{
	ImplJobSetupToDevMode( this, pSetupData, nFlags );
	if ( ImplUpdateSalJobSetup( this, pSetupData, TRUE, NULL ) )
	{
		ImplDevModeToJobSetup( this, pSetupData, nFlags );
		return ImplUpdateSalPrnIC( this, pSetupData );
	}

	return FALSE;
}

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

sal_uLong WinSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pSetupData )
{
	DWORD nRet = ImplDeviceCaps( this, DC_BINS, NULL, pSetupData );
	if ( nRet && (nRet != GDI_ERROR) )
		return nRet;
	else
		return 0;
}

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

XubString WinSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pSetupData, sal_uLong nPaperBin )
{
	XubString aPaperBinName;

	DWORD nBins = ImplDeviceCaps( this, DC_BINNAMES, NULL, pSetupData );
	if ( (nPaperBin < nBins) && (nBins != GDI_ERROR) )
	{
        if( aSalShlData.mbWPrinter )
        {
            sal_Unicode* pBuffer = new sal_Unicode[nBins*24];
            DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, (BYTE*)pBuffer, pSetupData );
            if ( nRet && (nRet != GDI_ERROR) )
                aPaperBinName = pBuffer + (nPaperBin*24);
            delete [] pBuffer;
        }
        else
        {
            char* pBuffer = new char[nBins*24];
            DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, (BYTE*)pBuffer, pSetupData );
            if ( nRet && (nRet != GDI_ERROR) )
                aPaperBinName = ImplSalGetUniString( (const char*)(pBuffer + (nPaperBin*24)) );
            delete [] pBuffer;
        }
	}

	return aPaperBinName;
}

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

sal_uLong WinSalInfoPrinter::GetCapabilities( const ImplJobSetup* pSetupData, sal_uInt16 nType )
{
	DWORD nRet;

	switch ( nType )
	{
		case PRINTER_CAPABILITIES_SUPPORTDIALOG:
			return TRUE;
		case PRINTER_CAPABILITIES_COPIES:
			nRet = ImplDeviceCaps( this, DC_COPIES, NULL, pSetupData );
			if ( nRet && (nRet != GDI_ERROR) )
				return nRet;
			return 0;
		case PRINTER_CAPABILITIES_COLLATECOPIES:
			if ( aSalShlData.mbW40 )
			{
				nRet = ImplDeviceCaps( this, DC_COLLATE, NULL, pSetupData );
				if ( nRet && (nRet != GDI_ERROR) )
				{
					nRet = ImplDeviceCaps( this, DC_COPIES, NULL, pSetupData );
					if ( nRet && (nRet != GDI_ERROR) )
						 return nRet;
				}
			}
			return 0;

		case PRINTER_CAPABILITIES_SETORIENTATION:
			nRet = ImplDeviceCaps( this, DC_ORIENTATION, NULL, pSetupData );
			if ( nRet && (nRet != GDI_ERROR) )
				return TRUE;
			return FALSE;

		case PRINTER_CAPABILITIES_SETPAPERBIN:
			nRet = ImplDeviceCaps( this, DC_BINS, NULL, pSetupData );
			if ( nRet && (nRet != GDI_ERROR) )
				return TRUE;
			return FALSE;

		case PRINTER_CAPABILITIES_SETPAPERSIZE:
		case PRINTER_CAPABILITIES_SETPAPER:
			nRet = ImplDeviceCaps( this, DC_PAPERS, NULL, pSetupData );
			if ( nRet && (nRet != GDI_ERROR) )
				return TRUE;
			return FALSE;
	}

	return 0;
}

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

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

	rOutWidth	= GetDeviceCaps( hDC, HORZRES );
	rOutHeight	= GetDeviceCaps( hDC, VERTRES );

	rPageOffX	= GetDeviceCaps( hDC, PHYSICALOFFSETX );
	rPageOffY	= GetDeviceCaps( hDC, PHYSICALOFFSETY );
	rPageWidth	= GetDeviceCaps( hDC, PHYSICALWIDTH );
	rPageHeight = GetDeviceCaps( hDC, PHYSICALHEIGHT );
}

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

SalPrinter* WinSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
{
	WinSalPrinter* pPrinter = new WinSalPrinter;
	pPrinter->mpInfoPrinter = static_cast<WinSalInfoPrinter*>(pInfoPrinter);
	return pPrinter;
}

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

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

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

BOOL CALLBACK SalPrintAbortProc( HDC hPrnDC, int /* nError */ )
{
	SalData*	pSalData = GetSalData();
	WinSalPrinter* pPrinter;
	sal_Bool		bWhile = TRUE;
	int 		i = 0;

	do
	{
		// Messages verarbeiten
		MSG aMsg;
		if ( ImplPeekMessage( &aMsg, 0, 0, 0, PM_REMOVE ) )
		{
            if ( !ImplInterceptChildWindowKeyDown( aMsg ) )
            {
                TranslateMessage( &aMsg );
                ImplDispatchMessage( &aMsg );
            }
			i++;
			if ( i > 15 )
				bWhile = FALSE;
		}
		else
			bWhile = FALSE;

		pPrinter = pSalData->mpFirstPrinter;
		while ( pPrinter )
		{
			if( pPrinter->mhDC == hPrnDC )
				break;

			pPrinter = pPrinter->mpNextPrinter;
		}

		if ( !pPrinter || pPrinter->mbAbort )
			return FALSE;
	}
	while ( bWhile );

	return TRUE;
}

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

static LPDEVMODEA ImplSalSetCopies( LPDEVMODEA pDevMode, sal_uLong nCopies, sal_Bool bCollate )
{
	LPDEVMODEA pNewDevMode = pDevMode;
	if ( pDevMode && (nCopies > 1) )
	{
		if ( nCopies > 32765 )
			nCopies = 32765;
		sal_uLong nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra;
		pNewDevMode = (LPDEVMODEA)rtl_allocateMemory( nDevSize );
		memcpy( pNewDevMode, pDevMode, nDevSize );
		pDevMode = pNewDevMode;
		pDevMode->dmFields |= DM_COPIES;
		pDevMode->dmCopies	= (short)(sal_uInt16)nCopies;
		if ( aSalShlData.mbW40 )
		{
			pDevMode->dmFields |= DM_COLLATE;
			if ( bCollate )
				pDevMode->dmCollate = DMCOLLATE_TRUE;
			else
				pDevMode->dmCollate = DMCOLLATE_FALSE;
		}
	}

	return pNewDevMode;
}

static LPDEVMODEW ImplSalSetCopies( LPDEVMODEW pDevMode, sal_uLong nCopies, sal_Bool bCollate )
{
	LPDEVMODEW pNewDevMode = pDevMode;
	if ( pDevMode && (nCopies > 1) )
	{
		if ( nCopies > 32765 )
			nCopies = 32765;
		sal_uLong nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra;
		pNewDevMode = (LPDEVMODEW)rtl_allocateMemory( nDevSize );
		memcpy( pNewDevMode, pDevMode, nDevSize );
		pDevMode = pNewDevMode;
		pDevMode->dmFields |= DM_COPIES;
		pDevMode->dmCopies	= (short)(sal_uInt16)nCopies;
		if ( aSalShlData.mbW40 )
		{
			pDevMode->dmFields |= DM_COLLATE;
			if ( bCollate )
				pDevMode->dmCollate = DMCOLLATE_TRUE;
			else
				pDevMode->dmCollate = DMCOLLATE_FALSE;
		}
	}

	return pNewDevMode;
}

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

WinSalPrinter::WinSalPrinter() :
    mpGraphics( NULL ),
    mpInfoPrinter( NULL ),
    mpNextPrinter( NULL ),
    mhDC( 0 ),
    mnError( 0 ),
    mnCopies( 0 ),
    mbCollate( FALSE ),
    mbAbort( FALSE ),
    mbValid( true )
{
	SalData* pSalData = GetSalData();
	// insert printer in printerlist
	mpNextPrinter = pSalData->mpFirstPrinter;
	pSalData->mpFirstPrinter = this;
}

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

WinSalPrinter::~WinSalPrinter()
{
	SalData* pSalData = GetSalData();

    // release DC if there is one still around because of AbortJob
	HDC hDC = mhDC;
	if ( hDC )
	{
		if ( mpGraphics )
		{
			ImplSalDeInitGraphics( mpGraphics );
			delete mpGraphics;
		}

		DeleteDC( hDC );
	}

	// remove printer from printerlist
	if ( this == pSalData->mpFirstPrinter )
		pSalData->mpFirstPrinter = mpNextPrinter;
	else
	{
		WinSalPrinter* pTempPrinter = pSalData->mpFirstPrinter;

		while( pTempPrinter->mpNextPrinter != this )
			pTempPrinter = pTempPrinter->mpNextPrinter;

		pTempPrinter->mpNextPrinter = mpNextPrinter;
	}
    mbValid = false;
}

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

void WinSalPrinter::markInvalid()
{
    mbValid = false;
}

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

// need wrappers for StarTocW/A to use structured exception handling
// since SEH does not mix with standard exception handling's cleanup
static int lcl_StartDocW( HDC hDC, DOCINFOW* pInfo, WinSalPrinter* pPrt )
{
    int nRet = 0;
    CATCH_DRIVER_EX_BEGIN;
    nRet = ::StartDocW( hDC, pInfo );
    CATCH_DRIVER_EX_END( "exception in StartDocW", pPrt );
    return nRet;
}

static int lcl_StartDocA( HDC hDC, DOCINFOA* pInfo, WinSalPrinter* pPrt )
{
    int nRet = 0;
    CATCH_DRIVER_EX_BEGIN;
    nRet = ::StartDocA( hDC, pInfo );
    CATCH_DRIVER_EX_END( "exception in StartDocW", pPrt );
    return nRet;
}

sal_Bool WinSalPrinter::StartJob( const XubString* pFileName,
						   const XubString& rJobName,
						   const XubString&,
						   sal_uLong nCopies,
                           bool bCollate,
                           bool /*bDirect*/,
						   ImplJobSetup* pSetupData )
{
	mnError		= 0;
	mbAbort		= FALSE;
	mnCopies		= nCopies;
	mbCollate 	= bCollate;

	LPDEVMODEA	pOrgDevModeA = NULL;
	LPDEVMODEA	pDevModeA = NULL;
	LPDEVMODEW	pOrgDevModeW = NULL;
	LPDEVMODEW	pDevModeW = NULL;
    HDC hDC = 0;
    if( aSalShlData.mbWPrinter )
    {
        if ( pSetupData && pSetupData->mpDriverData )
        {
            pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
            pDevModeW = ImplSalSetCopies( pOrgDevModeW, nCopies, bCollate );
        }
        else
            pDevModeW = NULL;
    
        // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateDC, although declared const - so provide some space
        sal_Unicode aDrvBuf[4096];
        sal_Unicode aDevBuf[4096];
        rtl_copyMemory( aDrvBuf, mpInfoPrinter->maDriverName.GetBuffer(), (mpInfoPrinter->maDriverName.Len()+1)*sizeof(sal_Unicode));
        rtl_copyMemory( aDevBuf, mpInfoPrinter->maDeviceName.GetBuffer(), (mpInfoPrinter->maDeviceName.Len()+1)*sizeof(sal_Unicode));
        hDC = CreateDCW( reinterpret_cast<LPCWSTR>(aDrvBuf),
                         reinterpret_cast<LPCWSTR>(aDevBuf),
                         NULL,
                         pDevModeW );
    
        if ( pDevModeW != pOrgDevModeW )
            rtl_freeMemory( pDevModeW );
    }
    else
    {
        if ( pSetupData && pSetupData->mpDriverData )
        {
            pOrgDevModeA = SAL_DEVMODE_A( pSetupData );
            pDevModeA = ImplSalSetCopies( pOrgDevModeA, nCopies, bCollate );
        }
        else
            pDevModeA = NULL;
    
        // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateDC, although declared const - so provide some space
        ByteString aDriver ( ImplSalGetWinAnsiString( mpInfoPrinter->maDriverName, TRUE ) );
        ByteString aDevice ( ImplSalGetWinAnsiString( mpInfoPrinter->maDeviceName, TRUE ) );
        int n = aDriver.Len() > aDevice.Len() ? aDriver.Len() : aDevice.Len();
        n += 2048;
        char *lpszDriverName = new char[n];
        char *lpszDeviceName = new char[n];
        strncpy( lpszDriverName, aDriver.GetBuffer(), n );
        strncpy( lpszDeviceName, aDevice.GetBuffer(), n );
        hDC = CreateDCA( lpszDriverName,
                         lpszDeviceName,
                         NULL,
                         pDevModeA );
    
        delete [] lpszDriverName;
        delete [] lpszDeviceName;
    
        if ( pDevModeA != pOrgDevModeA )
            rtl_freeMemory( pDevModeA );
    }

	if ( !hDC )
	{
		mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return FALSE;
	}

    // make sure mhDC is set before the printer driver may call our abortproc
    mhDC = hDC;
	if ( SetAbortProc( hDC, SalPrintAbortProc ) <= 0 )
	{
		mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return FALSE;
	}

	mnError	= 0;
	mbAbort	= FALSE;

	// Wegen Telocom Balloon Fax-Treiber, der uns unsere Messages
	// ansonsten oefters schickt, versuchen wir vorher alle
	// zu verarbeiten und dann eine Dummy-Message reinstellen
	sal_Bool bWhile = TRUE;
	int  i = 0;
	do
	{
		// Messages verarbeiten
		MSG aMsg;
		if ( ImplPeekMessage( &aMsg, 0, 0, 0, PM_REMOVE ) )
		{
            if ( !ImplInterceptChildWindowKeyDown( aMsg ) )
            {
                TranslateMessage( &aMsg );
                ImplDispatchMessage( &aMsg );
            }

			i++;
			if ( i > 15 )
				bWhile = FALSE;
		}
		else
			bWhile = FALSE;
	}
	while ( bWhile );
	ImplPostMessage( GetSalData()->mpFirstInstance->mhComWnd, SAL_MSG_DUMMY, 0, 0 );

    // bring up a file choser if printing to file port but no file name given
    OUString aOutFileName;
    if( mpInfoPrinter->maPortName.EqualsIgnoreCaseAscii( "FILE:" ) && !(pFileName && pFileName->Len()) )
    {

        uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
        if( xFactory.is() )
        {
            uno::Reference< XFilePicker > xFilePicker( xFactory->createInstance(
                OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.ui.dialogs.FilePicker" ) ) ),
                UNO_QUERY );
            DBG_ASSERT( xFilePicker.is(), "could not get FilePicker service" );

            uno::Reference< XInitialization > xInit( xFilePicker, UNO_QUERY );
            uno::Reference< XFilterManager > xFilterMgr( xFilePicker, UNO_QUERY );
            if( xInit.is() && xFilePicker.is() && xFilterMgr.is() )
            {
                Sequence< Any > aServiceType( 1 );
                aServiceType[0] <<= TemplateDescription::FILESAVE_SIMPLE;
                xInit->initialize( aServiceType );
                if( xFilePicker->execute() == ExecutableDialogResults::OK )
                {
                    Sequence< OUString > aPathSeq( xFilePicker->getFiles() );
				    INetURLObject aObj( aPathSeq[0] );
                    // we're using ansi calls (StartDocA) so convert the string
                    aOutFileName = aObj.PathToFileName();
                }
                else
                {
                    mnError = SAL_PRINTER_ERROR_ABORT;
                    return FALSE;
                }
            }
        }
    }

    if( aSalShlData.mbWPrinter )
    {
        DOCINFOW aInfo;
        memset( &aInfo, 0, sizeof( DOCINFOW ) );
        aInfo.cbSize = sizeof( aInfo );
        aInfo.lpszDocName = (LPWSTR)rJobName.GetBuffer();
        if ( pFileName || aOutFileName.getLength() )
        {
            if ( (pFileName && pFileName->Len()) || aOutFileName.getLength() )
            {
                aInfo.lpszOutput = (LPWSTR)( (pFileName && pFileName->Len()) ? pFileName->GetBuffer() : aOutFileName.getStr());
            }
            else
                aInfo.lpszOutput = L"FILE:";
        }
        else
            aInfo.lpszOutput = NULL;
    
        // start Job
        int nRet = lcl_StartDocW( hDC, &aInfo, this );
        
        if ( nRet <= 0 )
        {
            long nError = GetLastError();
            if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) )
                mnError = SAL_PRINTER_ERROR_ABORT;
            else
                mnError = SAL_PRINTER_ERROR_GENERALERROR;
            return FALSE;            
        }
    }
    else
    {
        // Both strings must exist, if StartJob() is called
        ByteString aJobName( ImplSalGetWinAnsiString( rJobName, TRUE ) );
        ByteString aFileName;
    
        DOCINFOA aInfo;
        memset( &aInfo, 0, sizeof( DOCINFOA ) );
        aInfo.cbSize = sizeof( aInfo );
        aInfo.lpszDocName = (LPCSTR)aJobName.GetBuffer();
        if ( pFileName || aOutFileName.getLength() )
        {
            if ( pFileName->Len() || aOutFileName.getLength() )
            {
                aFileName = ImplSalGetWinAnsiString( pFileName ? *pFileName : static_cast<const XubString>(aOutFileName), TRUE );
                aInfo.lpszOutput = (LPCSTR)aFileName.GetBuffer();
            }
            else
                aInfo.lpszOutput = "FILE:";
        }
        else
            aInfo.lpszOutput = NULL;
    
        // start Job
        int nRet = lcl_StartDocA( hDC, &aInfo, this );
        if ( nRet <= 0 )
        {
            long nError = GetLastError();
            if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) )
                mnError = SAL_PRINTER_ERROR_ABORT;
            else
                mnError = SAL_PRINTER_ERROR_GENERALERROR;
            return FALSE;
        }
    }

	return TRUE;
}

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

sal_Bool WinSalPrinter::EndJob()
{
	DWORD err = 0;
	HDC hDC = mhDC;
	if ( isValid() && hDC )
	{
		if ( mpGraphics )
		{
			ImplSalDeInitGraphics( mpGraphics );
			delete mpGraphics;
			mpGraphics = NULL;
		}

        // #i54419# Windows fax printer brings up a dialog in EndDoc
        // which text previously copied in soffice process can be
        // pasted to -> deadlock due to mutex not released.
        // it should be safe to release the yield mutex over the EndDoc
        // call, however the real solution is supposed to be the threading
        // framework yet to come.
        SalData* pSalData = GetSalData();
		sal_uLong nAcquire = pSalData->mpFirstInstance->ReleaseYieldMutex();
        CATCH_DRIVER_EX_BEGIN;
        if( ::EndDoc( hDC ) <= 0 )
            err = GetLastError();
        CATCH_DRIVER_EX_END( "exception in EndDoc", this );
        
		pSalData->mpFirstInstance->AcquireYieldMutex( nAcquire );
		DeleteDC( hDC );
        mhDC = 0;
	}

	return TRUE;
}

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

sal_Bool WinSalPrinter::AbortJob()
{
	mbAbort = TRUE;

	// Abort asyncron ausloesen
	HDC hDC = mhDC;
	if ( hDC )
	{
		SalData* pSalData = GetSalData();
		ImplPostMessage( pSalData->mpFirstInstance->mhComWnd,
						 SAL_MSG_PRINTABORTJOB, (WPARAM)hDC, 0 );
	}

	return TRUE;
}

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

void ImplSalPrinterAbortJobAsync( HDC hPrnDC )
{
	SalData*	pSalData = GetSalData();
	WinSalPrinter* pPrinter = pSalData->mpFirstPrinter;

	// Feststellen, ob Printer noch existiert
	while ( pPrinter )
	{
		if ( pPrinter->mhDC == hPrnDC )
			break;

		pPrinter = pPrinter->mpNextPrinter;
	}

	// Wenn Printer noch existiert, dann den Job abbrechen
	if ( pPrinter )
	{
		HDC hDC = pPrinter->mhDC;
		if ( hDC )
		{
			if ( pPrinter->mpGraphics )
			{
				ImplSalDeInitGraphics( pPrinter->mpGraphics );
				delete pPrinter->mpGraphics;
				pPrinter->mpGraphics = NULL;
			}

            CATCH_DRIVER_EX_BEGIN;
            ::AbortDoc( hDC );
            CATCH_DRIVER_EX_END( "exception in AbortDoc", pPrinter );

			DeleteDC( hDC );
            pPrinter->mhDC = 0;
		}
	}
}

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

SalGraphics* WinSalPrinter::StartPage( ImplJobSetup* pSetupData, sal_Bool bNewJobData )
{
    if( ! isValid() || mhDC == 0 )
        return NULL;
    
	HDC hDC = mhDC;
	if ( pSetupData && pSetupData->mpDriverData && bNewJobData )
	{
        if( aSalShlData.mbWPrinter )
        {
            LPDEVMODEW	pOrgDevModeW;
            LPDEVMODEW	pDevModeW;
            pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
            pDevModeW = ImplSalSetCopies( pOrgDevModeW, mnCopies, mbCollate );
            ResetDCW( hDC, pDevModeW );
            if ( pDevModeW != pOrgDevModeW )
                rtl_freeMemory( pDevModeW );
        }
        else
        {
            LPDEVMODEA	pOrgDevModeA;
            LPDEVMODEA	pDevModeA;
            pOrgDevModeA = SAL_DEVMODE_A( pSetupData );
            pDevModeA = ImplSalSetCopies( pOrgDevModeA, mnCopies, mbCollate );
            ResetDCA( hDC, pDevModeA );
            if ( pDevModeA != pOrgDevModeA )
                rtl_freeMemory( pDevModeA );
        }
	}
	int nRet = 0;
    CATCH_DRIVER_EX_BEGIN;
    nRet = ::StartPage( hDC );
    CATCH_DRIVER_EX_END( "exception in StartPage", this );
    
	if ( nRet <= 0 )
	{
		GetLastError();
		mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return NULL;
	}

	// Hack to work around old PostScript printer drivers optimizing away empty pages
    // TODO: move into ImplCreateSalPrnGraphics()?
	HPEN	hTempPen = SelectPen( hDC, GetStockPen( NULL_PEN ) );
	HBRUSH	hTempBrush = SelectBrush( hDC, GetStockBrush( NULL_BRUSH ) );
	WIN_Rectangle( hDC, -8000, -8000, -7999, -7999 );
	SelectPen( hDC, hTempPen );
	SelectBrush( hDC, hTempBrush );

    mpGraphics = ImplCreateSalPrnGraphics( hDC );
    return mpGraphics;
}

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

sal_Bool WinSalPrinter::EndPage()
{
	HDC hDC = mhDC;
	if ( hDC && mpGraphics )
	{
		ImplSalDeInitGraphics( mpGraphics );
		delete mpGraphics;
		mpGraphics = NULL;
	}

    if( ! isValid() )
        return FALSE;
    
	int nRet = 0;
    CATCH_DRIVER_EX_BEGIN;
    nRet = ::EndPage( hDC );
    CATCH_DRIVER_EX_END( "exception in EndPage", this );

	if ( nRet > 0 )
		return TRUE;
	else
	{
		GetLastError();
		mnError = SAL_PRINTER_ERROR_GENERALERROR;
		return FALSE;
	}
}

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

sal_uLong WinSalPrinter::GetErrorCode()
{
	return mnError;
}