diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/win/gdi/salprn.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/win/gdi/salprn.cxx')
-rw-r--r-- | vcl/win/gdi/salprn.cxx | 1604 |
1 files changed, 1604 insertions, 0 deletions
diff --git a/vcl/win/gdi/salprn.cxx b/vcl/win/gdi/salprn.cxx new file mode 100644 index 0000000000..3302efa2d9 --- /dev/null +++ b/vcl/win/gdi/salprn.cxx @@ -0,0 +1,1604 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <memory> +#include <vector> +#include <string.h> + +#include <svsys.h> + +#include <osl/module.h> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <tools/urlobj.hxx> + +#include <vcl/weld.hxx> +#include <vcl/QueueInfo.hxx> + +#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/FilePicker.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 <comphelper/windowsdebugoutput.hxx> + +#include <vcl/threadex.hxx> + +#include <malloc.h> + +#include <winspool.h> +#if defined GetDefaultPrinter +# undef GetDefaultPrinter +#endif +#if defined SetPrinterData +# undef SetPrinterData +#endif + +#define CATCH_DRIVER_EX_BEGIN \ + __try \ + { +#define CATCH_DRIVER_EX_END(mes, p) \ + } \ + __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\ + { \ + OSL_FAIL( mes ); \ + p->markInvalid(); \ + } +#define CATCH_DRIVER_EX_END_2(mes) \ + } \ + __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\ + { \ + OSL_FAIL( mes ); \ + } + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ui::dialogs; + +static DEVMODEW const * SAL_DEVMODE_W( const ImplJobSetup* pSetupData ) +{ + DEVMODEW const * pRet = nullptr; + SalDriverData const * pDrv = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData()); + if( pSetupData->GetDriverDataLen() >= sizeof(DEVMODEW)+sizeof(SalDriverData)-1 ) + pRet = reinterpret_cast<DEVMODEW const *>((pSetupData->GetDriverData()) + (pDrv->mnDriverOffset)); + return pRet; +} + +static PrintQueueFlags ImplWinQueueStatusToSal( DWORD nWinStatus ) +{ + PrintQueueFlags nStatus = PrintQueueFlags::NONE; + if ( nWinStatus & PRINTER_STATUS_PAUSED ) + nStatus |= PrintQueueFlags::Paused; + if ( nWinStatus & PRINTER_STATUS_ERROR ) + nStatus |= PrintQueueFlags::Error; + if ( nWinStatus & PRINTER_STATUS_PENDING_DELETION ) + nStatus |= PrintQueueFlags::PendingDeletion; + if ( nWinStatus & PRINTER_STATUS_PAPER_JAM ) + nStatus |= PrintQueueFlags::PaperJam; + if ( nWinStatus & PRINTER_STATUS_PAPER_OUT ) + nStatus |= PrintQueueFlags::PaperOut; + if ( nWinStatus & PRINTER_STATUS_MANUAL_FEED ) + nStatus |= PrintQueueFlags::ManualFeed; + if ( nWinStatus & PRINTER_STATUS_PAPER_PROBLEM ) + nStatus |= PrintQueueFlags::PaperProblem; + if ( nWinStatus & PRINTER_STATUS_OFFLINE ) + nStatus |= PrintQueueFlags::Offline; + if ( nWinStatus & PRINTER_STATUS_IO_ACTIVE ) + nStatus |= PrintQueueFlags::IOActive; + if ( nWinStatus & PRINTER_STATUS_BUSY ) + nStatus |= PrintQueueFlags::Busy; + if ( nWinStatus & PRINTER_STATUS_PRINTING ) + nStatus |= PrintQueueFlags::Printing; + if ( nWinStatus & PRINTER_STATUS_OUTPUT_BIN_FULL ) + nStatus |= PrintQueueFlags::OutputBinFull; + if ( nWinStatus & PRINTER_STATUS_WAITING ) + nStatus |= PrintQueueFlags::Waiting; + if ( nWinStatus & PRINTER_STATUS_PROCESSING ) + nStatus |= PrintQueueFlags::Processing; + if ( nWinStatus & PRINTER_STATUS_INITIALIZING ) + nStatus |= PrintQueueFlags::Initializing; + if ( nWinStatus & PRINTER_STATUS_WARMING_UP ) + nStatus |= PrintQueueFlags::WarmingUp; + if ( nWinStatus & PRINTER_STATUS_TONER_LOW ) + nStatus |= PrintQueueFlags::TonerLow; + if ( nWinStatus & PRINTER_STATUS_NO_TONER ) + nStatus |= PrintQueueFlags::NoToner; + if ( nWinStatus & PRINTER_STATUS_PAGE_PUNT ) + nStatus |= PrintQueueFlags::PagePunt; + if ( nWinStatus & PRINTER_STATUS_USER_INTERVENTION ) + nStatus |= PrintQueueFlags::UserIntervention; + if ( nWinStatus & PRINTER_STATUS_OUT_OF_MEMORY ) + nStatus |= PrintQueueFlags::OutOfMemory; + if ( nWinStatus & PRINTER_STATUS_DOOR_OPEN ) + nStatus |= PrintQueueFlags::DoorOpen; + if ( nWinStatus & PRINTER_STATUS_SERVER_UNKNOWN ) + nStatus |= PrintQueueFlags::StatusUnknown; + if ( nWinStatus & PRINTER_STATUS_POWER_SAVE ) + nStatus |= PrintQueueFlags::PowerSave; + if ( nStatus == PrintQueueFlags::NONE && !(nWinStatus & PRINTER_STATUS_NOT_AVAILABLE) ) + nStatus |= PrintQueueFlags::Ready; + return nStatus; +} + + +void WinSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList ) +{ + DWORD i; + DWORD nBytes = 0; + DWORD nInfoPrn4 = 0; + EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, nullptr, 0, &nBytes, &nInfoPrn4 ); + if ( nBytes ) + { + PRINTER_INFO_4W* pWinInfo4 = static_cast<PRINTER_INFO_4W*>(std::malloc( nBytes )); + if ( EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, reinterpret_cast<LPBYTE>(pWinInfo4), nBytes, &nBytes, &nInfoPrn4 ) ) + { + for ( i = 0; i < nInfoPrn4; i++ ) + { + std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo); + pInfo->maPrinterName = o3tl::toU(pWinInfo4[i].pPrinterName); + pInfo->mnStatus = PrintQueueFlags::NONE; + pInfo->mnJobs = 0; + pList->Add( std::move(pInfo) ); + } + } + std::free( pWinInfo4 ); + } +} + +void WinSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo ) +{ + HANDLE hPrinter = nullptr; + LPWSTR pPrnName = const_cast<LPWSTR>(o3tl::toW(pInfo->maPrinterName.getStr())); + if( OpenPrinterW( pPrnName, &hPrinter, nullptr ) ) + { + DWORD nBytes = 0; + GetPrinterW( hPrinter, 2, nullptr, 0, &nBytes ); + if( nBytes ) + { + PRINTER_INFO_2W* pWinInfo2 = static_cast<PRINTER_INFO_2W*>(std::malloc(nBytes)); + if( GetPrinterW( hPrinter, 2, reinterpret_cast<LPBYTE>(pWinInfo2), nBytes, &nBytes ) ) + { + if( pWinInfo2->pDriverName ) + pInfo->maDriver = o3tl::toU(pWinInfo2->pDriverName); + OUString aPortName; + if ( pWinInfo2->pPortName ) + aPortName = o3tl::toU(pWinInfo2->pPortName); + // pLocation can be 0 (the Windows docu doesn't describe this) + if ( pWinInfo2->pLocation && *pWinInfo2->pLocation ) + pInfo->maLocation = o3tl::toU(pWinInfo2->pLocation); + else + pInfo->maLocation = aPortName; + // pComment can be 0 (the Windows docu doesn't describe this) + if ( pWinInfo2->pComment ) + pInfo->maComment = o3tl::toU(pWinInfo2->pComment); + pInfo->mnStatus = ImplWinQueueStatusToSal( pWinInfo2->Status ); + pInfo->mnJobs = pWinInfo2->cJobs; + if( ! pInfo->moPortName ) + pInfo->moPortName = aPortName; + } + std::free(pWinInfo2); + } + ClosePrinter( hPrinter ); + } +} + +OUString WinSalInstance::GetDefaultPrinter() +{ + DWORD nChars = 0; + GetDefaultPrinterW( nullptr, &nChars ); + if( nChars ) + { + std::vector<WCHAR> pStr(nChars); + if (GetDefaultPrinterW(pStr.data(), &nChars)) + return OUString(o3tl::toU(pStr.data())); + } + return OUString(); +} + +static DWORD ImplDeviceCaps( WinSalInfoPrinter const * pPrinter, WORD nCaps, + BYTE* pOutput, const ImplJobSetup* pSetupData ) +{ + DEVMODEW const * pDevMode; + if ( !pSetupData || !pSetupData->GetDriverData() ) + pDevMode = nullptr; + else + pDevMode = SAL_DEVMODE_W( pSetupData ); + + return DeviceCapabilitiesW( o3tl::toW(pPrinter->maDeviceName.getStr()), + o3tl::toW(pPrinter->maPortName.getStr()), + nCaps, reinterpret_cast<LPWSTR>(pOutput), pDevMode ); +} + +static bool ImplTestSalJobSetup( WinSalInfoPrinter const * pPrinter, + ImplJobSetup* pSetupData, bool bDelete ) +{ + if ( pSetupData && pSetupData->GetDriverData() ) + { + // 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 + DEVMODEW const * pDevModeW = nullptr; + LONG dmSpecVersion = -1; + LONG dmDriverVersion = -1; + SalDriverData const * pSalDriverData = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData()); + BYTE const * pDriverData = reinterpret_cast<BYTE const *>(pSalDriverData) + pSalDriverData->mnDriverOffset; + pDevModeW = reinterpret_cast<DEVMODEW const *>(pDriverData); + + LONG nSysJobSize = -1; + if( pPrinter && 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# + HANDLE hPrn; + LPWSTR pPrinterNameW = const_cast<LPWSTR>(o3tl::toW(pPrinter->maDeviceName.getStr())); + if ( !OpenPrinterW( pPrinterNameW, &hPrn, nullptr ) ) + return false; + + // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded! + if( hPrn == HGDI_ERROR ) + return false; + + nSysJobSize = DocumentPropertiesW( nullptr, hPrn, + pPrinterNameW, + nullptr, nullptr, 0 ); + + if( nSysJobSize < 0 ) + { + ClosePrinter( hPrn ); + return false; + } + DEVMODEW *pBuffer = static_cast<DEVMODEW*>(_alloca( nSysJobSize )); + LONG nRet = DocumentPropertiesW( nullptr, hPrn, + pPrinterNameW, + pBuffer, nullptr, 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 = pBuffer->dmSpecVersion; + dmDriverVersion = pBuffer->dmDriverVersion; + + ClosePrinter( hPrn ); + } + SalDriverData const * pSetupDriverData = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData()); + if ( (pSetupData->GetSystem() == JOBSETUP_SYSTEM_WINDOWS) && + (pPrinter->maDriverName == pSetupData->GetDriver()) && + (pSetupData->GetDriverDataLen() > sizeof( SalDriverData )) && + static_cast<tools::Long>(pSetupData->GetDriverDataLen() - pSetupDriverData->mnDriverOffset) == nSysJobSize && + pSetupDriverData->mnSysSignature == SAL_DRIVERDATA_SYSSIGN ) + { + if( pDevModeW && + (dmSpecVersion == pDevModeW->dmSpecVersion) && + (dmDriverVersion == pDevModeW->dmDriverVersion) ) + return true; + } + if ( bDelete ) + { + pSetupData->SetDriverData( nullptr, 0 ); + } + } + + return false; +} + +static bool ImplUpdateSalJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSetup* pSetupData, + bool bIn, weld::Window* pVisibleDlgParent ) +{ + HANDLE hPrn; + LPWSTR pPrinterNameW = const_cast<LPWSTR>(o3tl::toW(pPrinter->maDeviceName.getStr())); + if ( !OpenPrinterW( pPrinterNameW, &hPrn, nullptr ) ) + return false; + // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded! + if( hPrn == HGDI_ERROR ) + return false; + + LONG nRet; + HWND hWnd = nullptr; + DWORD nMode = DM_OUT_BUFFER; + std::unique_ptr<sal_uInt8[]> pDriverData; + SalDriverData* pOutBuffer = nullptr; + BYTE const * pInBuffer = nullptr; + + LONG nSysJobSize = DocumentPropertiesW( hWnd, hPrn, + pPrinterNameW, + nullptr, nullptr, 0 ); + if ( nSysJobSize < 0 ) + { + ClosePrinter( hPrn ); + return false; + } + + // make Outputbuffer + const std::size_t nDriverDataLen = sizeof(SalDriverData) + nSysJobSize-1; + pDriverData = std::make_unique<sal_uInt8[]>( nDriverDataLen ); + memset(pDriverData.get(), 0, nDriverDataLen); + pOutBuffer = reinterpret_cast<SalDriverData*>(pDriverData.get()); + pOutBuffer->mnSysSignature = SAL_DRIVERDATA_SYSSIGN; + // calculate driver data offset including structure padding + pOutBuffer->mnDriverOffset = sal::static_int_cast<sal_uInt16>( + reinterpret_cast<char*>(pOutBuffer->maDriverData) - + reinterpret_cast<char*>(pOutBuffer) ); + + // check if we have a suitable input buffer + if ( bIn && ImplTestSalJobSetup( pPrinter, pSetupData, false ) ) + { + pInBuffer = pSetupData->GetDriverData() + reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData())->mnDriverOffset; + nMode |= DM_IN_BUFFER; + } + + // check if the dialog should be shown + if ( pVisibleDlgParent ) + { + hWnd = pVisibleDlgParent->get_system_data().hWnd; + nMode |= DM_IN_PROMPT; + } + + // Release mutex, in the other case we don't get paints and so on + sal_uInt32 nMutexCount = 0; + WinSalInstance* pInst = GetSalData()->mpInstance; + if ( pInst && pVisibleDlgParent ) + nMutexCount = pInst->ReleaseYieldMutexAll(); + + BYTE* pOutDevMode = reinterpret_cast<BYTE*>(pOutBuffer) + pOutBuffer->mnDriverOffset; + nRet = DocumentPropertiesW( hWnd, hPrn, + pPrinterNameW, + reinterpret_cast<LPDEVMODEW>(pOutDevMode), reinterpret_cast<LPDEVMODEW>(const_cast<BYTE *>(pInBuffer)), nMode ); + if ( pInst && pVisibleDlgParent ) + pInst->AcquireYieldMutex( nMutexCount ); + ClosePrinter( hPrn ); + + if( (nRet < 0) || (pVisibleDlgParent && (nRet == IDCANCEL)) ) + return false; + + // fill up string buffers with 0 so they do not influence a JobSetup's memcmp + if( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmSize >= 64 ) + { + sal_Int32 nLen = rtl_ustr_getLength( o3tl::toU(reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName) ); + if ( sal::static_int_cast<size_t>(nLen) < SAL_N_ELEMENTS( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName ) ) + memset( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName+nLen, 0, sizeof( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName )-(nLen*sizeof(sal_Unicode)) ); + } + if( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmSize >= 166 ) + { + sal_Int32 nLen = rtl_ustr_getLength( o3tl::toU(reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName) ); + if ( sal::static_int_cast<size_t>(nLen) < SAL_N_ELEMENTS( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName ) ) + memset( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName+nLen, 0, sizeof( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName )-(nLen*sizeof(sal_Unicode)) ); + } + + // update data + pSetupData->SetDriverData(std::move(pDriverData), nDriverDataLen); + pSetupData->SetSystem( JOBSETUP_SYSTEM_WINDOWS ); + + return true; +} + +static void ImplDevModeToJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSetup* pSetupData, JobSetFlags nFlags ) +{ + if ( !pSetupData || !pSetupData->GetDriverData() ) + return; + + DEVMODEW const * pDevModeW = SAL_DEVMODE_W(pSetupData); + if( pDevModeW == nullptr ) + return; + + // Orientation + if ( nFlags & JobSetFlags::ORIENTATION ) + { + if ( pDevModeW->dmOrientation == DMORIENT_PORTRAIT ) + pSetupData->SetOrientation( Orientation::Portrait ); + else if ( pDevModeW->dmOrientation == DMORIENT_LANDSCAPE ) + pSetupData->SetOrientation( Orientation::Landscape ); + } + + // PaperBin + if ( nFlags & JobSetFlags::PAPERBIN ) + { + const DWORD nCount = ImplDeviceCaps( pPrinter, DC_BINS, nullptr, pSetupData ); + + if ( nCount && (nCount != GDI_ERROR) ) + { + WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory( nCount*sizeof(WORD) )); + ImplDeviceCaps( pPrinter, DC_BINS, reinterpret_cast<BYTE*>(pBins), pSetupData ); + pSetupData->SetPaperBin( 0 ); + + // search the right bin and assign index to mnPaperBin + for( DWORD i = 0; i < nCount; ++i ) + { + if( pDevModeW->dmDefaultSource == pBins[ i ] ) + { + pSetupData->SetPaperBin( static_cast<sal_uInt16>(i) ); + break; + } + } + + std::free( pBins ); + } + } + + // PaperSize + if ( nFlags & JobSetFlags::PAPERSIZE ) + { + if( (pDevModeW->dmFields & (DM_PAPERWIDTH|DM_PAPERLENGTH)) == (DM_PAPERWIDTH|DM_PAPERLENGTH) ) + { + pSetupData->SetPaperWidth( pDevModeW->dmPaperWidth*10 ); + pSetupData->SetPaperHeight( pDevModeW->dmPaperLength*10 ); + } + else + { + const DWORD nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, nullptr, pSetupData ); + WORD* pPapers = nullptr; + const DWORD nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, nullptr, pSetupData ); + POINT* pPaperSizes = nullptr; + if ( nPaperCount && (nPaperCount != GDI_ERROR) ) + { + pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD))); + ImplDeviceCaps( pPrinter, DC_PAPERS, reinterpret_cast<BYTE*>(pPapers), pSetupData ); + } + if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) ) + { + pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT))); + ImplDeviceCaps( pPrinter, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData ); + } + if( nPaperSizeCount == nPaperCount && pPaperSizes && pPapers ) + { + for( DWORD i = 0; i < nPaperCount; ++i ) + { + if( pPapers[ i ] == pDevModeW->dmPaperSize ) + { + pSetupData->SetPaperWidth( pPaperSizes[ i ].x*10 ); + pSetupData->SetPaperHeight( pPaperSizes[ i ].y*10 ); + break; + } + } + } + if( pPapers ) + std::free( pPapers ); + if( pPaperSizes ) + std::free( pPaperSizes ); + } + switch( pDevModeW->dmPaperSize ) + { + case DMPAPER_LETTER: + pSetupData->SetPaperFormat( PAPER_LETTER ); + break; + case DMPAPER_TABLOID: + pSetupData->SetPaperFormat( PAPER_TABLOID ); + break; + case DMPAPER_LEDGER: + pSetupData->SetPaperFormat( PAPER_LEDGER ); + break; + case DMPAPER_LEGAL: + pSetupData->SetPaperFormat( PAPER_LEGAL ); + break; + case DMPAPER_STATEMENT: + pSetupData->SetPaperFormat( PAPER_STATEMENT ); + break; + case DMPAPER_EXECUTIVE: + pSetupData->SetPaperFormat( PAPER_EXECUTIVE ); + break; + case DMPAPER_A3: + pSetupData->SetPaperFormat( PAPER_A3 ); + break; + case DMPAPER_A4: + pSetupData->SetPaperFormat( PAPER_A4 ); + break; + case DMPAPER_A5: + pSetupData->SetPaperFormat( PAPER_A5 ); + break; + //See http://wiki.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 x 364 or ISO 250 x 353 + //(cmc) + case DMPAPER_B4: + pSetupData->SetPaperFormat( PAPER_B4_JIS ); + break; + case DMPAPER_B5: + pSetupData->SetPaperFormat( PAPER_B5_JIS ); + break; + case DMPAPER_QUARTO: + pSetupData->SetPaperFormat( PAPER_QUARTO ); + break; + case DMPAPER_10X14: + pSetupData->SetPaperFormat( PAPER_10x14 ); + break; + case DMPAPER_NOTE: + pSetupData->SetPaperFormat( PAPER_LETTER ); + break; + case DMPAPER_ENV_9: + pSetupData->SetPaperFormat( PAPER_ENV_9 ); + break; + case DMPAPER_ENV_10: + pSetupData->SetPaperFormat( PAPER_ENV_10 ); + break; + case DMPAPER_ENV_11: + pSetupData->SetPaperFormat( PAPER_ENV_11 ); + break; + case DMPAPER_ENV_12: + pSetupData->SetPaperFormat( PAPER_ENV_12 ); + break; + case DMPAPER_ENV_14: + pSetupData->SetPaperFormat( PAPER_ENV_14 ); + break; + case DMPAPER_CSHEET: + pSetupData->SetPaperFormat( PAPER_C ); + break; + case DMPAPER_DSHEET: + pSetupData->SetPaperFormat( PAPER_D ); + break; + case DMPAPER_ESHEET: + pSetupData->SetPaperFormat( PAPER_E ); + break; + case DMPAPER_ENV_DL: + pSetupData->SetPaperFormat( PAPER_ENV_DL ); + break; + case DMPAPER_ENV_C5: + pSetupData->SetPaperFormat( PAPER_ENV_C5 ); + break; + case DMPAPER_ENV_C3: + pSetupData->SetPaperFormat( PAPER_ENV_C3 ); + break; + case DMPAPER_ENV_C4: + pSetupData->SetPaperFormat( PAPER_ENV_C4 ); + break; + case DMPAPER_ENV_C6: + pSetupData->SetPaperFormat( PAPER_ENV_C6 ); + break; + case DMPAPER_ENV_C65: + pSetupData->SetPaperFormat( PAPER_ENV_C65 ); + break; + case DMPAPER_ENV_ITALY: + pSetupData->SetPaperFormat( PAPER_ENV_ITALY ); + break; + case DMPAPER_ENV_MONARCH: + pSetupData->SetPaperFormat( PAPER_ENV_MONARCH ); + break; + case DMPAPER_ENV_PERSONAL: + pSetupData->SetPaperFormat( PAPER_ENV_PERSONAL ); + break; + case DMPAPER_FANFOLD_US: + pSetupData->SetPaperFormat( PAPER_FANFOLD_US ); + break; + case DMPAPER_FANFOLD_STD_GERMAN: + pSetupData->SetPaperFormat( PAPER_FANFOLD_DE ); + break; + case DMPAPER_FANFOLD_LGL_GERMAN: + pSetupData->SetPaperFormat( PAPER_FANFOLD_LEGAL_DE ); + break; + case DMPAPER_ISO_B4: + pSetupData->SetPaperFormat( PAPER_B4_ISO ); + break; + case DMPAPER_JAPANESE_POSTCARD: + pSetupData->SetPaperFormat( PAPER_POSTCARD_JP ); + break; + case DMPAPER_9X11: + pSetupData->SetPaperFormat( PAPER_9x11 ); + break; + case DMPAPER_10X11: + pSetupData->SetPaperFormat( PAPER_10x11 ); + break; + case DMPAPER_15X11: + pSetupData->SetPaperFormat( PAPER_15x11 ); + break; + case DMPAPER_ENV_INVITE: + pSetupData->SetPaperFormat( PAPER_ENV_INVITE ); + break; + case DMPAPER_A_PLUS: + pSetupData->SetPaperFormat( PAPER_A_PLUS ); + break; + case DMPAPER_B_PLUS: + pSetupData->SetPaperFormat( PAPER_B_PLUS ); + break; + case DMPAPER_LETTER_PLUS: + pSetupData->SetPaperFormat( PAPER_LETTER_PLUS ); + break; + case DMPAPER_A4_PLUS: + pSetupData->SetPaperFormat( PAPER_A4_PLUS ); + break; + case DMPAPER_A2: + pSetupData->SetPaperFormat( PAPER_A2 ); + break; + case DMPAPER_DBL_JAPANESE_POSTCARD: + pSetupData->SetPaperFormat( PAPER_DOUBLEPOSTCARD_JP ); + break; + case DMPAPER_A6: + pSetupData->SetPaperFormat( PAPER_A6 ); + break; + case DMPAPER_B6_JIS: + pSetupData->SetPaperFormat( PAPER_B6_JIS ); + break; + case DMPAPER_12X11: + pSetupData->SetPaperFormat( PAPER_12x11 ); + break; + default: + pSetupData->SetPaperFormat( PAPER_USER ); + break; + } + } + + if( nFlags & JobSetFlags::DUPLEXMODE ) + { + DuplexMode eDuplex = DuplexMode::Unknown; + if( pDevModeW->dmFields & DM_DUPLEX ) + { + if( pDevModeW->dmDuplex == DMDUP_SIMPLEX ) + eDuplex = DuplexMode::Off; + else if( pDevModeW->dmDuplex == DMDUP_VERTICAL ) + eDuplex = DuplexMode::LongEdge; + else if( pDevModeW->dmDuplex == DMDUP_HORIZONTAL ) + eDuplex = DuplexMode::ShortEdge; + } + pSetupData->SetDuplexMode( eDuplex ); + } +} + +static void ImplJobSetupToDevMode( WinSalInfoPrinter const * pPrinter, const ImplJobSetup* pSetupData, JobSetFlags nFlags ) +{ + if ( !pSetupData || !pSetupData->GetDriverData() ) + return; + + DEVMODEW* pDevModeW = const_cast<DEVMODEW *>(SAL_DEVMODE_W(pSetupData)); + if( pDevModeW == nullptr ) + return; + + // Orientation + if ( nFlags & JobSetFlags::ORIENTATION ) + { + pDevModeW->dmFields |= DM_ORIENTATION; + if ( pSetupData->GetOrientation() == Orientation::Portrait ) + pDevModeW->dmOrientation = DMORIENT_PORTRAIT; + else + pDevModeW->dmOrientation = DMORIENT_LANDSCAPE; + } + + // PaperBin + if ( nFlags & JobSetFlags::PAPERBIN ) + { + const DWORD nCount = ImplDeviceCaps( pPrinter, DC_BINS, nullptr, pSetupData ); + + if ( nCount && (nCount != GDI_ERROR) ) + { + WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory(nCount*sizeof(WORD))); + ImplDeviceCaps( pPrinter, DC_BINS, reinterpret_cast<BYTE*>(pBins), pSetupData ); + pDevModeW->dmFields |= DM_DEFAULTSOURCE; + pDevModeW->dmDefaultSource = pBins[ pSetupData->GetPaperBin() ]; + std::free( pBins ); + } + } + + // PaperSize + if ( nFlags & JobSetFlags::PAPERSIZE ) + { + pDevModeW->dmFields |= DM_PAPERSIZE; + pDevModeW->dmPaperWidth = 0; + pDevModeW->dmPaperLength = 0; + + switch( pSetupData->GetPaperFormat() ) + { + case PAPER_A2: + pDevModeW->dmPaperSize = DMPAPER_A2; + break; + case PAPER_A3: + pDevModeW->dmPaperSize = DMPAPER_A3; + break; + case PAPER_A4: + pDevModeW->dmPaperSize = DMPAPER_A4; + break; + case PAPER_A5: + pDevModeW->dmPaperSize = DMPAPER_A5; + break; + case PAPER_B4_ISO: + pDevModeW->dmPaperSize = DMPAPER_ISO_B4; + break; + case PAPER_LETTER: + pDevModeW->dmPaperSize = DMPAPER_LETTER; + break; + case PAPER_LEGAL: + pDevModeW->dmPaperSize = DMPAPER_LEGAL; + break; + case PAPER_TABLOID: + pDevModeW->dmPaperSize = DMPAPER_TABLOID; + break; + + // 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_ENV_C4: + pDevModeW->dmPaperSize = DMPAPER_ENV_C4; + break; + case PAPER_ENV_C5: + pDevModeW->dmPaperSize = DMPAPER_ENV_C5; + break; + case PAPER_ENV_C6: + pDevModeW->dmPaperSize = DMPAPER_ENV_C6; + break; + case PAPER_ENV_C65: + pDevModeW->dmPaperSize = DMPAPER_ENV_C65; + break; + case PAPER_ENV_DL: + pDevModeW->dmPaperSize = DMPAPER_ENV_DL; + break; + case PAPER_C: + pDevModeW->dmPaperSize = DMPAPER_CSHEET; + break; + case PAPER_D: + pDevModeW->dmPaperSize = DMPAPER_DSHEET; + break; + case PAPER_E: + pDevModeW->dmPaperSize = DMPAPER_ESHEET; + break; + case PAPER_EXECUTIVE: + pDevModeW->dmPaperSize = DMPAPER_EXECUTIVE; + break; + case PAPER_FANFOLD_LEGAL_DE: + pDevModeW->dmPaperSize = DMPAPER_FANFOLD_LGL_GERMAN; + break; + case PAPER_ENV_MONARCH: + pDevModeW->dmPaperSize = DMPAPER_ENV_MONARCH; + break; + case PAPER_ENV_PERSONAL: + pDevModeW->dmPaperSize = DMPAPER_ENV_PERSONAL; + break; + case PAPER_ENV_9: + pDevModeW->dmPaperSize = DMPAPER_ENV_9; + break; + case PAPER_ENV_10: + pDevModeW->dmPaperSize = DMPAPER_ENV_10; + break; + case PAPER_ENV_11: + pDevModeW->dmPaperSize = DMPAPER_ENV_11; + break; + case PAPER_ENV_12: + pDevModeW->dmPaperSize = DMPAPER_ENV_12; + break; + //See the comments on DMPAPER_B4 above + case PAPER_B4_JIS: + pDevModeW->dmPaperSize = DMPAPER_B4; + break; + case PAPER_B5_JIS: + pDevModeW->dmPaperSize = DMPAPER_B5; + break; + case PAPER_B6_JIS: + pDevModeW->dmPaperSize = DMPAPER_B6_JIS; + break; + case PAPER_LEDGER: + pDevModeW->dmPaperSize = DMPAPER_LEDGER; + break; + case PAPER_STATEMENT: + pDevModeW->dmPaperSize = DMPAPER_STATEMENT; + break; + case PAPER_10x14: + pDevModeW->dmPaperSize = DMPAPER_10X14; + break; + case PAPER_ENV_14: + pDevModeW->dmPaperSize = DMPAPER_ENV_14; + break; + case PAPER_ENV_C3: + pDevModeW->dmPaperSize = DMPAPER_ENV_C3; + break; + case PAPER_ENV_ITALY: + pDevModeW->dmPaperSize = DMPAPER_ENV_ITALY; + break; + case PAPER_FANFOLD_US: + pDevModeW->dmPaperSize = DMPAPER_FANFOLD_US; + break; + case PAPER_FANFOLD_DE: + pDevModeW->dmPaperSize = DMPAPER_FANFOLD_STD_GERMAN; + break; + case PAPER_POSTCARD_JP: + pDevModeW->dmPaperSize = DMPAPER_JAPANESE_POSTCARD; + break; + case PAPER_9x11: + pDevModeW->dmPaperSize = DMPAPER_9X11; + break; + case PAPER_10x11: + pDevModeW->dmPaperSize = DMPAPER_10X11; + break; + case PAPER_15x11: + pDevModeW->dmPaperSize = DMPAPER_15X11; + break; + case PAPER_ENV_INVITE: + pDevModeW->dmPaperSize = DMPAPER_ENV_INVITE; + break; + case PAPER_A_PLUS: + pDevModeW->dmPaperSize = DMPAPER_A_PLUS; + break; + case PAPER_B_PLUS: + pDevModeW->dmPaperSize = DMPAPER_B_PLUS; + break; + case PAPER_LETTER_PLUS: + pDevModeW->dmPaperSize = DMPAPER_LETTER_PLUS; + break; + case PAPER_A4_PLUS: + pDevModeW->dmPaperSize = DMPAPER_A4_PLUS; + break; + case PAPER_DOUBLEPOSTCARD_JP: + pDevModeW->dmPaperSize = DMPAPER_DBL_JAPANESE_POSTCARD; + break; + case PAPER_A6: + pDevModeW->dmPaperSize = DMPAPER_A6; + break; + case PAPER_12x11: + pDevModeW->dmPaperSize = DMPAPER_12X11; + break; + default: + { + short nPaper = 0; + const DWORD nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, nullptr, pSetupData ); + WORD* pPapers = nullptr; + const DWORD nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, nullptr, pSetupData ); + POINT* pPaperSizes = nullptr; + DWORD nLandscapeAngle = ImplDeviceCaps( pPrinter, DC_ORIENTATION, nullptr, pSetupData ); + if ( nPaperCount && (nPaperCount != GDI_ERROR) ) + { + pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD))); + ImplDeviceCaps( pPrinter, DC_PAPERS, reinterpret_cast<BYTE*>(pPapers), pSetupData ); + } + if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) ) + { + pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT))); + ImplDeviceCaps( pPrinter, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData ); + } + if ( (nPaperSizeCount == nPaperCount) && pPapers && pPaperSizes ) + { + PaperInfo aInfo(pSetupData->GetPaperWidth(), pSetupData->GetPaperHeight()); + // compare paper formats and select a good match + for ( DWORD 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->GetPaperHeight(), pSetupData->GetPaperWidth()); + for ( DWORD i = 0; i < nPaperCount; ++i ) + { + if ( aRotatedInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)) ) + { + nPaper = pPapers[i]; + break; + } + } + } + + if ( nPaper ) + pDevModeW->dmPaperSize = nPaper; + } + + if ( !nPaper ) + { + pDevModeW->dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH; + pDevModeW->dmPaperSize = DMPAPER_USER; + pDevModeW->dmPaperWidth = static_cast<short>(pSetupData->GetPaperWidth()/10); + pDevModeW->dmPaperLength = static_cast<short>(pSetupData->GetPaperHeight()/10); + } + + if ( pPapers ) + std::free(pPapers); + if ( pPaperSizes ) + std::free(pPaperSizes); + + break; + } + } + } + if( nFlags & JobSetFlags::DUPLEXMODE ) + { + switch( pSetupData->GetDuplexMode() ) + { + case DuplexMode::Off: + pDevModeW->dmFields |= DM_DUPLEX; + pDevModeW->dmDuplex = DMDUP_SIMPLEX; + break; + case DuplexMode::ShortEdge: + pDevModeW->dmFields |= DM_DUPLEX; + pDevModeW->dmDuplex = DMDUP_HORIZONTAL; + break; + case DuplexMode::LongEdge: + pDevModeW->dmFields |= DM_DUPLEX; + pDevModeW->dmDuplex = DMDUP_VERTICAL; + break; + case DuplexMode::Unknown: + break; + } + } +} + +static HDC ImplCreateICW_WithCatch( LPWSTR pDriver, + LPCWSTR pDevice, + DEVMODEW const * pDevMode ) +{ + HDC hDC = nullptr; + CATCH_DRIVER_EX_BEGIN; + hDC = CreateICW( pDriver, pDevice, nullptr, pDevMode ); + CATCH_DRIVER_EX_END_2( "exception in CreateICW" ); + return hDC; +} + +static HDC ImplCreateSalPrnIC( WinSalInfoPrinter const * pPrinter, const ImplJobSetup* pSetupData ) +{ + HDC hDC = nullptr; + DEVMODEW const * pDevMode; + if ( pSetupData && pSetupData->GetDriverData() ) + pDevMode = SAL_DEVMODE_W( pSetupData ); + else + pDevMode = nullptr; + // #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.getLength() > 2048 || pPrinter->maDeviceName.getLength() > 2048 ) + return nullptr; + sal_Unicode pDriverName[ 4096 ]; + sal_Unicode pDeviceName[ 4096 ]; + memcpy( pDriverName, pPrinter->maDriverName.getStr(), pPrinter->maDriverName.getLength()*sizeof(sal_Unicode)); + memset( pDriverName+pPrinter->maDriverName.getLength(), 0, 32 ); + memcpy( pDeviceName, pPrinter->maDeviceName.getStr(), pPrinter->maDeviceName.getLength()*sizeof(sal_Unicode)); + memset( pDeviceName+pPrinter->maDeviceName.getLength(), 0, 32 ); + hDC = ImplCreateICW_WithCatch( o3tl::toW(pDriverName), + o3tl::toW(pDeviceName), + pDevMode ); + return hDC; +} + +static WinSalGraphics* ImplCreateSalPrnGraphics( HDC hDC ) +{ + WinSalGraphics* pGraphics = new WinSalGraphics(WinSalGraphics::PRINTER, false, nullptr, /* CHECKME */ nullptr); + pGraphics->SetLayout( SalLayoutFlags::NONE ); + pGraphics->setHDC(hDC); + return pGraphics; +} + +static bool ImplUpdateSalPrnIC( WinSalInfoPrinter* pPrinter, const ImplJobSetup* pSetupData ) +{ + HDC hNewDC = ImplCreateSalPrnIC( pPrinter, pSetupData ); + if ( !hNewDC ) + return false; + + pPrinter->setHDC(hNewDC); + return true; +} + + +SalInfoPrinter* WinSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo, + ImplJobSetup* pSetupData ) +{ + WinSalInfoPrinter* pPrinter = new WinSalInfoPrinter; + if( ! pQueueInfo->moPortName ) + GetPrinterQueueState( pQueueInfo ); + pPrinter->maDriverName = pQueueInfo->maDriver; + pPrinter->maDeviceName = pQueueInfo->maPrinterName; + pPrinter->maPortName = pQueueInfo->moPortName ? *pQueueInfo->moPortName : OUString(); + + // check if the provided setup data match the actual printer + ImplTestSalJobSetup( pPrinter, pSetupData, true ); + + HDC hDC = ImplCreateSalPrnIC( pPrinter, pSetupData ); + if ( !hDC ) + { + delete pPrinter; + return nullptr; + } + + pPrinter->setHDC(hDC); + if ( !pSetupData->GetDriverData() ) + ImplUpdateSalJobSetup( pPrinter, pSetupData, false, nullptr ); + ImplDevModeToJobSetup( pPrinter, pSetupData, JobSetFlags::ALL ); + pSetupData->SetSystem( JOBSETUP_SYSTEM_WINDOWS ); + + return pPrinter; +} + +void WinSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter ) +{ + delete pPrinter; +} + + +WinSalInfoPrinter::WinSalInfoPrinter() : + m_hDC(nullptr), + m_pGraphics(nullptr), + m_bGraphics(false) +{ + m_bPapersInit = false; +} + +WinSalInfoPrinter::~WinSalInfoPrinter() +{ + setHDC(nullptr); +} + +void WinSalInfoPrinter::setHDC(HDC hNewDC) +{ + assert(!m_bGraphics); + + if (m_hDC) + { + assert(!m_pGraphics || m_hDC == m_pGraphics->getHDC()); + delete m_pGraphics; + m_pGraphics = nullptr; + DeleteDC(m_hDC); + } + + m_hDC = hNewDC; +} + +void WinSalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData ) +{ + m_aPaperFormats.clear(); + + DWORD nCount = ImplDeviceCaps( this, DC_PAPERSIZE, nullptr, pSetupData ); + if( nCount == GDI_ERROR ) + nCount = 0; + + if( nCount ) + { + POINT* pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nCount*sizeof(POINT))); + ImplDeviceCaps( this, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData ); + + sal_Unicode* pNamesBuffer = static_cast<sal_Unicode*>(std::malloc(nCount*64*sizeof(sal_Unicode))); + ImplDeviceCaps( this, DC_PAPERNAMES, reinterpret_cast<BYTE*>(pNamesBuffer), pSetupData ); + + SAL_INFO("vcl.print", "DC_PAPERSIZE sizes (mm) from printer: " << DC_PAPERSIZE_array_to_string(pPaperSizes, nCount)); + + for( DWORD i = 0; i < nCount; ++i ) + { + PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10); + m_aPaperFormats.push_back( aInfo ); + } + std::free( pNamesBuffer ); + std::free( pPaperSizes ); + } + + m_bPapersInit = true; +} + +int WinSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* pSetupData ) +{ + const DWORD nRet = ImplDeviceCaps( this, DC_ORIENTATION, nullptr, pSetupData ); + + if( nRet != GDI_ERROR ) + return static_cast<int>(nRet) * 10; + return 900; // guess +} + +SalGraphics* WinSalInfoPrinter::AcquireGraphics() +{ + assert(m_hDC); + if (m_bGraphics) + return nullptr; + + if (!m_pGraphics) + m_pGraphics = ImplCreateSalPrnGraphics(m_hDC); + if (m_pGraphics) + m_bGraphics = true; + + return m_pGraphics; +} + +void WinSalInfoPrinter::ReleaseGraphics( SalGraphics* ) +{ + m_bGraphics = false; +} + +bool WinSalInfoPrinter::Setup(weld::Window* pFrame, ImplJobSetup* pSetupData) +{ + if ( ImplUpdateSalJobSetup(this, pSetupData, true, pFrame)) + { + ImplDevModeToJobSetup( this, pSetupData, JobSetFlags::ALL ); + return ImplUpdateSalPrnIC( this, pSetupData ); + } + + return false; +} + +bool WinSalInfoPrinter::SetPrinterData( ImplJobSetup* pSetupData ) +{ + if ( !ImplTestSalJobSetup( this, pSetupData, false ) ) + return false; + return ImplUpdateSalPrnIC( this, pSetupData ); +} + +bool WinSalInfoPrinter::SetData( JobSetFlags nFlags, ImplJobSetup* pSetupData ) +{ + ImplJobSetupToDevMode( this, pSetupData, nFlags ); + if ( ImplUpdateSalJobSetup( this, pSetupData, true, nullptr ) ) + { + ImplDevModeToJobSetup( this, pSetupData, nFlags ); + return ImplUpdateSalPrnIC( this, pSetupData ); + } + + return false; +} + +sal_uInt16 WinSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pSetupData ) +{ + DWORD nRet = ImplDeviceCaps( this, DC_BINS, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + return nRet; + else + return 0; +} + +OUString WinSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pSetupData, sal_uInt16 nPaperBin ) +{ + OUString aPaperBinName; + + DWORD nBins = ImplDeviceCaps( this, DC_BINNAMES, nullptr, pSetupData ); + if ( (nPaperBin < nBins) && (nBins != GDI_ERROR) ) + { + auto pBuffer = std::make_unique<sal_Unicode[]>(nBins*24); + DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, reinterpret_cast<BYTE*>(pBuffer.get()), pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + aPaperBinName = OUString( pBuffer.get() + (nPaperBin*24) ); + } + + return aPaperBinName; +} + +sal_uInt32 WinSalInfoPrinter::GetCapabilities( const ImplJobSetup* pSetupData, PrinterCapType nType ) +{ + DWORD nRet; + + switch ( nType ) + { + case PrinterCapType::SupportDialog: + return TRUE; + case PrinterCapType::Copies: + nRet = ImplDeviceCaps( this, DC_COPIES, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + return nRet; + return 0; + case PrinterCapType::CollateCopies: + nRet = ImplDeviceCaps( this, DC_COLLATE, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + { + nRet = ImplDeviceCaps( this, DC_COPIES, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + return nRet; + } + return 0; + + case PrinterCapType::SetOrientation: + nRet = ImplDeviceCaps( this, DC_ORIENTATION, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + return TRUE; + return FALSE; + + case PrinterCapType::SetPaperSize: + case PrinterCapType::SetPaper: + nRet = ImplDeviceCaps( this, DC_PAPERS, nullptr, pSetupData ); + if ( nRet && (nRet != GDI_ERROR) ) + return TRUE; + return FALSE; + + default: + break; + } + + return 0; +} + +void WinSalInfoPrinter::GetPageInfo( const ImplJobSetup*, + tools::Long& rOutWidth, tools::Long& rOutHeight, + Point& rPageOffset, + Size& rPaperSize ) +{ + HDC hDC = m_hDC; + + rOutWidth = GetDeviceCaps( hDC, HORZRES ); + rOutHeight = GetDeviceCaps( hDC, VERTRES ); + + rPageOffset.setX( GetDeviceCaps( hDC, PHYSICALOFFSETX ) ); + rPageOffset.setY( GetDeviceCaps( hDC, PHYSICALOFFSETY ) ); + rPaperSize.setWidth( GetDeviceCaps( hDC, PHYSICALWIDTH ) ); + rPaperSize.setHeight( GetDeviceCaps( hDC, PHYSICALHEIGHT ) ); +} + + +std::unique_ptr<SalPrinter> WinSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) +{ + WinSalPrinter* pPrinter = new WinSalPrinter; + pPrinter->mpInfoPrinter = static_cast<WinSalInfoPrinter*>(pInfoPrinter); + return std::unique_ptr<SalPrinter>(pPrinter); +} + +static BOOL CALLBACK SalPrintAbortProc( HDC hPrnDC, int /* nError */ ) +{ + SalData* pSalData = GetSalData(); + WinSalPrinter* pPrinter; + int i = 0; + bool bWhile = true; + + // Ensure we handle the mutex which will be released in WinSalInstance::DoYield + SolarMutexGuard aSolarMutexGuard; + do + { + // process messages + bWhile = Application::Reschedule( true ); + if (i > 15) + bWhile = false; + else + ++i; + + pPrinter = pSalData->mpFirstPrinter; + while ( pPrinter ) + { + if( pPrinter->mhDC == hPrnDC ) + break; + + pPrinter = pPrinter->mpNextPrinter; + } + + if ( !pPrinter || pPrinter->mbAbort ) + return FALSE; + } + while ( bWhile ); + + return TRUE; +} + +static DEVMODEW const * ImplSalSetCopies( DEVMODEW const * pDevMode, sal_uInt32 nCopies, bool bCollate ) +{ + if ( pDevMode && (nCopies > 1) ) + { + if ( nCopies > 32765 ) + nCopies = 32765; + sal_uLong nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra; + LPDEVMODEW pNewDevMode = static_cast<LPDEVMODEW>(std::malloc( nDevSize )); + assert(pNewDevMode); // Don't handle OOM conditions + memcpy( pNewDevMode, pDevMode, nDevSize ); + pNewDevMode->dmFields |= DM_COPIES; + pNewDevMode->dmCopies = static_cast<short>(static_cast<sal_uInt16>(nCopies)); + pNewDevMode->dmFields |= DM_COLLATE; + if ( bCollate ) + pNewDevMode->dmCollate = DMCOLLATE_TRUE; + else + pNewDevMode->dmCollate = DMCOLLATE_FALSE; + return pNewDevMode; + } + else + { + return pDevMode; + } +} + + +WinSalPrinter::WinSalPrinter() : + mpInfoPrinter( nullptr ), + mpNextPrinter( nullptr ), + mhDC( nullptr ), + mnError( SalPrinterError::NONE ), + 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 ) + { + // explicitly reset(), so the mxGraphics's borrowed HDC defaults are + // restored and WinSalGraphics's destructor won't work on a deleted HDC. + mxGraphics.reset(); + 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; + } +} + +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_StartDocW1( HDC hDC, DOCINFOW const * 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_StartDocW( HDC hDC, DOCINFOW const * pInfo, WinSalPrinter* pPrt ) +{ + //tdf#127547 - Freeze/crash in Microsoft Print to PDF dialog, if we try to paste while + // executing the StartDocW method, Windows will call back into us on a separate thread, + // where we will attempt to take the SolarMutex. + SolarMutexReleaser aReleaser; + + return lcl_StartDocW1(hDC, pInfo, pPrt); +} + +bool WinSalPrinter::StartJob( const OUString* pFileName, + const OUString& rJobName, + const OUString&, + sal_uInt32 nCopies, + bool bCollate, + bool /*bDirect*/, + ImplJobSetup* pSetupData ) +{ + mnError = SalPrinterError::NONE; + mbAbort = false; + mnCopies = nCopies; + mbCollate = bCollate; + + DEVMODEW const * pOrgDevModeW = nullptr; + DEVMODEW const * pDevModeW = nullptr; + HDC hDC = nullptr; + if ( pSetupData && pSetupData->GetDriverData() ) + { + pOrgDevModeW = SAL_DEVMODE_W( pSetupData ); + pDevModeW = ImplSalSetCopies( pOrgDevModeW, nCopies, bCollate ); + } + + // #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]; + memcpy( aDrvBuf, mpInfoPrinter->maDriverName.getStr(), (mpInfoPrinter->maDriverName.getLength()+1)*sizeof(sal_Unicode)); + memcpy( aDevBuf, mpInfoPrinter->maDeviceName.getStr(), (mpInfoPrinter->maDeviceName.getLength()+1)*sizeof(sal_Unicode)); + hDC = CreateDCW( o3tl::toW(aDrvBuf), + o3tl::toW(aDevBuf), + nullptr, + pDevModeW ); + + if ( pDevModeW != pOrgDevModeW ) + std::free( const_cast<DEVMODEW *>(pDevModeW) ); + + if ( !hDC ) + { + mnError = SalPrinterError::General; + return false; + } + + // make sure mhDC is set before the printer driver may call our abortproc + mhDC = hDC; + if ( SetAbortProc( hDC, SalPrintAbortProc ) <= 0 ) + { + mnError = SalPrinterError::General; + return false; + } + + mnError = SalPrinterError::NONE; + mbAbort = false; + + // As the Telecom Balloon Fax driver tends to send messages repeatedly + // we try to process first all, and then insert a dummy message + for (int i = 0; Application::Reschedule( true ) && i <= 15; ++i); + bool const ret = PostMessageW(GetSalData()->mpInstance->mhComWnd, SAL_MSG_DUMMY, 0, 0); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + + // bring up a file chooser if printing to file port but no file name given + OUString aOutFileName; + if( mpInfoPrinter->maPortName.equalsIgnoreAsciiCase( "FILE:" ) && (!pFileName || pFileName->isEmpty()) ) + { + + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference< XFilePicker3 > xFilePicker = FilePicker::createWithMode(xContext, TemplateDescription::FILESAVE_SIMPLE); + + if( xFilePicker->execute() == ExecutableDialogResults::OK ) + { + Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() ); + INetURLObject aObj( aPathSeq[0] ); + aOutFileName = aObj.PathToFileName(); + } + else + { + mnError = SalPrinterError::Abort; + return false; + } + } + + DOCINFOW aInfo = {}; + aInfo.cbSize = sizeof( aInfo ); + aInfo.lpszDocName = o3tl::toW(rJobName.getStr()); + if ( pFileName || aOutFileName.getLength() ) + { + if ( (pFileName && !pFileName->isEmpty()) || aOutFileName.getLength() ) + { + aInfo.lpszOutput = o3tl::toW((pFileName && !pFileName->isEmpty()) ? pFileName->getStr() : aOutFileName.getStr()); + } + else + aInfo.lpszOutput = L"FILE:"; + } + else + aInfo.lpszOutput = nullptr; + + // start Job, in the main thread + int nRet = vcl::solarthread::syncExecute([hDC, this, &aInfo]() -> int { return lcl_StartDocW(hDC, &aInfo, this); }); + + if ( nRet <= 0 ) + { + DWORD nError = GetLastError(); + if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) ) + mnError = SalPrinterError::Abort; + else + mnError = SalPrinterError::General; + return false; + } + + return true; +} + +void WinSalPrinter::DoEndDoc(HDC hDC) +{ + CATCH_DRIVER_EX_BEGIN; + if( ::EndDoc( hDC ) <= 0 ) + GetLastError(); + CATCH_DRIVER_EX_END( "exception in EndDoc", this ); +} + +bool WinSalPrinter::EndJob() +{ + HDC hDC = mhDC; + if (isValid()) + { + mxGraphics.reset(); + + // #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. + { + SolarMutexReleaser aReleaser; + DoEndDoc( hDC ); + } + DeleteDC( hDC ); + mhDC = nullptr; + } + + return true; +} + +SalGraphics* WinSalPrinter::StartPage( ImplJobSetup* pSetupData, bool bNewJobData ) +{ + if (!isValid()) + return nullptr; + + HDC hDC = mhDC; + if ( pSetupData && pSetupData->GetDriverData() && bNewJobData ) + { + DEVMODEW const * pOrgDevModeW; + DEVMODEW const * pDevModeW; + pOrgDevModeW = SAL_DEVMODE_W( pSetupData ); + pDevModeW = ImplSalSetCopies( pOrgDevModeW, mnCopies, mbCollate ); + ResetDCW( hDC, pDevModeW ); + if ( pDevModeW != pOrgDevModeW ) + std::free( const_cast<DEVMODEW *>(pDevModeW) ); + } + volatile int nRet = 0; + CATCH_DRIVER_EX_BEGIN; + nRet = ::StartPage( hDC ); + CATCH_DRIVER_EX_END( "exception in StartPage", this ); + + if ( nRet <= 0 ) + { + GetLastError(); + mnError = SalPrinterError::General; + return nullptr; + } + + // 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 ) ); + Rectangle( hDC, -8000, -8000, -7999, -7999 ); + SelectPen( hDC, hTempPen ); + SelectBrush( hDC, hTempBrush ); + + mxGraphics.reset(ImplCreateSalPrnGraphics( hDC )); + return mxGraphics.get(); +} + +void WinSalPrinter::EndPage() +{ + mxGraphics.reset(); + + if (!isValid()) + return; + + HDC hDC = mhDC; + volatile int nRet = 0; + CATCH_DRIVER_EX_BEGIN; + nRet = ::EndPage( hDC ); + CATCH_DRIVER_EX_END( "exception in EndPage", this ); + + if ( nRet <= 0 ) + { + GetLastError(); + mnError = SalPrinterError::General; + } +} + +SalPrinterError WinSalPrinter::GetErrorCode() +{ + return mnError; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |