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