summaryrefslogtreecommitdiffstats
path: root/vcl/win/gdi/salprn.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/win/gdi/salprn.cxx1601
1 files changed, 1601 insertions, 0 deletions
diff --git a/vcl/win/gdi/salprn.cxx b/vcl/win/gdi/salprn.cxx
new file mode 100644
index 000000000..98d39dc02
--- /dev/null
+++ b/vcl/win/gdi/salprn.cxx
@@ -0,0 +1,1601 @@
+/* -*- 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->mpPortName )
+ pInfo->mpPortName.reset(new OUString(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 )
+ {
+ std::free( const_cast<sal_uInt8*>(pSetupData->GetDriverData()) );
+ pSetupData->SetDriverData( nullptr );
+ pSetupData->SetDriverDataLen( 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;
+ 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;
+ pOutBuffer = static_cast<SalDriverData*>(rtl_allocateZeroMemory( nDriverDataLen ));
+ 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)) )
+ {
+ std::free( pOutBuffer );
+ 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
+ if ( pSetupData->GetDriverData() )
+ std::free( const_cast<sal_uInt8*>(pSetupData->GetDriverData()) );
+ pSetupData->SetDriverDataLen( nDriverDataLen );
+ pSetupData->SetDriverData(reinterpret_cast<BYTE*>(pOutBuffer));
+ 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->mpPortName )
+ GetPrinterQueueState( pQueueInfo );
+ pPrinter->maDriverName = pQueueInfo->maDriver;
+ pPrinter->maDeviceName = pQueueInfo->maPrinterName;
+ pPrinter->maPortName = pQueueInfo->mpPortName ? *pQueueInfo->mpPortName : 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());
+ // we get intermittent crashes on the Windows jenkins box around here, let us see if there is something weird about the DC
+ SAL_WARN_IF(!hNewDC, "vcl", "Graphics DC " << m_hDC);
+ 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_StartDocW( 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;
+}
+
+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: */