xref: /core/vcl/source/gdi/print.cxx (revision 750a6bdd)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/types.h>
21 #include <sal/log.hxx>
22 
23 #include <tools/helpers.hxx>
24 #include <tools/debug.hxx>
25 
26 #include <vcl/QueueInfo.hxx>
27 #include <vcl/event.hxx>
28 #include <vcl/virdev.hxx>
29 #include <vcl/print.hxx>
30 
31 #include <comphelper/processfactory.hxx>
32 
33 #include <salinst.hxx>
34 #include <salvd.hxx>
35 #include <salgdi.hxx>
36 #include <salptype.hxx>
37 #include <salprn.hxx>
38 #include <svdata.hxx>
39 #include <print.hrc>
40 #include <jobset.h>
41 #include <outdev.h>
42 #include <PhysicalFontCollection.hxx>
43 #include <print.h>
44 
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/configuration/theDefaultProvider.hpp>
47 #include <com/sun/star/container/XNameAccess.hpp>
48 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
49 #include <com/sun/star/uno/Sequence.h>
50 
51 int nImplSysDialog = 0;
52 
53 namespace
54 {
55     Paper ImplGetPaperFormat( long nWidth100thMM, long nHeight100thMM )
56     {
57         PaperInfo aInfo(nWidth100thMM, nHeight100thMM);
58         aInfo.doSloppyFit();
59         return aInfo.getPaper();
60     }
61 
62     const PaperInfo& ImplGetEmptyPaper()
63     {
64         static PaperInfo aInfo(PAPER_USER);
65         return aInfo;
66     }
67 }
68 
69 void ImplUpdateJobSetupPaper( JobSetup& rJobSetup )
70 {
71     const ImplJobSetup& rConstData = rJobSetup.ImplGetConstData();
72 
73     if ( !rConstData.GetPaperWidth() || !rConstData.GetPaperHeight() )
74     {
75         if ( rConstData.GetPaperFormat() != PAPER_USER )
76         {
77             PaperInfo aInfo(rConstData.GetPaperFormat());
78 
79             ImplJobSetup& rData = rJobSetup.ImplGetData();
80             rData.SetPaperWidth( aInfo.getWidth() );
81             rData.SetPaperHeight( aInfo.getHeight() );
82         }
83     }
84     else if ( rConstData.GetPaperFormat() == PAPER_USER )
85     {
86         Paper ePaper = ImplGetPaperFormat( rConstData.GetPaperWidth(), rConstData.GetPaperHeight() );
87         if ( ePaper != PAPER_USER )
88             rJobSetup.ImplGetData().SetPaperFormat(ePaper);
89     }
90 }
91 
92 // PrinterOptions
93 PrinterOptions::PrinterOptions() :
94     mbReduceTransparency( false ),
95     meReducedTransparencyMode( PrinterTransparencyMode::Auto ),
96     mbReduceGradients( false ),
97     meReducedGradientsMode( PrinterGradientMode::Stripes ),
98     mnReducedGradientStepCount( 64 ),
99     mbReduceBitmaps( false ),
100     meReducedBitmapMode( PrinterBitmapMode::Normal ),
101     mnReducedBitmapResolution( 200 ),
102     mbReducedBitmapsIncludeTransparency( true ),
103     mbConvertToGreyscales( false ),
104     mbPDFAsStandardPrintJobFormat( false )
105 {
106 }
107 
108 void PrinterOptions::ReadFromConfig( bool i_bFile )
109 {
110     bool bSuccess = false;
111     // save old state in case something goes wrong
112     PrinterOptions aOldValues( *this );
113 
114     // get the configuration service
115     css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider;
116     css::uno::Reference< css::container::XNameAccess > xConfigAccess;
117     try
118     {
119         // get service provider
120         css::uno::Reference< css::uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
121         // create configuration hierarchical access name
122         try
123         {
124             xConfigProvider = css::configuration::theDefaultProvider::get( xContext );
125 
126             css::uno::Sequence< css::uno::Any > aArgs(1);
127             css::beans::PropertyValue aVal;
128             aVal.Name = "nodepath";
129             if( i_bFile )
130                 aVal.Value <<= OUString( "/org.openoffice.Office.Common/Print/Option/File" );
131             else
132                 aVal.Value <<= OUString( "/org.openoffice.Office.Common/Print/Option/Printer" );
133             aArgs.getArray()[0] <<= aVal;
134             xConfigAccess.set(
135                     xConfigProvider->createInstanceWithArguments(
136                         "com.sun.star.configuration.ConfigurationAccess", aArgs ),
137                         css::uno::UNO_QUERY );
138             if( xConfigAccess.is() )
139             {
140                 css::uno::Reference< css::beans::XPropertySet > xSet( xConfigAccess, css::uno::UNO_QUERY );
141                 if( xSet.is() )
142                 {
143                     sal_Int32 nValue = 0;
144                     bool  bValue = false;
145                     if( xSet->getPropertyValue("ReduceTransparency") >>= bValue )
146                         SetReduceTransparency( bValue );
147                     if( xSet->getPropertyValue("ReducedTransparencyMode") >>= nValue )
148                         SetReducedTransparencyMode( static_cast<PrinterTransparencyMode>(nValue) );
149                     if( xSet->getPropertyValue("ReduceGradients") >>= bValue )
150                         SetReduceGradients( bValue );
151                     if( xSet->getPropertyValue("ReducedGradientMode") >>= nValue )
152                         SetReducedGradientMode( static_cast<PrinterGradientMode>(nValue) );
153                     if( xSet->getPropertyValue("ReducedGradientStepCount") >>= nValue )
154                         SetReducedGradientStepCount( static_cast<sal_uInt16>(nValue) );
155                     if( xSet->getPropertyValue("ReduceBitmaps") >>= bValue )
156                         SetReduceBitmaps( bValue );
157                     if( xSet->getPropertyValue("ReducedBitmapMode") >>= nValue )
158                         SetReducedBitmapMode( static_cast<PrinterBitmapMode>(nValue) );
159                     if( xSet->getPropertyValue("ReducedBitmapResolution") >>= nValue )
160                         SetReducedBitmapResolution( static_cast<sal_uInt16>(nValue) );
161                     if( xSet->getPropertyValue("ReducedBitmapIncludesTransparency") >>= bValue )
162                         SetReducedBitmapIncludesTransparency( bValue );
163                     if( xSet->getPropertyValue("ConvertToGreyscales") >>= bValue )
164                         SetConvertToGreyscales( bValue );
165                     if( xSet->getPropertyValue("PDFAsStandardPrintJobFormat") >>= bValue )
166                         SetPDFAsStandardPrintJobFormat( bValue );
167 
168                     bSuccess = true;
169                 }
170             }
171         }
172         catch( const css::uno::Exception& )
173         {
174         }
175     }
176     catch( const css::lang::WrappedTargetException& )
177     {
178     }
179 
180     if( ! bSuccess )
181         *this = aOldValues;
182 }
183 
184 bool Printer::DrawTransformBitmapExDirect(
185     const basegfx::B2DHomMatrix& /*aFullTransform*/,
186     const BitmapEx& /*rBitmapEx*/)
187 {
188     // printers can't draw bitmaps directly
189     return false;
190 }
191 
192 bool Printer::TransformAndReduceBitmapExToTargetRange(
193     const basegfx::B2DHomMatrix& /*aFullTransform*/,
194     basegfx::B2DRange& /*aVisibleRange*/,
195     double& /*fMaximumArea*/)
196 {
197     // deliberately do nothing - you can't reduce the
198     // target range for a printer at all
199     return true;
200 }
201 
202 void Printer::DrawDeviceBitmap( const Point& rDestPt, const Size& rDestSize,
203                                 const Point& rSrcPtPixel, const Size& rSrcSizePixel,
204                                 BitmapEx& rBmpEx )
205 {
206     if( rBmpEx.IsAlpha() )
207     {
208         // #107169# For true alpha bitmaps, no longer masking the
209         // bitmap, but perform a full alpha blend against a white
210         // background here.
211         Bitmap aBmp( rBmpEx.GetBitmap() );
212         aBmp.Blend( rBmpEx.GetAlpha(), COL_WHITE );
213         DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp );
214     }
215     else
216     {
217         Bitmap aBmp( rBmpEx.GetBitmap() ), aMask( rBmpEx.GetMask() );
218         aBmp.Replace( aMask, COL_WHITE );
219         ImplPrintTransparent( aBmp, aMask, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
220     }
221 }
222 
223 void Printer::EmulateDrawTransparent ( const tools::PolyPolygon& rPolyPoly,
224                                        sal_uInt16 nTransparencePercent )
225 {
226     // #110958# Disable alpha VDev, we perform the necessary
227     VirtualDevice* pOldAlphaVDev = mpAlphaVDev;
228 
229     // operation explicitly further below.
230     if( mpAlphaVDev )
231         mpAlphaVDev = nullptr;
232 
233     GDIMetaFile* pOldMetaFile = mpMetaFile;
234     mpMetaFile = nullptr;
235 
236     mpMetaFile = pOldMetaFile;
237 
238     // #110958# Restore disabled alpha VDev
239     mpAlphaVDev = pOldAlphaVDev;
240 
241     tools::Rectangle       aPolyRect( LogicToPixel( rPolyPoly ).GetBoundRect() );
242     const Size      aDPISize( LogicToPixel(Size(1, 1), MapMode(MapUnit::MapInch)) );
243     const long      nBaseExtent = std::max( FRound( aDPISize.Width() / 300. ), 1L );
244     long            nMove;
245     const sal_uInt16    nTrans = ( nTransparencePercent < 13 ) ? 0 :
246         ( nTransparencePercent < 38 ) ? 25 :
247         ( nTransparencePercent < 63 ) ? 50 :
248         ( nTransparencePercent < 88 ) ? 75 : 100;
249 
250     switch( nTrans )
251     {
252         case 25: nMove = nBaseExtent * 3; break;
253         case 50: nMove = nBaseExtent * 4; break;
254         case 75: nMove = nBaseExtent * 6; break;
255 
256             // #i112959#  very transparent (88 < nTransparencePercent <= 99)
257         case 100: nMove = nBaseExtent * 8; break;
258 
259             // #i112959# not transparent (nTransparencePercent < 13)
260         default:    nMove = 0; break;
261     }
262 
263     Push( PushFlags::CLIPREGION | PushFlags::LINECOLOR );
264     IntersectClipRegion(vcl::Region(rPolyPoly));
265     SetLineColor( GetFillColor() );
266     const bool bOldMap = mbMap;
267     EnableMapMode( false );
268 
269     if(nMove)
270     {
271         tools::Rectangle aRect( aPolyRect.TopLeft(), Size( aPolyRect.GetWidth(), nBaseExtent ) );
272         while( aRect.Top() <= aPolyRect.Bottom() )
273         {
274             DrawRect( aRect );
275             aRect.Move( 0, nMove );
276         }
277 
278         aRect = tools::Rectangle( aPolyRect.TopLeft(), Size( nBaseExtent, aPolyRect.GetHeight() ) );
279         while( aRect.Left() <= aPolyRect.Right() )
280         {
281             DrawRect( aRect );
282             aRect.Move( nMove, 0 );
283         }
284     }
285     else
286     {
287         // #i112959# if not transparent, draw full rectangle in clip region
288         DrawRect( aPolyRect );
289     }
290 
291     EnableMapMode( bOldMap );
292     Pop();
293 
294     mpMetaFile = pOldMetaFile;
295 
296     // #110958# Restore disabled alpha VDev
297     mpAlphaVDev = pOldAlphaVDev;
298 }
299 
300 void Printer::DrawOutDev( const Point& /*rDestPt*/, const Size& /*rDestSize*/,
301                                const Point& /*rSrcPt*/,  const Size& /*rSrcSize*/ )
302 {
303     SAL_WARN( "vcl.gdi", "Don't use OutputDevice::DrawOutDev(...) with printer devices!" );
304 }
305 
306 void Printer::DrawOutDev( const Point& /*rDestPt*/, const Size& /*rDestSize*/,
307                                const Point& /*rSrcPt*/,  const Size& /*rSrcSize*/,
308                                const OutputDevice& /*rOutDev*/ )
309 {
310     SAL_WARN( "vcl.gdi", "Don't use OutputDevice::DrawOutDev(...) with printer devices!" );
311 }
312 
313 void Printer::CopyArea( const Point& /*rDestPt*/,
314                         const Point& /*rSrcPt*/,  const Size& /*rSrcSize*/,
315                         bool /*bWindowInvalidate*/ )
316 {
317     SAL_WARN( "vcl.gdi", "Don't use OutputDevice::CopyArea(...) with printer devices!" );
318 }
319 
320 tools::Rectangle Printer::GetBackgroundComponentBounds() const
321 {
322     Point aPageOffset = Point( 0, 0 ) - this->GetPageOffsetPixel();
323     Size aSize  = this->GetPaperSizePixel();
324     return tools::Rectangle( aPageOffset, aSize );
325 }
326 
327 void Printer::SetPrinterOptions( const PrinterOptions& i_rOptions )
328 {
329     *mpPrinterOptions = i_rOptions;
330 }
331 
332 bool Printer::HasMirroredGraphics() const
333 {
334     // due to a "hotfix" for AOO bug i55719, this needs to return false
335     return false;
336 }
337 
338 SalPrinterQueueInfo::SalPrinterQueueInfo()
339 {
340     mnStatus    = PrintQueueFlags::NONE;
341     mnJobs      = QUEUE_JOBS_DONTKNOW;
342 }
343 
344 SalPrinterQueueInfo::~SalPrinterQueueInfo()
345 {
346 }
347 
348 ImplPrnQueueList::~ImplPrnQueueList()
349 {
350 }
351 
352 void ImplPrnQueueList::Add( std::unique_ptr<SalPrinterQueueInfo> pData )
353 {
354     std::unordered_map< OUString, sal_Int32 >::iterator it =
355         m_aNameToIndex.find( pData->maPrinterName );
356     if( it == m_aNameToIndex.end() )
357     {
358         m_aNameToIndex[ pData->maPrinterName ] = m_aQueueInfos.size();
359         m_aPrinterList.push_back( pData->maPrinterName );
360         m_aQueueInfos.push_back( ImplPrnQueueData() );
361         m_aQueueInfos.back().mpQueueInfo = nullptr;
362         m_aQueueInfos.back().mpSalQueueInfo = std::move(pData);
363     }
364     else // this should not happen, but ...
365     {
366         ImplPrnQueueData& rData = m_aQueueInfos[ it->second ];
367         rData.mpQueueInfo.reset();
368         rData.mpSalQueueInfo = std::move(pData);
369     }
370 }
371 
372 ImplPrnQueueData* ImplPrnQueueList::Get( const OUString& rPrinter )
373 {
374     ImplPrnQueueData* pData = nullptr;
375     std::unordered_map<OUString,sal_Int32>::iterator it =
376         m_aNameToIndex.find( rPrinter );
377     if( it != m_aNameToIndex.end() )
378         pData = &m_aQueueInfos[it->second];
379     return pData;
380 }
381 
382 static void ImplInitPrnQueueList()
383 {
384     ImplSVData* pSVData = ImplGetSVData();
385 
386     pSVData->maGDIData.mpPrinterQueueList.reset(new ImplPrnQueueList);
387 
388     static const char* pEnv = getenv( "SAL_DISABLE_PRINTERLIST" );
389     if( !pEnv || !*pEnv )
390         pSVData->mpDefInst->GetPrinterQueueInfo( pSVData->maGDIData.mpPrinterQueueList.get() );
391 }
392 
393 void ImplDeletePrnQueueList()
394 {
395     ImplSVData*         pSVData = ImplGetSVData();
396     pSVData->maGDIData.mpPrinterQueueList.reset();
397 }
398 
399 const std::vector<OUString>& Printer::GetPrinterQueues()
400 {
401     ImplSVData* pSVData = ImplGetSVData();
402     if ( !pSVData->maGDIData.mpPrinterQueueList )
403         ImplInitPrnQueueList();
404     return pSVData->maGDIData.mpPrinterQueueList->m_aPrinterList;
405 }
406 
407 const QueueInfo* Printer::GetQueueInfo( const OUString& rPrinterName, bool bStatusUpdate )
408 {
409     ImplSVData* pSVData = ImplGetSVData();
410 
411     if ( !pSVData->maGDIData.mpPrinterQueueList )
412         ImplInitPrnQueueList();
413 
414     if ( !pSVData->maGDIData.mpPrinterQueueList )
415         return nullptr;
416 
417     ImplPrnQueueData* pInfo = pSVData->maGDIData.mpPrinterQueueList->Get( rPrinterName );
418     if( pInfo )
419     {
420         if( !pInfo->mpQueueInfo || bStatusUpdate )
421             pSVData->mpDefInst->GetPrinterQueueState( pInfo->mpSalQueueInfo.get() );
422 
423         if ( !pInfo->mpQueueInfo )
424             pInfo->mpQueueInfo.reset(new QueueInfo);
425 
426         pInfo->mpQueueInfo->maPrinterName   = pInfo->mpSalQueueInfo->maPrinterName;
427         pInfo->mpQueueInfo->maDriver        = pInfo->mpSalQueueInfo->maDriver;
428         pInfo->mpQueueInfo->maLocation      = pInfo->mpSalQueueInfo->maLocation;
429         pInfo->mpQueueInfo->maComment       = pInfo->mpSalQueueInfo->maComment;
430         pInfo->mpQueueInfo->mnStatus        = pInfo->mpSalQueueInfo->mnStatus;
431         pInfo->mpQueueInfo->mnJobs          = pInfo->mpSalQueueInfo->mnJobs;
432         return pInfo->mpQueueInfo.get();
433     }
434     return nullptr;
435 }
436 
437 OUString Printer::GetDefaultPrinterName()
438 {
439     static const char* pEnv = getenv( "SAL_DISABLE_DEFAULTPRINTER" );
440     if( !pEnv || !*pEnv )
441     {
442         ImplSVData* pSVData = ImplGetSVData();
443 
444         return pSVData->mpDefInst->GetDefaultPrinter();
445     }
446     return OUString();
447 }
448 
449 void Printer::ImplInitData()
450 {
451     mbDevOutput         = false;
452     mbDefPrinter        = false;
453     mnError             = ERRCODE_NONE;
454     mnPageQueueSize     = 0;
455     mnCopyCount         = 1;
456     mbCollateCopy       = false;
457     mbPrinting          = false;
458     mbJobActive         = false;
459     mbPrintFile         = false;
460     mbInPrintPage       = false;
461     mbNewJobSetup       = false;
462     mpInfoPrinter       = nullptr;
463     mpPrinter           = nullptr;
464     mpDisplayDev        = nullptr;
465     mpPrinterOptions.reset(new PrinterOptions);
466 
467     // Add printer to the list
468     ImplSVData* pSVData = ImplGetSVData();
469     mpNext = pSVData->maGDIData.mpFirstPrinter;
470     mpPrev = nullptr;
471     if ( mpNext )
472         mpNext->mpPrev = this;
473     pSVData->maGDIData.mpFirstPrinter = this;
474 }
475 
476 bool Printer::AcquireGraphics() const
477 {
478     DBG_TESTSOLARMUTEX();
479 
480     if ( mpGraphics )
481         return true;
482 
483     mbInitLineColor     = true;
484     mbInitFillColor     = true;
485     mbInitFont          = true;
486     mbInitTextColor     = true;
487     mbInitClipRegion    = true;
488 
489     ImplSVData* pSVData = ImplGetSVData();
490 
491     if ( mpJobGraphics )
492         mpGraphics = mpJobGraphics;
493     else if ( mpDisplayDev )
494     {
495         const VirtualDevice* pVirDev = mpDisplayDev;
496         mpGraphics = pVirDev->mpVirDev->AcquireGraphics();
497         // if needed retry after releasing least recently used virtual device graphics
498         while ( !mpGraphics )
499         {
500             if ( !pSVData->maGDIData.mpLastVirGraphics )
501                 break;
502             pSVData->maGDIData.mpLastVirGraphics->ReleaseGraphics();
503             mpGraphics = pVirDev->mpVirDev->AcquireGraphics();
504         }
505         // update global LRU list of virtual device graphics
506         if ( mpGraphics )
507         {
508             mpNextGraphics = pSVData->maGDIData.mpFirstVirGraphics;
509             pSVData->maGDIData.mpFirstVirGraphics = const_cast<Printer*>(this);
510             if ( mpNextGraphics )
511                 mpNextGraphics->mpPrevGraphics = const_cast<Printer*>(this);
512             if ( !pSVData->maGDIData.mpLastVirGraphics )
513                 pSVData->maGDIData.mpLastVirGraphics = const_cast<Printer*>(this);
514         }
515     }
516     else
517     {
518         mpGraphics = mpInfoPrinter->AcquireGraphics();
519         // if needed retry after releasing least recently used printer graphics
520         while ( !mpGraphics )
521         {
522             if ( !pSVData->maGDIData.mpLastPrnGraphics )
523                 break;
524             pSVData->maGDIData.mpLastPrnGraphics->ReleaseGraphics();
525             mpGraphics = mpInfoPrinter->AcquireGraphics();
526         }
527         // update global LRU list of printer graphics
528         if ( mpGraphics )
529         {
530             mpNextGraphics = pSVData->maGDIData.mpFirstPrnGraphics;
531             pSVData->maGDIData.mpFirstPrnGraphics = const_cast<Printer*>(this);
532             if ( mpNextGraphics )
533                 mpNextGraphics->mpPrevGraphics = const_cast<Printer*>(this);
534             if ( !pSVData->maGDIData.mpLastPrnGraphics )
535                 pSVData->maGDIData.mpLastPrnGraphics = const_cast<Printer*>(this);
536         }
537     }
538 
539     if ( mpGraphics )
540     {
541         mpGraphics->SetXORMode( (RasterOp::Invert == meRasterOp) || (RasterOp::Xor == meRasterOp), RasterOp::Invert == meRasterOp );
542         mpGraphics->setAntiAliasB2DDraw(bool(mnAntialiasing & AntialiasingFlags::EnableB2dDraw));
543     }
544 
545     return mpGraphics != nullptr;
546 }
547 
548 void Printer::ImplReleaseFonts()
549 {
550 #ifdef UNX
551     // HACK to fix an urgent P1 printing issue fast
552     // WinSalPrinter does not respect GetGraphics/ReleaseGraphics conventions
553     // so Printer::mpGraphics often points to a dead WinSalGraphics
554     // TODO: fix WinSalPrinter's GetGraphics/ReleaseGraphics handling
555     mpGraphics->ReleaseFonts();
556 #endif
557     mbNewFont = true;
558     mbInitFont = true;
559 
560     mpFontInstance.clear();
561     mpDeviceFontList.reset();
562     mpDeviceFontSizeList.reset();
563 }
564 
565 void Printer::ReleaseGraphics( bool bRelease )
566 {
567     DBG_TESTSOLARMUTEX();
568 
569     if ( !mpGraphics )
570         return;
571 
572     // release the fonts of the physically released graphics device
573     if( bRelease )
574         ImplReleaseFonts();
575 
576     ImplSVData* pSVData = ImplGetSVData();
577 
578     Printer* pPrinter = this;
579 
580     if ( !pPrinter->mpJobGraphics )
581     {
582         if ( pPrinter->mpDisplayDev )
583         {
584             VirtualDevice* pVirDev = pPrinter->mpDisplayDev;
585             if ( bRelease )
586                 pVirDev->mpVirDev->ReleaseGraphics( mpGraphics );
587             // remove from global LRU list of virtual device graphics
588             if ( mpPrevGraphics )
589                 mpPrevGraphics->mpNextGraphics = mpNextGraphics;
590             else
591                 pSVData->maGDIData.mpFirstVirGraphics = mpNextGraphics;
592             if ( mpNextGraphics )
593                 mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
594             else
595                 pSVData->maGDIData.mpLastVirGraphics = mpPrevGraphics;
596         }
597         else
598         {
599             if ( bRelease )
600                 pPrinter->mpInfoPrinter->ReleaseGraphics( mpGraphics );
601             // remove from global LRU list of printer graphics
602             if ( mpPrevGraphics )
603                 mpPrevGraphics->mpNextGraphics = mpNextGraphics;
604             else
605                 pSVData->maGDIData.mpFirstPrnGraphics = mpNextGraphics;
606             if ( mpNextGraphics )
607                 mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
608             else
609                 pSVData->maGDIData.mpLastPrnGraphics = mpPrevGraphics;
610         }
611     }
612 
613     mpGraphics      = nullptr;
614     mpPrevGraphics  = nullptr;
615     mpNextGraphics  = nullptr;
616 }
617 
618 void Printer::ImplInit( SalPrinterQueueInfo* pInfo )
619 {
620     ImplSVData* pSVData = ImplGetSVData();
621     // #i74084# update info for this specific SalPrinterQueueInfo
622     pSVData->mpDefInst->GetPrinterQueueState( pInfo );
623 
624     // Test whether the driver actually matches the JobSetup
625     ImplJobSetup& rData = maJobSetup.ImplGetData();
626     if ( rData.GetDriverData() )
627     {
628         if ( rData.GetPrinterName() != pInfo->maPrinterName ||
629              rData.GetDriver() != pInfo->maDriver )
630         {
631             std::free( const_cast<sal_uInt8*>(rData.GetDriverData()) );
632             rData.SetDriverData(nullptr);
633             rData.SetDriverDataLen(0);
634         }
635     }
636 
637     // Remember printer name
638     maPrinterName = pInfo->maPrinterName;
639     maDriver = pInfo->maDriver;
640 
641     // Add printer name to JobSetup
642     rData.SetPrinterName( maPrinterName );
643     rData.SetDriver( maDriver );
644 
645     mpInfoPrinter   = pSVData->mpDefInst->CreateInfoPrinter( pInfo, &rData );
646     mpPrinter       = nullptr;
647     mpJobGraphics   = nullptr;
648     ImplUpdateJobSetupPaper( maJobSetup );
649 
650     if ( !mpInfoPrinter )
651     {
652         ImplInitDisplay();
653         return;
654     }
655 
656     // we need a graphics
657     if ( !AcquireGraphics() )
658     {
659         ImplInitDisplay();
660         return;
661     }
662 
663     // Init data
664     ImplUpdatePageData();
665     mxFontCollection = std::make_shared<PhysicalFontCollection>();
666     mxFontCache = std::make_shared<ImplFontCache>();
667     mpGraphics->GetDevFontList(mxFontCollection.get());
668 }
669 
670 void Printer::ImplInitDisplay()
671 {
672     ImplSVData* pSVData = ImplGetSVData();
673 
674     mpInfoPrinter       = nullptr;
675     mpPrinter           = nullptr;
676     mpJobGraphics       = nullptr;
677 
678     mpDisplayDev = VclPtr<VirtualDevice>::Create();
679     mxFontCollection    = pSVData->maGDIData.mxScreenFontList;
680     mxFontCache         = pSVData->maGDIData.mxScreenFontCache;
681     mnDPIX              = mpDisplayDev->mnDPIX;
682     mnDPIY              = mpDisplayDev->mnDPIY;
683 }
684 
685 void Printer::DrawDeviceMask( const Bitmap& rMask, const Color& rMaskColor,
686                          const Point& rDestPt, const Size& rDestSize,
687                          const Point& rSrcPtPixel, const Size& rSrcSizePixel )
688 {
689     Point       aDestPt( LogicToPixel( rDestPt ) );
690     Size        aDestSz( LogicToPixel( rDestSize ) );
691     tools::Rectangle   aSrcRect( rSrcPtPixel, rSrcSizePixel );
692 
693     aSrcRect.Justify();
694 
695     if( !(!rMask.IsEmpty() && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height()) )
696         return;
697 
698     Bitmap  aMask( rMask );
699     BmpMirrorFlags nMirrFlags = BmpMirrorFlags::NONE;
700 
701     if( aMask.GetBitCount() > 1 )
702         aMask.Convert( BmpConversion::N1BitThreshold );
703 
704     // mirrored horizontically
705     if( aDestSz.Width() < 0 )
706     {
707         aDestSz.setWidth( -aDestSz.Width() );
708         aDestPt.AdjustX( -( aDestSz.Width() - 1 ) );
709         nMirrFlags |= BmpMirrorFlags::Horizontal;
710     }
711 
712     // mirrored vertically
713     if( aDestSz.Height() < 0 )
714     {
715         aDestSz.setHeight( -aDestSz.Height() );
716         aDestPt.AdjustY( -( aDestSz.Height() - 1 ) );
717         nMirrFlags |= BmpMirrorFlags::Vertical;
718     }
719 
720     // source cropped?
721     if( aSrcRect != tools::Rectangle( Point(), aMask.GetSizePixel() ) )
722         aMask.Crop( aSrcRect );
723 
724     // destination mirrored
725     if( nMirrFlags != BmpMirrorFlags::NONE)
726         aMask.Mirror( nMirrFlags );
727 
728     // do painting
729     const long      nSrcWidth = aSrcRect.GetWidth(), nSrcHeight = aSrcRect.GetHeight();
730     long            nX, nY; //, nWorkX, nWorkY, nWorkWidth, nWorkHeight;
731     std::unique_ptr<long[]> pMapX( new long[ nSrcWidth + 1 ] );
732     std::unique_ptr<long[]> pMapY( new long[ nSrcHeight + 1 ] );
733     GDIMetaFile*    pOldMetaFile = mpMetaFile;
734     const bool      bOldMap = mbMap;
735 
736     mpMetaFile = nullptr;
737     mbMap = false;
738     Push( PushFlags::FILLCOLOR | PushFlags::LINECOLOR );
739     SetLineColor( rMaskColor );
740     SetFillColor( rMaskColor );
741     InitLineColor();
742     InitFillColor();
743 
744     // create forward mapping tables
745     for( nX = 0; nX <= nSrcWidth; nX++ )
746         pMapX[ nX ] = aDestPt.X() + FRound( static_cast<double>(aDestSz.Width()) * nX / nSrcWidth );
747 
748     for( nY = 0; nY <= nSrcHeight; nY++ )
749         pMapY[ nY ] = aDestPt.Y() + FRound( static_cast<double>(aDestSz.Height()) * nY / nSrcHeight );
750 
751     // walk through all rectangles of mask
752     const vcl::Region aWorkRgn(aMask.CreateRegion(COL_BLACK, tools::Rectangle(Point(), aMask.GetSizePixel())));
753     RectangleVector aRectangles;
754     aWorkRgn.GetRegionRectangles(aRectangles);
755 
756     for (auto const& rectangle : aRectangles)
757     {
758         const Point aMapPt(pMapX[rectangle.Left()], pMapY[rectangle.Top()]);
759         const Size aMapSz(
760             pMapX[rectangle.Right() + 1] - aMapPt.X(),      // pMapX[L + W] -> L + ((R - L) + 1) -> R + 1
761             pMapY[rectangle.Bottom() + 1] - aMapPt.Y());    // same for Y
762 
763         DrawRect(tools::Rectangle(aMapPt, aMapSz));
764     }
765 
766     Pop();
767     mbMap = bOldMap;
768     mpMetaFile = pOldMetaFile;
769 }
770 
771 SalPrinterQueueInfo* Printer::ImplGetQueueInfo( const OUString& rPrinterName,
772                                                 const OUString* pDriver )
773 {
774     ImplSVData* pSVData = ImplGetSVData();
775     if ( !pSVData->maGDIData.mpPrinterQueueList )
776         ImplInitPrnQueueList();
777 
778     ImplPrnQueueList* pPrnList = pSVData->maGDIData.mpPrinterQueueList.get();
779     if ( pPrnList && !pPrnList->m_aQueueInfos.empty() )
780     {
781         // first search for the printer name directly
782         ImplPrnQueueData* pInfo = pPrnList->Get( rPrinterName );
783         if( pInfo )
784             return pInfo->mpSalQueueInfo.get();
785 
786         // then search case insensitive
787         for(const ImplPrnQueueData & rQueueInfo : pPrnList->m_aQueueInfos)
788         {
789             if( rQueueInfo.mpSalQueueInfo->maPrinterName.equalsIgnoreAsciiCase( rPrinterName ) )
790                 return rQueueInfo.mpSalQueueInfo.get();
791         }
792 
793         // then search for driver name
794         if ( pDriver )
795         {
796             for(const ImplPrnQueueData & rQueueInfo : pPrnList->m_aQueueInfos)
797             {
798                 if( rQueueInfo.mpSalQueueInfo->maDriver == *pDriver )
799                     return rQueueInfo.mpSalQueueInfo.get();
800             }
801         }
802 
803         // then the default printer
804         pInfo = pPrnList->Get( GetDefaultPrinterName() );
805         if( pInfo )
806             return pInfo->mpSalQueueInfo.get();
807 
808         // last chance: the first available printer
809         return pPrnList->m_aQueueInfos[0].mpSalQueueInfo.get();
810     }
811 
812     return nullptr;
813 }
814 
815 void Printer::ImplUpdatePageData()
816 {
817     // we need a graphics
818     if ( !AcquireGraphics() )
819         return;
820 
821     mpGraphics->GetResolution( mnDPIX, mnDPIY );
822     mpInfoPrinter->GetPageInfo( &maJobSetup.ImplGetConstData(),
823                                 mnOutWidth, mnOutHeight,
824                                 maPageOffset,
825                                 maPaperSize );
826 }
827 
828 void Printer::ImplUpdateFontList()
829 {
830     ImplUpdateFontData();
831 }
832 
833 long Printer::GetGradientStepCount( long nMinRect )
834 {
835     // use display-equivalent step size calculation
836     long nInc = (nMinRect < 800) ? 10 : 20;
837 
838     return nInc;
839 }
840 
841 Printer::Printer()
842     : OutputDevice(OUTDEV_PRINTER)
843 {
844     ImplInitData();
845     SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( GetDefaultPrinterName(), nullptr );
846     if ( pInfo )
847     {
848         ImplInit( pInfo );
849         if ( !IsDisplayPrinter() )
850             mbDefPrinter = true;
851     }
852     else
853         ImplInitDisplay();
854 }
855 
856 Printer::Printer( const JobSetup& rJobSetup )
857     : OutputDevice(OUTDEV_PRINTER)
858     , maJobSetup(rJobSetup)
859 {
860     ImplInitData();
861     const ImplJobSetup& rConstData = rJobSetup.ImplGetConstData();
862     OUString aDriver = rConstData.GetDriver();
863     SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rConstData.GetPrinterName(),
864                                                    &aDriver );
865     if ( pInfo )
866     {
867         ImplInit( pInfo );
868         SetJobSetup( rJobSetup );
869     }
870     else
871     {
872         ImplInitDisplay();
873         maJobSetup = JobSetup();
874     }
875 }
876 
877 Printer::Printer( const QueueInfo& rQueueInfo )
878     : OutputDevice(OUTDEV_PRINTER)
879 {
880     ImplInitData();
881     SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rQueueInfo.GetPrinterName(),
882                                                    &rQueueInfo.GetDriver() );
883     if ( pInfo )
884         ImplInit( pInfo );
885     else
886         ImplInitDisplay();
887 }
888 
889 Printer::Printer( const OUString& rPrinterName )
890     : OutputDevice(OUTDEV_PRINTER)
891 {
892     ImplInitData();
893     SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( rPrinterName, nullptr );
894     if ( pInfo )
895         ImplInit( pInfo );
896     else
897         ImplInitDisplay();
898 }
899 
900 Printer::~Printer()
901 {
902     disposeOnce();
903 }
904 
905 void Printer::dispose()
906 {
907     SAL_WARN_IF( IsPrinting(), "vcl.gdi", "Printer::~Printer() - Job is printing" );
908     SAL_WARN_IF( IsJobActive(), "vcl.gdi", "Printer::~Printer() - Job is active" );
909 
910     mpPrinterOptions.reset();
911 
912     ReleaseGraphics();
913     if ( mpInfoPrinter )
914         ImplGetSVData()->mpDefInst->DestroyInfoPrinter( mpInfoPrinter );
915     if ( mpDisplayDev )
916         mpDisplayDev.disposeAndClear();
917     else
918     {
919         // OutputDevice Dtor is trying the same thing; that why we need to set
920         // the FontEntry to NULL here
921         // TODO: consolidate duplicate cleanup by Printer and OutputDevice
922         mpFontInstance.clear();
923         mpDeviceFontList.reset();
924         mpDeviceFontSizeList.reset();
925         mxFontCache.reset();
926         // font list deleted by OutputDevice dtor
927     }
928 
929     // Add printer from the list
930     ImplSVData* pSVData = ImplGetSVData();
931     if ( mpPrev )
932         mpPrev->mpNext = mpNext;
933     else
934         pSVData->maGDIData.mpFirstPrinter = mpNext;
935     if ( mpNext )
936         mpNext->mpPrev = mpPrev;
937 
938     mpPrev.clear();
939     mpNext.clear();
940     OutputDevice::dispose();
941 }
942 
943 Size Printer::GetButtonBorderSize()
944 {
945     Size aBrdSize(LogicToPixel(Size(20, 20), MapMode(MapUnit::Map100thMM)));
946 
947     if (!aBrdSize.Width())
948         aBrdSize.setWidth(1);
949 
950     if (!aBrdSize.Height())
951         aBrdSize.setHeight(1);
952 
953     return aBrdSize;
954 }
955 
956 sal_uInt32 Printer::GetCapabilities( PrinterCapType nType ) const
957 {
958     if ( IsDisplayPrinter() )
959         return 0;
960 
961     if( mpInfoPrinter )
962         return mpInfoPrinter->GetCapabilities( &maJobSetup.ImplGetConstData(), nType );
963     else
964         return 0;
965 }
966 
967 bool Printer::HasSupport( PrinterSupport eFeature ) const
968 {
969     switch ( eFeature )
970     {
971         case PrinterSupport::SetOrientation:
972             return GetCapabilities( PrinterCapType::SetOrientation ) != 0;
973         case PrinterSupport::SetPaperSize:
974             return GetCapabilities( PrinterCapType::SetPaperSize ) != 0;
975         case PrinterSupport::SetPaper:
976             return GetCapabilities( PrinterCapType::SetPaper ) != 0;
977         case PrinterSupport::CollateCopy:
978             return (GetCapabilities( PrinterCapType::CollateCopies ) != 0);
979         case PrinterSupport::SetupDialog:
980             return GetCapabilities( PrinterCapType::SupportDialog ) != 0;
981     }
982 
983     return true;
984 }
985 
986 bool Printer::SetJobSetup( const JobSetup& rSetup )
987 {
988     if ( IsDisplayPrinter() || mbInPrintPage )
989         return false;
990 
991     JobSetup aJobSetup = rSetup;
992 
993     ReleaseGraphics();
994     if ( mpInfoPrinter->SetPrinterData( &aJobSetup.ImplGetData() ) )
995     {
996         ImplUpdateJobSetupPaper( aJobSetup );
997         mbNewJobSetup = true;
998         maJobSetup = aJobSetup;
999         ImplUpdatePageData();
1000         ImplUpdateFontList();
1001         return true;
1002     }
1003 
1004     return false;
1005 }
1006 
1007 bool Printer::Setup(weld::Window* pWindow, PrinterSetupMode eMode)
1008 {
1009     if ( IsDisplayPrinter() )
1010         return false;
1011 
1012     if ( IsJobActive() || IsPrinting() )
1013         return false;
1014 
1015     JobSetup aJobSetup = maJobSetup;
1016     ImplJobSetup& rData = aJobSetup.ImplGetData();
1017     rData.SetPrinterSetupMode( eMode );
1018     // TODO: orig page size
1019 
1020     if (!pWindow)
1021     {
1022         vcl::Window* pDefWin = ImplGetDefaultWindow();
1023         pWindow = pDefWin ? pDefWin->GetFrameWeld() : nullptr;
1024     }
1025     if( !pWindow )
1026         return false;
1027 
1028     ReleaseGraphics();
1029     ImplSVData* pSVData = ImplGetSVData();
1030     pSVData->maAppData.mnModalMode++;
1031     nImplSysDialog++;
1032     bool bSetup = mpInfoPrinter->Setup(pWindow, &rData);
1033     pSVData->maAppData.mnModalMode--;
1034     nImplSysDialog--;
1035     if ( bSetup )
1036     {
1037         ImplUpdateJobSetupPaper( aJobSetup );
1038         mbNewJobSetup = true;
1039         maJobSetup = aJobSetup;
1040         ImplUpdatePageData();
1041         ImplUpdateFontList();
1042         return true;
1043     }
1044     return false;
1045 }
1046 
1047 bool Printer::SetPrinterProps( const Printer* pPrinter )
1048 {
1049     if ( IsJobActive() || IsPrinting() )
1050         return false;
1051 
1052     ImplSVData* pSVData = ImplGetSVData();
1053 
1054     mbDefPrinter        = pPrinter->mbDefPrinter;
1055     maPrintFile         = pPrinter->maPrintFile;
1056     mbPrintFile         = pPrinter->mbPrintFile;
1057     mnCopyCount         = pPrinter->mnCopyCount;
1058     mbCollateCopy       = pPrinter->mbCollateCopy;
1059     mnPageQueueSize     = pPrinter->mnPageQueueSize;
1060     *mpPrinterOptions   = *pPrinter->mpPrinterOptions;
1061 
1062     if ( pPrinter->IsDisplayPrinter() )
1063     {
1064         // Destroy old printer
1065         if ( !IsDisplayPrinter() )
1066         {
1067             ReleaseGraphics();
1068             pSVData->mpDefInst->DestroyInfoPrinter( mpInfoPrinter );
1069             mpFontInstance.clear();
1070             mpDeviceFontList.reset();
1071             mpDeviceFontSizeList.reset();
1072             // clean up font list
1073             mxFontCache.reset();
1074             mxFontCollection.reset();
1075 
1076             mbInitFont = true;
1077             mbNewFont = true;
1078             mpInfoPrinter = nullptr;
1079         }
1080 
1081         // Construct new printer
1082         ImplInitDisplay();
1083         return true;
1084     }
1085 
1086     // Destroy old printer?
1087     if ( GetName() != pPrinter->GetName() )
1088     {
1089         ReleaseGraphics();
1090         if ( mpDisplayDev )
1091         {
1092             mpDisplayDev.disposeAndClear();
1093         }
1094         else
1095         {
1096             pSVData->mpDefInst->DestroyInfoPrinter( mpInfoPrinter );
1097 
1098             mpFontInstance.clear();
1099             mpDeviceFontList.reset();
1100             mpDeviceFontSizeList.reset();
1101             mxFontCache.reset();
1102             mxFontCollection.reset();
1103             mbInitFont = true;
1104             mbNewFont = true;
1105             mpInfoPrinter = nullptr;
1106         }
1107 
1108         // Construct new printer
1109         OUString aDriver = pPrinter->GetDriverName();
1110         SalPrinterQueueInfo* pInfo = ImplGetQueueInfo( pPrinter->GetName(), &aDriver );
1111         if ( pInfo )
1112         {
1113             ImplInit( pInfo );
1114             SetJobSetup( pPrinter->GetJobSetup() );
1115         }
1116         else
1117             ImplInitDisplay();
1118     }
1119     else
1120         SetJobSetup( pPrinter->GetJobSetup() );
1121 
1122     return false;
1123 }
1124 
1125 bool Printer::SetOrientation( Orientation eOrientation )
1126 {
1127     if ( mbInPrintPage )
1128         return false;
1129 
1130     if ( maJobSetup.ImplGetConstData().GetOrientation() != eOrientation )
1131     {
1132         JobSetup      aJobSetup = maJobSetup;
1133         ImplJobSetup& rData = aJobSetup.ImplGetData();
1134 
1135         rData.SetOrientation(eOrientation);
1136 
1137         if ( IsDisplayPrinter() )
1138         {
1139             mbNewJobSetup = true;
1140             maJobSetup = aJobSetup;
1141             return true;
1142         }
1143 
1144         ReleaseGraphics();
1145         if ( mpInfoPrinter->SetData( JobSetFlags::ORIENTATION, &rData ) )
1146         {
1147             ImplUpdateJobSetupPaper( aJobSetup );
1148             mbNewJobSetup = true;
1149             maJobSetup = aJobSetup;
1150             ImplUpdatePageData();
1151             ImplUpdateFontList();
1152             return true;
1153         }
1154         else
1155             return false;
1156     }
1157 
1158     return true;
1159 }
1160 
1161 Orientation Printer::GetOrientation() const
1162 {
1163     return maJobSetup.ImplGetConstData().GetOrientation();
1164 }
1165 
1166 bool Printer::SetPaperBin( sal_uInt16 nPaperBin )
1167 {
1168     if ( mbInPrintPage )
1169         return false;
1170 
1171     if ( maJobSetup.ImplGetConstData().GetPaperBin() != nPaperBin &&
1172          nPaperBin < GetPaperBinCount() )
1173     {
1174         JobSetup      aJobSetup = maJobSetup;
1175         ImplJobSetup& rData = aJobSetup.ImplGetData();
1176         rData.SetPaperBin(nPaperBin);
1177 
1178         if ( IsDisplayPrinter() )
1179         {
1180             mbNewJobSetup = true;
1181             maJobSetup = aJobSetup;
1182             return true;
1183         }
1184 
1185         ReleaseGraphics();
1186         if ( mpInfoPrinter->SetData( JobSetFlags::PAPERBIN, &rData ) )
1187         {
1188             ImplUpdateJobSetupPaper( aJobSetup );
1189             mbNewJobSetup = true;
1190             maJobSetup = aJobSetup;
1191             ImplUpdatePageData();
1192             ImplUpdateFontList();
1193             return true;
1194         }
1195         else
1196             return false;
1197     }
1198 
1199     return true;
1200 }
1201 
1202 sal_uInt16 Printer::GetPaperBin() const
1203 {
1204     return maJobSetup.ImplGetConstData().GetPaperBin();
1205 }
1206 
1207 bool Printer::GetPrinterSettingsPreferred() const
1208 {
1209     return maJobSetup.ImplGetConstData().GetPapersizeFromSetup();
1210 }
1211 
1212 // dear loplugins, DO NOT REMOVE this code
1213 // it will be used in follow-up commits
1214 void Printer::SetPrinterSettingsPreferred( bool bPaperSizeFromSetup)
1215 {
1216     if ( maJobSetup.ImplGetConstData().GetPapersizeFromSetup() != bPaperSizeFromSetup )
1217     {
1218         JobSetup      aJobSetup = maJobSetup;
1219         ImplJobSetup& rData = aJobSetup.ImplGetData();
1220         rData.SetPapersizeFromSetup(bPaperSizeFromSetup);
1221 
1222         mbNewJobSetup = true;
1223         maJobSetup = aJobSetup;
1224     }
1225 }
1226 
1227 // Map user paper format to an available printer paper format
1228 void Printer::ImplFindPaperFormatForUserSize( JobSetup& aJobSetup )
1229 {
1230     ImplJobSetup& rData = aJobSetup.ImplGetData();
1231 
1232     // The angle that a landscape page will be turned counterclockwise wrt to portrait.
1233     int     nLandscapeAngle = mpInfoPrinter ? mpInfoPrinter->GetLandscapeAngle( &maJobSetup.ImplGetConstData() ) : 900;
1234     int     nPaperCount     = GetPaperInfoCount();
1235     PaperInfo aInfo(rData.GetPaperWidth(), rData.GetPaperHeight());
1236 
1237     // Compare all paper formats and get the appropriate one
1238     for ( int i = 0; i < nPaperCount; i++ )
1239     {
1240         const PaperInfo& rPaperInfo = GetPaperInfo( i );
1241 
1242         if ( aInfo.sloppyEqual(rPaperInfo) )
1243         {
1244             rData.SetPaperFormat(
1245                 ImplGetPaperFormat( rPaperInfo.getWidth(),
1246                     rPaperInfo.getHeight() ));
1247             rData.SetOrientation( Orientation::Portrait );
1248             return;
1249         }
1250     }
1251 
1252     // If the printer supports landscape orientation, check paper sizes again
1253     // with landscape orientation. This is necessary as a printer driver provides
1254     // all paper sizes with portrait orientation only!!
1255     if ( !(rData.GetPaperFormat() == PAPER_USER &&
1256          nLandscapeAngle != 0 &&
1257          HasSupport( PrinterSupport::SetOrientation )))
1258         return;
1259 
1260     const long nRotatedWidth = rData.GetPaperHeight();
1261     const long nRotatedHeight = rData.GetPaperWidth();
1262     PaperInfo aRotatedInfo(nRotatedWidth, nRotatedHeight);
1263 
1264     for ( int i = 0; i < nPaperCount; i++ )
1265     {
1266         const PaperInfo& rPaperInfo = GetPaperInfo( i );
1267 
1268         if ( aRotatedInfo.sloppyEqual( rPaperInfo ) )
1269         {
1270             rData.SetPaperFormat(
1271                 ImplGetPaperFormat( rPaperInfo.getWidth(),
1272                     rPaperInfo.getHeight() ));
1273             rData.SetOrientation( Orientation::Landscape );
1274             return;
1275         }
1276     }
1277 }
1278 
1279 void Printer::SetPaper( Paper ePaper )
1280 {
1281     if ( mbInPrintPage )
1282         return;
1283 
1284     if ( maJobSetup.ImplGetConstData().GetPaperFormat() == ePaper )
1285         return;
1286 
1287     JobSetup      aJobSetup = maJobSetup;
1288     ImplJobSetup& rData = aJobSetup.ImplGetData();
1289 
1290     rData.SetPaperFormat( ePaper );
1291     if ( ePaper != PAPER_USER )
1292     {
1293         PaperInfo aInfo(ePaper);
1294         rData.SetPaperWidth( aInfo.getWidth() );
1295         rData.SetPaperHeight( aInfo.getHeight() );
1296     }
1297 
1298     if ( IsDisplayPrinter() )
1299     {
1300         mbNewJobSetup = true;
1301         maJobSetup = aJobSetup;
1302         return;
1303     }
1304 
1305     ReleaseGraphics();
1306     if ( ePaper == PAPER_USER )
1307         ImplFindPaperFormatForUserSize( aJobSetup );
1308     if ( mpInfoPrinter->SetData( JobSetFlags::PAPERSIZE | JobSetFlags::ORIENTATION, &rData ))
1309     {
1310         ImplUpdateJobSetupPaper( aJobSetup );
1311         mbNewJobSetup = true;
1312         maJobSetup = aJobSetup;
1313         ImplUpdatePageData();
1314         ImplUpdateFontList();
1315     }
1316 }
1317 
1318 bool Printer::SetPaperSizeUser( const Size& rSize )
1319 {
1320     if ( mbInPrintPage )
1321         return false;
1322 
1323     const Size aPixSize = LogicToPixel( rSize );
1324     const Size aPageSize = PixelToLogic(aPixSize, MapMode(MapUnit::Map100thMM));
1325     bool bNeedToChange(maJobSetup.ImplGetConstData().GetPaperWidth() != aPageSize.Width() ||
1326         maJobSetup.ImplGetConstData().GetPaperHeight() != aPageSize.Height());
1327 
1328     if(!bNeedToChange)
1329     {
1330         // #i122984# only need to change when Paper is different from PAPER_USER and
1331         // the mapped Paper which will created below in the call to ImplFindPaperFormatForUserSize
1332         // and will replace maJobSetup.ImplGetConstData()->GetPaperFormat(). This leads to
1333         // unnecessary JobSetups, e.g. when printing a multi-page fax, but also with
1334         // normal print
1335         const Paper aPaper = ImplGetPaperFormat(aPageSize.Width(), aPageSize.Height());
1336 
1337         bNeedToChange = maJobSetup.ImplGetConstData().GetPaperFormat() != PAPER_USER &&
1338             maJobSetup.ImplGetConstData().GetPaperFormat() != aPaper;
1339     }
1340 
1341     if(bNeedToChange)
1342     {
1343         JobSetup      aJobSetup = maJobSetup;
1344         ImplJobSetup& rData = aJobSetup.ImplGetData();
1345         rData.SetPaperFormat( PAPER_USER );
1346         rData.SetPaperWidth( aPageSize.Width() );
1347         rData.SetPaperHeight( aPageSize.Height() );
1348 
1349         if ( IsDisplayPrinter() )
1350         {
1351             mbNewJobSetup = true;
1352             maJobSetup = aJobSetup;
1353             return true;
1354         }
1355 
1356         ReleaseGraphics();
1357         ImplFindPaperFormatForUserSize( aJobSetup );
1358 
1359         // Changing the paper size can also change the orientation!
1360         if ( mpInfoPrinter->SetData( JobSetFlags::PAPERSIZE | JobSetFlags::ORIENTATION, &rData ))
1361         {
1362             ImplUpdateJobSetupPaper( aJobSetup );
1363             mbNewJobSetup = true;
1364             maJobSetup = aJobSetup;
1365             ImplUpdatePageData();
1366             ImplUpdateFontList();
1367             return true;
1368         }
1369         else
1370             return false;
1371     }
1372 
1373     return true;
1374 }
1375 
1376 int Printer::GetPaperInfoCount() const
1377 {
1378     if( ! mpInfoPrinter )
1379         return 0;
1380     if( ! mpInfoPrinter->m_bPapersInit )
1381         mpInfoPrinter->InitPaperFormats( &maJobSetup.ImplGetConstData() );
1382     return mpInfoPrinter->m_aPaperFormats.size();
1383 }
1384 
1385 OUString Printer::GetPaperName( Paper ePaper )
1386 {
1387     ImplSVData* pSVData = ImplGetSVData();
1388     if( pSVData->maPaperNames.empty() )
1389     {
1390         static const int PaperIndex[] =
1391         {
1392             PAPER_A0, PAPER_A1, PAPER_A2, PAPER_A3, PAPER_A4, PAPER_A5, PAPER_B4_ISO, PAPER_B5_ISO,
1393             PAPER_LETTER, PAPER_LEGAL, PAPER_TABLOID, PAPER_USER, PAPER_B6_ISO, PAPER_ENV_C4, PAPER_ENV_C5,
1394             PAPER_ENV_C6, PAPER_ENV_C65, PAPER_ENV_DL, PAPER_SLIDE_DIA, PAPER_SCREEN_4_3, PAPER_C, PAPER_D,
1395             PAPER_E, PAPER_EXECUTIVE, PAPER_FANFOLD_LEGAL_DE, PAPER_ENV_MONARCH, PAPER_ENV_PERSONAL, PAPER_ENV_9,
1396             PAPER_ENV_10, PAPER_ENV_11, PAPER_ENV_12, PAPER_KAI16, PAPER_KAI32, PAPER_KAI32BIG, PAPER_B4_JIS,
1397             PAPER_B5_JIS, PAPER_B6_JIS, PAPER_LEDGER, PAPER_STATEMENT, PAPER_QUARTO, PAPER_10x14, PAPER_ENV_14,
1398             PAPER_ENV_C3, PAPER_ENV_ITALY, PAPER_FANFOLD_US, PAPER_FANFOLD_DE, PAPER_POSTCARD_JP, PAPER_9x11,
1399             PAPER_10x11, PAPER_15x11, PAPER_ENV_INVITE, PAPER_A_PLUS, PAPER_B_PLUS, PAPER_LETTER_PLUS, PAPER_A4_PLUS,
1400             PAPER_DOUBLEPOSTCARD_JP, PAPER_A6, PAPER_12x11, PAPER_A7, PAPER_A8, PAPER_A9, PAPER_A10, PAPER_B0_ISO,
1401             PAPER_B1_ISO, PAPER_B2_ISO, PAPER_B3_ISO, PAPER_B7_ISO, PAPER_B8_ISO, PAPER_B9_ISO, PAPER_B10_ISO,
1402             PAPER_ENV_C2, PAPER_ENV_C7, PAPER_ENV_C8, PAPER_ARCHA, PAPER_ARCHB, PAPER_ARCHC, PAPER_ARCHD,
1403             PAPER_ARCHE, PAPER_SCREEN_16_9, PAPER_SCREEN_16_10, PAPER_16K_195x270, PAPER_16K_197x273
1404         };
1405         assert(SAL_N_ELEMENTS(PaperIndex) == SAL_N_ELEMENTS(RID_STR_PAPERNAMES) && "localized paper name count wrong");
1406         for (size_t i = 0; i < SAL_N_ELEMENTS(PaperIndex); ++i)
1407             pSVData->maPaperNames[PaperIndex[i]] = VclResId(RID_STR_PAPERNAMES[i]);
1408     }
1409 
1410     std::unordered_map<int,OUString>::const_iterator it = pSVData->maPaperNames.find( static_cast<int>(ePaper) );
1411     return (it != pSVData->maPaperNames.end()) ? it->second : OUString();
1412 }
1413 
1414 const PaperInfo& Printer::GetPaperInfo( int nPaper ) const
1415 {
1416     if( ! mpInfoPrinter )
1417         return ImplGetEmptyPaper();
1418     if( ! mpInfoPrinter->m_bPapersInit )
1419         mpInfoPrinter->InitPaperFormats( &maJobSetup.ImplGetConstData() );
1420     if( mpInfoPrinter->m_aPaperFormats.empty() || nPaper < 0 || nPaper >= int(mpInfoPrinter->m_aPaperFormats.size()) )
1421         return ImplGetEmptyPaper();
1422     return mpInfoPrinter->m_aPaperFormats[nPaper];
1423 }
1424 
1425 Size Printer::GetPaperSize( int nPaper )
1426 {
1427     PaperInfo aInfo = GetPaperInfo( nPaper );
1428     return PixelToLogic( Size( aInfo.getWidth(), aInfo.getHeight() ) );
1429 }
1430 
1431 void Printer::SetDuplexMode( DuplexMode eDuplex )
1432 {
1433     if ( mbInPrintPage )
1434         return;
1435 
1436     if ( maJobSetup.ImplGetConstData().GetDuplexMode() == eDuplex )
1437         return;
1438 
1439     JobSetup      aJobSetup = maJobSetup;
1440     ImplJobSetup& rData = aJobSetup.ImplGetData();
1441 
1442     rData.SetDuplexMode( eDuplex );
1443 
1444     if ( IsDisplayPrinter() )
1445     {
1446         mbNewJobSetup = true;
1447         maJobSetup = aJobSetup;
1448         return;
1449     }
1450 
1451     ReleaseGraphics();
1452     if ( mpInfoPrinter->SetData( JobSetFlags::DUPLEXMODE, &rData ) )
1453     {
1454         ImplUpdateJobSetupPaper( aJobSetup );
1455         mbNewJobSetup = true;
1456         maJobSetup = aJobSetup;
1457         ImplUpdatePageData();
1458         ImplUpdateFontList();
1459     }
1460 }
1461 
1462 DuplexMode Printer::GetDuplexMode() const
1463 {
1464     return maJobSetup.ImplGetConstData().GetDuplexMode();
1465 }
1466 
1467 Paper Printer::GetPaper() const
1468 {
1469     return maJobSetup.ImplGetConstData().GetPaperFormat();
1470 }
1471 
1472 sal_uInt16 Printer::GetPaperBinCount() const
1473 {
1474     if ( IsDisplayPrinter() )
1475         return 0;
1476 
1477     return mpInfoPrinter->GetPaperBinCount( &maJobSetup.ImplGetConstData() );
1478 }
1479 
1480 OUString Printer::GetPaperBinName( sal_uInt16 nPaperBin ) const
1481 {
1482     if ( IsDisplayPrinter() )
1483         return OUString();
1484 
1485     if ( nPaperBin < GetPaperBinCount() )
1486         return mpInfoPrinter->GetPaperBinName( &maJobSetup.ImplGetConstData(), nPaperBin );
1487     else
1488         return OUString();
1489 }
1490 
1491 void Printer::SetCopyCount( sal_uInt16 nCopy, bool bCollate )
1492 {
1493     mnCopyCount = nCopy;
1494     mbCollateCopy = bCollate;
1495 }
1496 
1497 ErrCode Printer::ImplSalPrinterErrorCodeToVCL( SalPrinterError nError )
1498 {
1499     ErrCode nVCLError;
1500     switch ( nError )
1501     {
1502         case SalPrinterError::NONE:
1503             nVCLError = ERRCODE_NONE;
1504             break;
1505         case SalPrinterError::Abort:
1506             nVCLError = PRINTER_ABORT;
1507             break;
1508         default:
1509             nVCLError = PRINTER_GENERALERROR;
1510             break;
1511     }
1512 
1513     return nVCLError;
1514 }
1515 
1516 void Printer::EndJob()
1517 {
1518     if ( !IsJobActive() )
1519         return;
1520 
1521     SAL_WARN_IF( mbInPrintPage, "vcl.gdi", "Printer::EndJob() - StartPage() without EndPage() called" );
1522 
1523     mbJobActive = false;
1524 
1525     if ( mpPrinter )
1526     {
1527         ReleaseGraphics();
1528 
1529         mbPrinting      = false;
1530 
1531         mbDevOutput = false;
1532         mpPrinter->EndJob();
1533         mpPrinter.reset();
1534     }
1535 }
1536 
1537 void Printer::ImplStartPage()
1538 {
1539     if ( !IsJobActive() )
1540         return;
1541 
1542     if ( !mpPrinter )
1543         return;
1544 
1545     SalGraphics* pGraphics = mpPrinter->StartPage( &maJobSetup.ImplGetData(),
1546                                                    mbNewJobSetup );
1547     if ( pGraphics )
1548     {
1549         ReleaseGraphics();
1550         mpJobGraphics = pGraphics;
1551     }
1552     mbDevOutput = true;
1553 
1554     // PrintJob not aborted ???
1555     if ( IsJobActive() )
1556         mbInPrintPage = true;
1557 }
1558 
1559 void Printer::ImplEndPage()
1560 {
1561     if ( !IsJobActive() )
1562         return;
1563 
1564     mbInPrintPage = false;
1565 
1566     if ( mpPrinter )
1567     {
1568         mpPrinter->EndPage();
1569         ReleaseGraphics();
1570         mbDevOutput = false;
1571 
1572         mpJobGraphics = nullptr;
1573         mbNewJobSetup = false;
1574     }
1575 }
1576 
1577 void Printer::updatePrinters()
1578 {
1579     ImplSVData*         pSVData = ImplGetSVData();
1580     ImplPrnQueueList*   pPrnList = pSVData->maGDIData.mpPrinterQueueList.get();
1581 
1582     if ( !pPrnList )
1583         return;
1584 
1585     std::unique_ptr<ImplPrnQueueList> pNewList(new ImplPrnQueueList);
1586     pSVData->mpDefInst->GetPrinterQueueInfo( pNewList.get() );
1587 
1588     bool bChanged = pPrnList->m_aQueueInfos.size() != pNewList->m_aQueueInfos.size();
1589     for( decltype(pPrnList->m_aQueueInfos)::size_type i = 0; ! bChanged && i < pPrnList->m_aQueueInfos.size(); i++ )
1590     {
1591         ImplPrnQueueData& rInfo     = pPrnList->m_aQueueInfos[i];
1592         ImplPrnQueueData& rNewInfo  = pNewList->m_aQueueInfos[i];
1593         if( ! rInfo.mpSalQueueInfo || ! rNewInfo.mpSalQueueInfo || // sanity check
1594             rInfo.mpSalQueueInfo->maPrinterName != rNewInfo.mpSalQueueInfo->maPrinterName )
1595         {
1596             bChanged = true;
1597         }
1598     }
1599     if( !bChanged )
1600         return;
1601 
1602     ImplDeletePrnQueueList();
1603     pSVData->maGDIData.mpPrinterQueueList = std::move(pNewList);
1604 
1605     Application* pApp = GetpApp();
1606     if( pApp )
1607     {
1608         DataChangedEvent aDCEvt( DataChangedEventType::PRINTER );
1609         Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt);
1610         Application::NotifyAllWindows( aDCEvt );
1611     }
1612 }
1613 
1614 bool Printer::UsePolyPolygonForComplexGradient()
1615 {
1616     return true;
1617 }
1618 
1619 void Printer::ClipAndDrawGradientMetafile ( const Gradient &rGradient, const tools::PolyPolygon &rPolyPoly )
1620 {
1621     const tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
1622 
1623     Push( PushFlags::CLIPREGION );
1624     IntersectClipRegion(vcl::Region(rPolyPoly));
1625     DrawGradient( aBoundRect, rGradient );
1626     Pop();
1627 }
1628 
1629 void Printer::SetFontOrientation( LogicalFontInstance* const pFontEntry ) const
1630 {
1631     pFontEntry->mnOrientation = pFontEntry->mxFontMetric->GetOrientation();
1632 }
1633 
1634 vcl::Region Printer::ClipToDeviceBounds(vcl::Region aRegion) const
1635 {
1636     return aRegion;
1637 }
1638 
1639 Bitmap Printer::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
1640 {
1641     SAL_WARN("vcl.gdi", "GetBitmap(): This should never be called on by a Printer instance");
1642 
1643     return OutputDevice::GetBitmap( rSrcPt, rSize );
1644 }
1645 
1646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1647