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 <vcl/weld.hxx>
21 #include <vcl/print.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/metaact.hxx>
24 #include <configsettings.hxx>
25 #include <tools/urlobj.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <comphelper/sequence.hxx>
28 #include <sal/types.h>
29 #include <sal/log.hxx>
30 #include <tools/debug.hxx>
31 #include <tools/diagnose_ex.h>
32 
33 #include <printdlg.hxx>
34 #include <svdata.hxx>
35 #include <salinst.hxx>
36 #include <salprn.hxx>
37 #include <strings.hrc>
38 
39 #include <com/sun/star/ui/dialogs/FilePicker.hpp>
40 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
41 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
42 #include <com/sun/star/view/DuplexMode.hpp>
43 #include <com/sun/star/lang/IllegalArgumentException.hpp>
44 #include <com/sun/star/awt/Size.hpp>
45 
46 #include <unordered_map>
47 #include <unordered_set>
48 
49 using namespace vcl;
50 
51 namespace {
52 
53 class ImplPageCache
54 {
55     struct CacheEntry
56     {
57         GDIMetaFile                 aPage;
58         PrinterController::PageSize aSize;
59     };
60 
61     std::vector< CacheEntry >   maPages;
62     std::vector< sal_Int32 >    maPageNumbers;
63     std::vector< sal_Int32 >    maCacheRanking;
64 
65     static const sal_Int32      nCacheSize = 6;
66 
updateRanking(sal_Int32 nLastHit)67     void updateRanking( sal_Int32 nLastHit )
68     {
69         if( maCacheRanking[0] != nLastHit )
70         {
71             for( sal_Int32 i = nCacheSize-1; i > 0; i-- )
72                 maCacheRanking[i] = maCacheRanking[i-1];
73             maCacheRanking[0] = nLastHit;
74         }
75     }
76 
77 public:
ImplPageCache()78     ImplPageCache()
79     : maPages( nCacheSize )
80     , maPageNumbers( nCacheSize, -1 )
81     , maCacheRanking( nCacheSize )
82     {
83         for( sal_Int32 i = 0; i < nCacheSize; i++ )
84             maCacheRanking[i] = nCacheSize - i - 1;
85     }
86 
87     // caution: does not ensure uniqueness
insert(sal_Int32 i_nPageNo,const GDIMetaFile & i_rPage,const PrinterController::PageSize & i_rSize)88     void insert( sal_Int32 i_nPageNo, const GDIMetaFile& i_rPage, const PrinterController::PageSize& i_rSize )
89     {
90         sal_Int32 nReplacePage = maCacheRanking.back();
91         maPages[ nReplacePage ].aPage = i_rPage;
92         maPages[ nReplacePage ].aSize = i_rSize;
93         maPageNumbers[ nReplacePage ] = i_nPageNo;
94         // cache insertion means in our case, the page was just queried
95         // so update the ranking
96         updateRanking( nReplacePage );
97     }
98 
99     // caution: bad algorithm; should there ever be reason to increase the cache size beyond 6
100     // this needs to be urgently rewritten. However do NOT increase the cache size lightly,
101     // whole pages can be rather memory intensive
get(sal_Int32 i_nPageNo,GDIMetaFile & o_rPageFile,PrinterController::PageSize & o_rSize)102     bool get( sal_Int32 i_nPageNo, GDIMetaFile& o_rPageFile, PrinterController::PageSize& o_rSize )
103     {
104         for( sal_Int32 i = 0; i < nCacheSize; ++i )
105         {
106             if( maPageNumbers[i] == i_nPageNo )
107             {
108                 updateRanking( i );
109                 o_rPageFile = maPages[i].aPage;
110                 o_rSize = maPages[i].aSize;
111                 return true;
112             }
113         }
114         return false;
115     }
116 
invalidate()117     void invalidate()
118     {
119         for( sal_Int32 i = 0; i < nCacheSize; ++i )
120         {
121             maPageNumbers[i] = -1;
122             maPages[i].aPage.Clear();
123             maCacheRanking[i] = nCacheSize - i - 1;
124         }
125     }
126 };
127 
128 }
129 
130 class vcl::ImplPrinterControllerData
131 {
132 public:
133     struct ControlDependency
134     {
135         OUString       maDependsOnName;
136         sal_Int32           mnDependsOnEntry;
137 
ControlDependencyvcl::ImplPrinterControllerData::ControlDependency138         ControlDependency() : mnDependsOnEntry( -1 ) {}
139     };
140 
141     typedef std::unordered_map< OUString, size_t > PropertyToIndexMap;
142     typedef std::unordered_map< OUString, ControlDependency > ControlDependencyMap;
143     typedef std::unordered_map< OUString, css::uno::Sequence< sal_Bool > > ChoiceDisableMap;
144 
145     VclPtr< Printer >                                           mxPrinter;
146     weld::Window*                                               mpWindow;
147     css::uno::Sequence< css::beans::PropertyValue >             maUIOptions;
148     std::vector< css::beans::PropertyValue >                    maUIProperties;
149     std::vector< bool >                                         maUIPropertyEnabled;
150     PropertyToIndexMap                                          maPropertyToIndex;
151     ControlDependencyMap                                        maControlDependencies;
152     ChoiceDisableMap                                            maChoiceDisableMap;
153     bool                                                        mbFirstPage;
154     bool                                                        mbLastPage;
155     bool                                                        mbReversePageOrder;
156     bool                                                        mbPapersizeFromSetup;
157     bool                                                        mbPapersizeFromUser;
158     bool                                                        mbPrinterModified;
159     css::view::PrintableState                                   meJobState;
160 
161     vcl::PrinterController::MultiPageSetup                      maMultiPage;
162 
163     std::shared_ptr<vcl::PrintProgressDialog>                   mxProgress;
164 
165     ImplPageCache                                               maPageCache;
166 
167     // set by user through printer properties subdialog of printer settings dialog
168     Size                                                        maDefaultPageSize;
169     // set by user through print dialog
170     Size                                                        maUserPageSize;
171     // set by user through printer properties subdialog of printer settings dialog
172     sal_Int32                                                   mnDefaultPaperBin;
173     // Set by user through printer properties subdialog of print dialog.
174     // Overrides application-set tray for a page.
175     sal_Int32                                                   mnFixedPaperBin;
176 
177     // N.B. Apparently we have three levels of paper tray settings
178     // (latter overrides former):
179     // 1. default tray
180     // 2. tray set for a concrete page by an application, e.g., writer
181     //    allows setting a printer tray (for the default printer) for a
182     //    page style. This setting can be overridden by user by selecting
183     //    "Use only paper tray from printer preferences" on the Options
184     //    page in the print dialog, in which case the default tray is
185     //    used for all pages.
186     // 3. tray set in printer properties the printer dialog
187     // I'm not quite sure why 1. and 3. are distinct, but the commit
188     // history suggests this is intentional...
189 
ImplPrinterControllerData()190     ImplPrinterControllerData() :
191         mpWindow( nullptr ),
192         mbFirstPage( true ),
193         mbLastPage( false ),
194         mbReversePageOrder( false ),
195         mbPapersizeFromSetup( false ),
196         mbPapersizeFromUser( false ),
197         mbPrinterModified( false ),
198         meJobState( css::view::PrintableState_JOB_STARTED ),
199         mnDefaultPaperBin( -1 ),
200         mnFixedPaperBin( -1 )
201     {}
202 
~ImplPrinterControllerData()203     ~ImplPrinterControllerData()
204     {
205         if (mxProgress)
206         {
207             mxProgress->response(RET_CANCEL);
208             mxProgress.reset();
209         }
210     }
211 
getRealPaperSize(const Size & i_rPageSize,bool bNoNUP) const212     const Size& getRealPaperSize( const Size& i_rPageSize, bool bNoNUP ) const
213     {
214         if ( mbPapersizeFromUser )
215             return maUserPageSize;
216         if( mbPapersizeFromSetup )
217             return maDefaultPageSize;
218         if( maMultiPage.nRows * maMultiPage.nColumns > 1 && ! bNoNUP )
219             return maMultiPage.aPaperSize;
220         return i_rPageSize;
221     }
222     PrinterController::PageSize modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps );
223     void resetPaperToLastConfigured();
224 };
225 
PrinterController(const VclPtr<Printer> & i_xPrinter,weld::Window * i_pWindow)226 PrinterController::PrinterController(const VclPtr<Printer>& i_xPrinter, weld::Window* i_pWindow)
227     : mpImplData( new ImplPrinterControllerData )
228 {
229     mpImplData->mxPrinter = i_xPrinter;
230     mpImplData->mpWindow = i_pWindow;
231 }
232 
queryFile(Printer const * pPrinter)233 static OUString queryFile( Printer const * pPrinter )
234 {
235     OUString aResult;
236 
237     css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
238     css::uno::Reference< css::ui::dialogs::XFilePicker3 > xFilePicker = css::ui::dialogs::FilePicker::createWithMode(xContext, css::ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION);
239 
240     try
241     {
242 #ifdef UNX
243         // add PostScript and PDF
244         bool bPS = true, bPDF = true;
245         if( pPrinter )
246         {
247             if( pPrinter->GetCapabilities( PrinterCapType::PDF ) )
248                 bPS = false;
249             else
250                 bPDF = false;
251         }
252         if( bPS )
253             xFilePicker->appendFilter( "PostScript", "*.ps" );
254         if( bPDF )
255             xFilePicker->appendFilter( "Portable Document Format", "*.pdf" );
256 #elif defined _WIN32
257         (void)pPrinter;
258         xFilePicker->appendFilter( "*.PRN", "*.prn" );
259 #endif
260         // add arbitrary files
261         xFilePicker->appendFilter(VclResId(SV_STDTEXT_ALLFILETYPES), "*.*");
262     }
263     catch (const css::lang::IllegalArgumentException&)
264     {
265         TOOLS_WARN_EXCEPTION( "vcl.gdi", "caught IllegalArgumentException when registering filter" );
266     }
267 
268     if( xFilePicker->execute() == css::ui::dialogs::ExecutableDialogResults::OK )
269     {
270         css::uno::Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() );
271         INetURLObject aObj( aPathSeq[0] );
272         aResult = aObj.PathToFileName();
273     }
274     return aResult;
275 }
276 
277 namespace {
278 
279 struct PrintJobAsync
280 {
281     std::shared_ptr<PrinterController>  mxController;
282     JobSetup                            maInitSetup;
283 
PrintJobAsync__anone572c6250211::PrintJobAsync284     PrintJobAsync(const std::shared_ptr<PrinterController>& i_xController,
285                   const JobSetup& i_rInitSetup)
286     : mxController( i_xController ), maInitSetup( i_rInitSetup )
287     {}
288 
289     DECL_LINK( ExecJob, void*, void );
290 };
291 
292 }
293 
IMPL_LINK_NOARG(PrintJobAsync,ExecJob,void *,void)294 IMPL_LINK_NOARG(PrintJobAsync, ExecJob, void*, void)
295 {
296     Printer::ImplPrintJob(mxController, maInitSetup);
297 
298     // clean up, do not access members after this
299     delete this;
300 }
301 
PrintJob(const std::shared_ptr<PrinterController> & i_xController,const JobSetup & i_rInitSetup)302 void Printer::PrintJob(const std::shared_ptr<PrinterController>& i_xController,
303                        const JobSetup& i_rInitSetup)
304 {
305     bool bSynchronous = false;
306     css::beans::PropertyValue* pVal = i_xController->getValue( "Wait" );
307     if( pVal )
308         pVal->Value >>= bSynchronous;
309 
310     if( bSynchronous )
311         ImplPrintJob(i_xController, i_rInitSetup);
312     else
313     {
314         PrintJobAsync* pAsync = new PrintJobAsync(i_xController, i_rInitSetup);
315         Application::PostUserEvent( LINK( pAsync, PrintJobAsync, ExecJob ) );
316     }
317 }
318 
PreparePrintJob(std::shared_ptr<PrinterController> xController,const JobSetup & i_rInitSetup)319 bool Printer::PreparePrintJob(std::shared_ptr<PrinterController> xController,
320                            const JobSetup& i_rInitSetup)
321 {
322     // check if there is a default printer; if not, show an error box (if appropriate)
323     if( GetDefaultPrinterName().isEmpty() )
324     {
325         if (xController->isShowDialogs())
326         {
327             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornoprinterdialog.ui"));
328             std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoPrinterDialog"));
329             xBox->run();
330         }
331         xController->setValue( "IsDirect",
332                                css::uno::makeAny( false ) );
333     }
334 
335     // setup printer
336 
337     // #i114306# changed behavior back from persistence
338     // if no specific printer is already set, create the default printer
339     if (!xController->getPrinter())
340     {
341         OUString aPrinterName( i_rInitSetup.GetPrinterName() );
342         VclPtrInstance<Printer> xPrinter( aPrinterName );
343         xPrinter->SetJobSetup(i_rInitSetup);
344         xController->setPrinter(xPrinter);
345         xController->setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred());
346     }
347 
348     // reset last page property
349     xController->setLastPage(false);
350 
351     // update "PageRange" property inferring from other properties:
352     // case 1: "Pages" set from UNO API ->
353     //         setup "Print Selection" and insert "PageRange" attribute
354     // case 2: "All pages" is selected
355     //         update "Page range" attribute to have a sensible default,
356     //         but leave "All" as selected
357 
358     // "Pages" attribute from API is now equivalent to "PageRange"
359     // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
360     // Argh ! That sure needs cleaning up
361     css::beans::PropertyValue* pContentVal = xController->getValue("PrintRange");
362     if( ! pContentVal )
363         pContentVal = xController->getValue("PrintContent");
364 
365     // case 1: UNO API has set "Pages"
366     css::beans::PropertyValue* pPagesVal = xController->getValue("Pages");
367     if( pPagesVal )
368     {
369         OUString aPagesVal;
370         pPagesVal->Value >>= aPagesVal;
371         if( !aPagesVal.isEmpty() )
372         {
373             // "Pages" attribute from API is now equivalent to "PageRange"
374             // AND "PrintContent" = 1 except calc where it is "PrintRange" = 1
375             // Argh ! That sure needs cleaning up
376             if( pContentVal )
377             {
378                 pContentVal->Value <<= sal_Int32( 1 );
379                 xController->setValue("PageRange", pPagesVal->Value);
380             }
381         }
382     }
383     // case 2: is "All" selected ?
384     else if( pContentVal )
385     {
386         sal_Int32 nContent = -1;
387         if( pContentVal->Value >>= nContent )
388         {
389             if( nContent == 0 )
390             {
391                 // do not overwrite PageRange if it is already set
392                 css::beans::PropertyValue* pRangeVal = xController->getValue("PageRange");
393                 OUString aRange;
394                 if( pRangeVal )
395                     pRangeVal->Value >>= aRange;
396                 if( aRange.isEmpty() )
397                 {
398                     sal_Int32 nPages = xController->getPageCount();
399                     if( nPages > 0 )
400                     {
401                         OUStringBuffer aBuf( 32 );
402                         aBuf.append( "1" );
403                         if( nPages > 1 )
404                         {
405                             aBuf.append( "-" );
406                             aBuf.append( nPages );
407                         }
408                         xController->setValue("PageRange", css::uno::makeAny(aBuf.makeStringAndClear()));
409                     }
410                 }
411             }
412         }
413     }
414 
415     css::beans::PropertyValue* pReverseVal = xController->getValue("PrintReverse");
416     if( pReverseVal )
417     {
418         bool bReverse = false;
419         pReverseVal->Value >>= bReverse;
420         xController->setReversePrint( bReverse );
421     }
422 
423     css::beans::PropertyValue* pPapersizeFromSetupVal = xController->getValue("PapersizeFromSetup");
424     if( pPapersizeFromSetupVal )
425     {
426         bool bPapersizeFromSetup = false;
427         pPapersizeFromSetupVal->Value >>= bPapersizeFromSetup;
428         xController->setPapersizeFromSetup(bPapersizeFromSetup);
429     }
430 
431     // setup NUp printing from properties
432     sal_Int32 nRows = xController->getIntProperty("NUpRows", 1);
433     sal_Int32 nCols = xController->getIntProperty("NUpColumns", 1);
434     if( nRows > 1 || nCols > 1 )
435     {
436         PrinterController::MultiPageSetup aMPS;
437         aMPS.nRows         = std::max<sal_Int32>(nRows, 1);
438         aMPS.nColumns      = std::max<sal_Int32>(nCols, 1);
439         sal_Int32 nValue = xController->getIntProperty("NUpPageMarginLeft", aMPS.nLeftMargin);
440         if( nValue >= 0 )
441             aMPS.nLeftMargin = nValue;
442         nValue = xController->getIntProperty("NUpPageMarginRight", aMPS.nRightMargin);
443         if( nValue >= 0 )
444             aMPS.nRightMargin = nValue;
445         nValue = xController->getIntProperty( "NUpPageMarginTop", aMPS.nTopMargin );
446         if( nValue >= 0 )
447             aMPS.nTopMargin = nValue;
448         nValue = xController->getIntProperty( "NUpPageMarginBottom", aMPS.nBottomMargin );
449         if( nValue >= 0 )
450             aMPS.nBottomMargin = nValue;
451         nValue = xController->getIntProperty( "NUpHorizontalSpacing", aMPS.nHorizontalSpacing );
452         if( nValue >= 0 )
453             aMPS.nHorizontalSpacing = nValue;
454         nValue = xController->getIntProperty( "NUpVerticalSpacing", aMPS.nVerticalSpacing );
455         if( nValue >= 0 )
456             aMPS.nVerticalSpacing = nValue;
457         aMPS.bDrawBorder = xController->getBoolProperty( "NUpDrawBorder", aMPS.bDrawBorder );
458         aMPS.nOrder = static_cast<NupOrderType>(xController->getIntProperty( "NUpSubPageOrder", static_cast<sal_Int32>(aMPS.nOrder) ));
459         aMPS.aPaperSize = xController->getPrinter()->PixelToLogic( xController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) );
460         css::beans::PropertyValue* pPgSizeVal = xController->getValue( "NUpPaperSize" );
461         css::awt::Size aSizeVal;
462         if( pPgSizeVal && (pPgSizeVal->Value >>= aSizeVal) )
463         {
464             aMPS.aPaperSize.setWidth( aSizeVal.Width );
465             aMPS.aPaperSize.setHeight( aSizeVal.Height );
466         }
467 
468         xController->setMultipage( aMPS );
469     }
470 
471     // in direct print case check whether there is anything to print.
472     // if not, show an errorbox (if appropriate)
473     if( xController->isShowDialogs() && xController->isDirectPrint() )
474     {
475         if( xController->getFilteredPageCount() == 0 )
476         {
477             std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(xController->getWindow(), "vcl/ui/errornocontentdialog.ui"));
478             std::unique_ptr<weld::MessageDialog> xBox(xBuilder->weld_message_dialog("ErrorNoContentDialog"));
479             xBox->run();
480             return false;
481         }
482     }
483 
484     // check if the printer brings up its own dialog
485     // in that case leave the work to that dialog
486     if( ! xController->getPrinter()->GetCapabilities( PrinterCapType::ExternalDialog ) &&
487         ! xController->isDirectPrint() &&
488         xController->isShowDialogs()
489         )
490     {
491         try
492         {
493             PrintDialog aDlg(xController->getWindow(), xController);
494             if (!aDlg.run())
495             {
496                 xController->abortJob();
497                 return false;
498             }
499             if (aDlg.isPrintToFile())
500             {
501                 OUString aFile = queryFile( xController->getPrinter().get() );
502                 if( aFile.isEmpty() )
503                 {
504                     xController->abortJob();
505                     return false;
506                 }
507                 xController->setValue( "LocalFileName",
508                                        css::uno::makeAny( aFile ) );
509             }
510             else if (aDlg.isSingleJobs())
511             {
512                 xController->getPrinter()->SetSinglePrintJobs(true);
513             }
514         }
515         catch (const std::bad_alloc&)
516         {
517         }
518     }
519 
520     xController->pushPropertiesToPrinter();
521     return true;
522 }
523 
ExecutePrintJob(const std::shared_ptr<PrinterController> & xController)524 bool Printer::ExecutePrintJob(const std::shared_ptr<PrinterController>& xController)
525 {
526     OUString aJobName;
527     css::beans::PropertyValue* pJobNameVal = xController->getValue( "JobName" );
528     if( pJobNameVal )
529         pJobNameVal->Value >>= aJobName;
530 
531     return xController->getPrinter()->StartJob( aJobName, xController );
532 }
533 
FinishPrintJob(const std::shared_ptr<PrinterController> & xController)534 void Printer::FinishPrintJob(const std::shared_ptr<PrinterController>& xController)
535 {
536     xController->resetPaperToLastConfigured();
537     xController->jobFinished( xController->getJobState() );
538 }
539 
ImplPrintJob(const std::shared_ptr<PrinterController> & xController,const JobSetup & i_rInitSetup)540 void Printer::ImplPrintJob(const std::shared_ptr<PrinterController>& xController,
541                            const JobSetup& i_rInitSetup)
542 {
543     if (PreparePrintJob(xController, i_rInitSetup))
544     {
545         ExecutePrintJob(xController);
546     }
547     FinishPrintJob(xController);
548 }
549 
StartJob(const OUString & i_rJobName,std::shared_ptr<vcl::PrinterController> const & i_xController)550 bool Printer::StartJob( const OUString& i_rJobName, std::shared_ptr<vcl::PrinterController> const & i_xController)
551 {
552     mnError = ERRCODE_NONE;
553 
554     if ( IsDisplayPrinter() )
555         return false;
556 
557     if ( IsJobActive() || IsPrinting() )
558         return false;
559 
560     sal_uInt32 nCopies = mnCopyCount;
561     bool    bCollateCopy = mbCollateCopy;
562     bool    bUserCopy = false;
563 
564     if ( nCopies > 1 )
565     {
566         const sal_uInt32 nDevCopy = GetCapabilities( bCollateCopy
567             ? PrinterCapType::CollateCopies
568             : PrinterCapType::Copies );
569 
570         // need to do copies by hand ?
571         if ( nCopies > nDevCopy )
572         {
573             bUserCopy = true;
574             nCopies = 1;
575             bCollateCopy = false;
576         }
577     }
578     else
579         bCollateCopy = false;
580 
581     ImplSVData* pSVData = ImplGetSVData();
582     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
583 
584     if (!mpPrinter)
585         return false;
586 
587     bool bSinglePrintJobs = i_xController->getPrinter()->IsSinglePrintJobs();
588 
589     css::beans::PropertyValue* pFileValue = i_xController->getValue("LocalFileName");
590     if( pFileValue )
591     {
592         OUString aFile;
593         pFileValue->Value >>= aFile;
594         if( !aFile.isEmpty() )
595         {
596             mbPrintFile = true;
597             maPrintFile = aFile;
598             bSinglePrintJobs = false;
599         }
600     }
601 
602     OUString* pPrintFile = nullptr;
603     if ( mbPrintFile )
604         pPrintFile = &maPrintFile;
605     mpPrinterOptions->ReadFromConfig( mbPrintFile );
606 
607     mbPrinting              = true;
608     if( GetCapabilities( PrinterCapType::UsePullModel ) )
609     {
610         mbJobActive             = true;
611         // SAL layer does all necessary page printing
612         // and also handles showing a dialog
613         // that also means it must call jobStarted when the dialog is finished
614         // it also must set the JobState of the Controller
615         if( mpPrinter->StartJob( pPrintFile,
616                                  i_rJobName,
617                                  Application::GetDisplayName(),
618                                  &maJobSetup.ImplGetData(),
619                                  *i_xController) )
620         {
621             EndJob();
622         }
623         else
624         {
625             mnError = ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode());
626             if ( !mnError )
627                 mnError = PRINTER_GENERALERROR;
628             mbPrinting = false;
629             mpPrinter.reset();
630             mbJobActive = false;
631 
632             GDIMetaFile aDummyFile;
633             i_xController->setLastPage(true);
634             i_xController->getFilteredPageFile(0, aDummyFile);
635 
636             return false;
637         }
638     }
639     else
640     {
641         // possibly a dialog has been shown
642         // now the real job starts
643         i_xController->setJobState( css::view::PrintableState_JOB_STARTED );
644         i_xController->jobStarted();
645 
646         int nJobs = 1;
647         int nOuterRepeatCount = 1;
648         int nInnerRepeatCount = 1;
649         if( bUserCopy )
650         {
651             if( mbCollateCopy )
652                 nOuterRepeatCount = mnCopyCount;
653             else
654                 nInnerRepeatCount = mnCopyCount;
655         }
656         if( bSinglePrintJobs )
657         {
658             nJobs = mnCopyCount;
659             nCopies = 1;
660             nOuterRepeatCount = nInnerRepeatCount = 1;
661         }
662 
663         for( int nJobIteration = 0; nJobIteration < nJobs; nJobIteration++ )
664         {
665             bool bError = false;
666             if( mpPrinter->StartJob( pPrintFile,
667                                      i_rJobName,
668                                      Application::GetDisplayName(),
669                                      nCopies,
670                                      bCollateCopy,
671                                      i_xController->isDirectPrint(),
672                                      &maJobSetup.ImplGetData() ) )
673             {
674                 bool bAborted = false;
675                 mbJobActive             = true;
676                 i_xController->createProgressDialog();
677                 const int nPages = i_xController->getFilteredPageCount();
678                 // abort job, if no pages will be printed.
679                 if ( nPages == 0 )
680                 {
681                     i_xController->abortJob();
682                     bAborted = true;
683                 }
684                 for( int nOuterIteration = 0; nOuterIteration < nOuterRepeatCount && ! bAborted; nOuterIteration++ )
685                 {
686                     for( int nPage = 0; nPage < nPages && ! bAborted; nPage++ )
687                     {
688                         for( int nInnerIteration = 0; nInnerIteration < nInnerRepeatCount && ! bAborted; nInnerIteration++ )
689                         {
690                             if( nPage == nPages-1 &&
691                                 nOuterIteration == nOuterRepeatCount-1 &&
692                                 nInnerIteration == nInnerRepeatCount-1 &&
693                                 nJobIteration == nJobs-1 )
694                             {
695                                 i_xController->setLastPage(true);
696                             }
697                             i_xController->printFilteredPage(nPage);
698                             if (i_xController->isProgressCanceled())
699                             {
700                                 i_xController->abortJob();
701                             }
702                             if (i_xController->getJobState() ==
703                                     css::view::PrintableState_JOB_ABORTED)
704                             {
705                                 bAborted = true;
706                             }
707                         }
708                     }
709                     // FIXME: duplex ?
710                 }
711                 EndJob();
712 
713                 if( nJobIteration < nJobs-1 )
714                 {
715                     mpPrinter = pSVData->mpDefInst->CreatePrinter( mpInfoPrinter );
716 
717                     if ( mpPrinter )
718                         mbPrinting              = true;
719                     else
720                         bError = true;
721                 }
722             }
723             else
724                 bError = true;
725 
726             if( bError )
727             {
728                 mnError = mpPrinter ? ImplSalPrinterErrorCodeToVCL(mpPrinter->GetErrorCode()) : ERRCODE_NONE;
729                 if ( !mnError )
730                     mnError = PRINTER_GENERALERROR;
731                 i_xController->setJobState( mnError == PRINTER_ABORT
732                                             ? css::view::PrintableState_JOB_ABORTED
733                                             : css::view::PrintableState_JOB_FAILED );
734                 mbPrinting = false;
735                 mpPrinter.reset();
736 
737                 return false;
738             }
739         }
740 
741         if (i_xController->getJobState() == css::view::PrintableState_JOB_STARTED)
742             i_xController->setJobState(css::view::PrintableState_JOB_SPOOLED);
743     }
744 
745     // make last used printer persistent for UI jobs
746     if (i_xController->isShowDialogs() && !i_xController->isDirectPrint())
747     {
748         SettingsConfigItem* pItem = SettingsConfigItem::get();
749         pItem->setValue( "PrintDialog",
750                          "LastPrinterUsed",
751                          GetName()
752                          );
753     }
754 
755     return true;
756 }
757 
~PrinterController()758 PrinterController::~PrinterController()
759 {
760 }
761 
getJobState() const762 css::view::PrintableState PrinterController::getJobState() const
763 {
764     return mpImplData->meJobState;
765 }
766 
setJobState(css::view::PrintableState i_eState)767 void PrinterController::setJobState( css::view::PrintableState i_eState )
768 {
769     mpImplData->meJobState = i_eState;
770 }
771 
getPrinter() const772 const VclPtr<Printer>& PrinterController::getPrinter() const
773 {
774     return mpImplData->mxPrinter;
775 }
776 
getWindow() const777 weld::Window* PrinterController::getWindow() const
778 {
779     return mpImplData->mpWindow;
780 }
781 
dialogsParentClosing()782 void PrinterController::dialogsParentClosing()
783 {
784     mpImplData->mpWindow = nullptr;
785     if (mpImplData->mxProgress)
786     {
787         // close the dialog without doing anything, just get rid of it
788         mpImplData->mxProgress->response(RET_OK);
789         mpImplData->mxProgress.reset();
790     }
791 }
792 
setPrinter(const VclPtr<Printer> & i_rPrinter)793 void PrinterController::setPrinter( const VclPtr<Printer>& i_rPrinter )
794 {
795     VclPtr<Printer> xPrinter = mpImplData->mxPrinter;
796 
797     Size aPaperSize;          // Save current paper size
798     Orientation eOrientation = Orientation::Portrait; // Save current paper orientation
799     bool bSavedSizeOrientation = false;
800 
801     // #tdf 126744 Transfer paper size and orientation settings to newly selected printer
802     if ( xPrinter )
803     {
804         aPaperSize = xPrinter->GetPaperSize();
805         eOrientation = xPrinter->GetOrientation();
806         bSavedSizeOrientation = true;
807     }
808 
809     mpImplData->mxPrinter = i_rPrinter;
810     setValue( "Name",
811               css::uno::makeAny( i_rPrinter->GetName() ) );
812     mpImplData->mnDefaultPaperBin = mpImplData->mxPrinter->GetPaperBin();
813     mpImplData->mxPrinter->Push();
814     mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
815     mpImplData->maDefaultPageSize = mpImplData->mxPrinter->GetPaperSize();
816 
817     if ( bSavedSizeOrientation )
818     {
819           mpImplData->mxPrinter->SetPaperSizeUser(aPaperSize);
820           mpImplData->mxPrinter->SetOrientation(eOrientation);
821     }
822 
823     mpImplData->mbPapersizeFromUser = false;
824     mpImplData->mxPrinter->Pop();
825     mpImplData->mnFixedPaperBin = -1;
826 }
827 
resetPrinterOptions(bool i_bFileOutput)828 void PrinterController::resetPrinterOptions( bool i_bFileOutput )
829 {
830     PrinterOptions aOpt;
831     aOpt.ReadFromConfig( i_bFileOutput );
832     mpImplData->mxPrinter->SetPrinterOptions( aOpt );
833 }
834 
setupPrinter(weld::Window * i_pParent)835 void PrinterController::setupPrinter( weld::Window* i_pParent )
836 {
837     bool bRet = false;
838 
839     // Important to hold printer alive while doing setup etc.
840     VclPtr< Printer > xPrinter = mpImplData->mxPrinter;
841 
842     if( !xPrinter )
843         return;
844 
845     xPrinter->Push();
846     xPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
847 
848     // get current data
849     Size aPaperSize(xPrinter->GetPaperSize());
850     Orientation eOrientation = xPrinter->GetOrientation();
851     sal_uInt16 nPaperBin = xPrinter->GetPaperBin();
852 
853     // reset paper size back to last configured size, not
854     // whatever happens to be the current page
855     // (but only if the printer config has changed, otherwise
856     // don't override printer page auto-detection - tdf#91362)
857     if (getPrinterModified() || getPapersizeFromSetup())
858     {
859         resetPaperToLastConfigured();
860     }
861 
862     // call driver setup
863     bRet = xPrinter->Setup( i_pParent, PrinterSetupMode::SingleJob );
864     SAL_WARN_IF(xPrinter != mpImplData->mxPrinter, "vcl.gdi",
865                 "Printer changed underneath us during setup");
866     xPrinter = mpImplData->mxPrinter;
867 
868     Size aNewPaperSize(xPrinter->GetPaperSize());
869     if (bRet)
870     {
871         bool bInvalidateCache = false;
872         setPapersizeFromSetup(xPrinter->GetPrinterSettingsPreferred());
873 
874         // was papersize overridden ? if so we need to take action if we're
875         // configured to use the driver papersize
876         if (aNewPaperSize != mpImplData->maDefaultPageSize)
877         {
878             mpImplData->maDefaultPageSize = aNewPaperSize;
879             bInvalidateCache = getPapersizeFromSetup();
880         }
881 
882         // was bin overridden ? if so we need to take action
883         sal_uInt16 nNewPaperBin = xPrinter->GetPaperBin();
884         if (nNewPaperBin != nPaperBin)
885         {
886             mpImplData->mnFixedPaperBin = nNewPaperBin;
887             bInvalidateCache = true;
888         }
889 
890         if (bInvalidateCache)
891         {
892             mpImplData->maPageCache.invalidate();
893         }
894     }
895     else
896     {
897         //restore to whatever it was before we entered this method
898         xPrinter->SetOrientation( eOrientation );
899         if (aPaperSize != aNewPaperSize)
900             xPrinter->SetPaperSizeUser(aPaperSize);
901     }
902     xPrinter->Pop();
903 }
904 
modifyJobSetup(const css::uno::Sequence<css::beans::PropertyValue> & i_rProps)905 PrinterController::PageSize vcl::ImplPrinterControllerData::modifyJobSetup( const css::uno::Sequence< css::beans::PropertyValue >& i_rProps )
906 {
907     PrinterController::PageSize aPageSize;
908     aPageSize.aSize = mxPrinter->GetPaperSize();
909     css::awt::Size aSetSize, aIsSize;
910     sal_Int32 nPaperBin = mnDefaultPaperBin;
911     for( const auto& rProp : i_rProps )
912     {
913         if ( rProp.Name == "PreferredPageSize" )
914         {
915             rProp.Value >>= aSetSize;
916         }
917         else if ( rProp.Name == "PageSize" )
918         {
919             rProp.Value >>= aIsSize;
920         }
921         else if ( rProp.Name == "PageIncludesNonprintableArea" )
922         {
923             bool bVal = false;
924             rProp.Value >>= bVal;
925             aPageSize.bFullPaper = bVal;
926         }
927         else if ( rProp.Name == "PrinterPaperTray" )
928         {
929             sal_Int32 nBin = -1;
930             rProp.Value >>= nBin;
931             if( nBin >= 0 && nBin < static_cast<sal_Int32>(mxPrinter->GetPaperBinCount()) )
932                 nPaperBin = nBin;
933         }
934     }
935 
936     Size aCurSize( mxPrinter->GetPaperSize() );
937     if( aSetSize.Width && aSetSize.Height )
938     {
939         Size aSetPaperSize( aSetSize.Width, aSetSize.Height );
940         Size aRealPaperSize( getRealPaperSize( aSetPaperSize, true/*bNoNUP*/ ) );
941         if( aRealPaperSize != aCurSize )
942             aIsSize = aSetSize;
943     }
944 
945     if( aIsSize.Width && aIsSize.Height )
946     {
947         aPageSize.aSize.setWidth( aIsSize.Width );
948         aPageSize.aSize.setHeight( aIsSize.Height );
949 
950         Size aRealPaperSize( getRealPaperSize( aPageSize.aSize, true/*bNoNUP*/ ) );
951         if( aRealPaperSize != aCurSize )
952             mxPrinter->SetPaperSizeUser( aRealPaperSize );
953     }
954 
955     // paper bin set from properties in print dialog overrides
956     // application default for a page
957     if ( mnFixedPaperBin != -1 )
958         nPaperBin = mnFixedPaperBin;
959 
960     if( nPaperBin != -1 && nPaperBin != mxPrinter->GetPaperBin() )
961         mxPrinter->SetPaperBin( nPaperBin );
962 
963     return aPageSize;
964 }
965 
966 //fdo#61886
967 
968 //when printing is finished, set the paper size of the printer to either what
969 //the user explicitly set as the desired paper size, or fallback to whatever
970 //the printer had before printing started. That way it doesn't contain the last
971 //paper size of a multiple paper size using document when we are in our normal
972 //auto accept document paper size mode and end up overwriting the original
973 //paper size setting for file->printer_settings just by pressing "ok" in the
974 //print dialog
resetPaperToLastConfigured()975 void vcl::ImplPrinterControllerData::resetPaperToLastConfigured()
976 {
977     mxPrinter->Push();
978     mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
979     Size aCurSize(mxPrinter->GetPaperSize());
980     if (aCurSize != maDefaultPageSize)
981         mxPrinter->SetPaperSizeUser(maDefaultPageSize);
982     mxPrinter->Pop();
983 }
984 
getPageCountProtected() const985 int PrinterController::getPageCountProtected() const
986 {
987     const MapMode aMapMode( MapUnit::Map100thMM );
988 
989     mpImplData->mxPrinter->Push();
990     mpImplData->mxPrinter->SetMapMode( aMapMode );
991     int nPages = getPageCount();
992     mpImplData->mxPrinter->Pop();
993     return nPages;
994 }
995 
getPageParametersProtected(int i_nPage) const996 css::uno::Sequence< css::beans::PropertyValue > PrinterController::getPageParametersProtected( int i_nPage ) const
997 {
998     const MapMode aMapMode( MapUnit::Map100thMM );
999 
1000     mpImplData->mxPrinter->Push();
1001     mpImplData->mxPrinter->SetMapMode( aMapMode );
1002     css::uno::Sequence< css::beans::PropertyValue > aResult( getPageParameters( i_nPage ) );
1003     mpImplData->mxPrinter->Pop();
1004     return aResult;
1005 }
1006 
getPageFile(int i_nUnfilteredPage,GDIMetaFile & o_rMtf,bool i_bMayUseCache)1007 PrinterController::PageSize PrinterController::getPageFile( int i_nUnfilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
1008 {
1009     // update progress if necessary
1010     if( mpImplData->mxProgress )
1011     {
1012         // do nothing if printing is canceled
1013         if( mpImplData->mxProgress->isCanceled() )
1014             return PrinterController::PageSize();
1015         mpImplData->mxProgress->tick();
1016         Application::Reschedule( true );
1017     }
1018 
1019     if( i_bMayUseCache )
1020     {
1021         PrinterController::PageSize aPageSize;
1022         if( mpImplData->maPageCache.get( i_nUnfilteredPage, o_rMtf, aPageSize ) )
1023         {
1024             return aPageSize;
1025         }
1026     }
1027     else
1028         mpImplData->maPageCache.invalidate();
1029 
1030     o_rMtf.Clear();
1031 
1032     // get page parameters
1033     css::uno::Sequence< css::beans::PropertyValue > aPageParm( getPageParametersProtected( i_nUnfilteredPage ) );
1034     const MapMode aMapMode( MapUnit::Map100thMM );
1035 
1036     mpImplData->mxPrinter->Push();
1037     mpImplData->mxPrinter->SetMapMode( aMapMode );
1038 
1039     // modify job setup if necessary
1040     PrinterController::PageSize aPageSize = mpImplData->modifyJobSetup( aPageParm );
1041 
1042     o_rMtf.SetPrefSize( aPageSize.aSize );
1043     o_rMtf.SetPrefMapMode( aMapMode );
1044 
1045     mpImplData->mxPrinter->EnableOutput( false );
1046 
1047     o_rMtf.Record( mpImplData->mxPrinter.get() );
1048 
1049     printPage( i_nUnfilteredPage );
1050 
1051     o_rMtf.Stop();
1052     o_rMtf.WindStart();
1053     mpImplData->mxPrinter->Pop();
1054 
1055     if( i_bMayUseCache )
1056         mpImplData->maPageCache.insert( i_nUnfilteredPage, o_rMtf, aPageSize );
1057 
1058     // reset "FirstPage" property to false now we've gotten at least our first one
1059     mpImplData->mbFirstPage = false;
1060 
1061     return aPageSize;
1062 }
1063 
appendSubPage(GDIMetaFile & o_rMtf,const tools::Rectangle & i_rClipRect,GDIMetaFile & io_rSubPage,bool i_bDrawBorder)1064 static void appendSubPage( GDIMetaFile& o_rMtf, const tools::Rectangle& i_rClipRect, GDIMetaFile& io_rSubPage, bool i_bDrawBorder )
1065 {
1066     // intersect all clipregion actions with our clip rect
1067     io_rSubPage.WindStart();
1068     io_rSubPage.Clip( i_rClipRect );
1069 
1070     // save gstate
1071     o_rMtf.AddAction( new MetaPushAction( PushFlags::ALL ) );
1072 
1073     // clip to page rect
1074     o_rMtf.AddAction( new MetaClipRegionAction( vcl::Region( i_rClipRect ), true ) );
1075 
1076     // append the subpage
1077     io_rSubPage.WindStart();
1078     io_rSubPage.Play( o_rMtf );
1079 
1080     // restore gstate
1081     o_rMtf.AddAction( new MetaPopAction() );
1082 
1083     // draw a border
1084     if( !i_bDrawBorder )
1085         return;
1086 
1087     // save gstate
1088     o_rMtf.AddAction( new MetaPushAction( PushFlags::LINECOLOR | PushFlags::FILLCOLOR | PushFlags::CLIPREGION | PushFlags::MAPMODE ) );
1089     o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) );
1090 
1091     tools::Rectangle aBorderRect( i_rClipRect );
1092     o_rMtf.AddAction( new MetaLineColorAction( COL_BLACK, true ) );
1093     o_rMtf.AddAction( new MetaFillColorAction( COL_TRANSPARENT, false ) );
1094     o_rMtf.AddAction( new MetaRectAction( aBorderRect ) );
1095 
1096     // restore gstate
1097     o_rMtf.AddAction( new MetaPopAction() );
1098 }
1099 
getFilteredPageFile(int i_nFilteredPage,GDIMetaFile & o_rMtf,bool i_bMayUseCache)1100 PrinterController::PageSize PrinterController::getFilteredPageFile( int i_nFilteredPage, GDIMetaFile& o_rMtf, bool i_bMayUseCache )
1101 {
1102     const MultiPageSetup& rMPS( mpImplData->maMultiPage );
1103     int nSubPages = rMPS.nRows * rMPS.nColumns;
1104     if( nSubPages < 1 )
1105         nSubPages = 1;
1106 
1107     // reverse sheet order
1108     if( mpImplData->mbReversePageOrder )
1109     {
1110         int nDocPages = getFilteredPageCount();
1111         i_nFilteredPage = nDocPages - 1 - i_nFilteredPage;
1112     }
1113 
1114     // there is no filtering to be done (and possibly the page size of the
1115     // original page is to be set), when N-Up is "neutral" that is there is
1116     // only one subpage and the margins are 0
1117     if( nSubPages == 1 &&
1118         rMPS.nLeftMargin == 0 && rMPS.nRightMargin == 0 &&
1119         rMPS.nTopMargin == 0 && rMPS.nBottomMargin == 0 )
1120     {
1121         PrinterController::PageSize aPageSize = getPageFile( i_nFilteredPage, o_rMtf, i_bMayUseCache );
1122         if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED)
1123         {   // rhbz#657394: check that we are still printing...
1124             return PrinterController::PageSize();
1125         }
1126         Size aPaperSize = mpImplData->getRealPaperSize( aPageSize.aSize, true );
1127         mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1128         mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize );
1129         if( aPaperSize != aPageSize.aSize )
1130         {
1131             // user overridden page size, center Metafile
1132             o_rMtf.WindStart();
1133             tools::Long nDX = (aPaperSize.Width() - aPageSize.aSize.Width()) / 2;
1134             tools::Long nDY = (aPaperSize.Height() - aPageSize.aSize.Height()) / 2;
1135             o_rMtf.Move( nDX, nDY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1136             o_rMtf.WindStart();
1137             o_rMtf.SetPrefSize( aPaperSize );
1138             aPageSize.aSize = aPaperSize;
1139         }
1140         return aPageSize;
1141     }
1142 
1143     // set last page property really only on the very last page to be rendered
1144     // that is on the last subpage of a NUp run
1145     bool bIsLastPage = mpImplData->mbLastPage;
1146     mpImplData->mbLastPage = false;
1147 
1148     Size aPaperSize( mpImplData->getRealPaperSize( mpImplData->maMultiPage.aPaperSize, false ) );
1149 
1150     // multi page area: page size minus margins + one time spacing right and down
1151     // the added spacing is so each subpage can be calculated including its spacing
1152     Size aMPArea( aPaperSize );
1153     aMPArea.AdjustWidth( -(rMPS.nLeftMargin + rMPS.nRightMargin) );
1154     aMPArea.AdjustWidth(rMPS.nHorizontalSpacing );
1155     aMPArea.AdjustHeight( -(rMPS.nTopMargin + rMPS.nBottomMargin) );
1156     aMPArea.AdjustHeight(rMPS.nVerticalSpacing );
1157 
1158     // determine offsets
1159     tools::Long nAdvX = aMPArea.Width() / rMPS.nColumns;
1160     tools::Long nAdvY = aMPArea.Height() / rMPS.nRows;
1161 
1162     // determine size of a "cell" subpage, leave a little space around pages
1163     Size aSubPageSize( nAdvX - rMPS.nHorizontalSpacing, nAdvY - rMPS.nVerticalSpacing );
1164 
1165     o_rMtf.Clear();
1166     o_rMtf.SetPrefSize( aPaperSize );
1167     o_rMtf.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
1168     o_rMtf.AddAction( new MetaMapModeAction( MapMode( MapUnit::Map100thMM ) ) );
1169 
1170     int nDocPages = getPageCountProtected();
1171     if (mpImplData->meJobState != css::view::PrintableState_JOB_STARTED)
1172     {   // rhbz#657394: check that we are still printing...
1173         return PrinterController::PageSize();
1174     }
1175     for( int nSubPage = 0; nSubPage < nSubPages; nSubPage++ )
1176     {
1177         // map current sub page to real page
1178         int nPage = i_nFilteredPage * nSubPages + nSubPage;
1179         if( nSubPage == nSubPages-1 ||
1180             nPage == nDocPages-1 )
1181         {
1182             mpImplData->mbLastPage = bIsLastPage;
1183         }
1184         if( nPage >= 0 && nPage < nDocPages )
1185         {
1186             GDIMetaFile aPageFile;
1187             PrinterController::PageSize aPageSize = getPageFile( nPage, aPageFile, i_bMayUseCache );
1188             if( aPageSize.aSize.Width() && aPageSize.aSize.Height() )
1189             {
1190                 tools::Long nCellX = 0, nCellY = 0;
1191                 switch( rMPS.nOrder )
1192                 {
1193                 case NupOrderType::LRTB:
1194                     nCellX = (nSubPage % rMPS.nColumns);
1195                     nCellY = (nSubPage / rMPS.nColumns);
1196                     break;
1197                 case NupOrderType::TBLR:
1198                     nCellX = (nSubPage / rMPS.nRows);
1199                     nCellY = (nSubPage % rMPS.nRows);
1200                     break;
1201                 case NupOrderType::RLTB:
1202                     nCellX = rMPS.nColumns - 1 - (nSubPage % rMPS.nColumns);
1203                     nCellY = (nSubPage / rMPS.nColumns);
1204                     break;
1205                 case NupOrderType::TBRL:
1206                     nCellX = rMPS.nColumns - 1 - (nSubPage / rMPS.nRows);
1207                     nCellY = (nSubPage % rMPS.nRows);
1208                     break;
1209                 }
1210                 // scale the metafile down to a sub page size
1211                 double fScaleX = double(aSubPageSize.Width())/double(aPageSize.aSize.Width());
1212                 double fScaleY = double(aSubPageSize.Height())/double(aPageSize.aSize.Height());
1213                 double fScale  = std::min( fScaleX, fScaleY );
1214                 aPageFile.Scale( fScale, fScale );
1215                 aPageFile.WindStart();
1216 
1217                 // move the subpage so it is centered in its "cell"
1218                 tools::Long nOffX = (aSubPageSize.Width() - tools::Long(double(aPageSize.aSize.Width()) * fScale)) / 2;
1219                 tools::Long nOffY = (aSubPageSize.Height() - tools::Long(double(aPageSize.aSize.Height()) * fScale)) / 2;
1220                 tools::Long nX = rMPS.nLeftMargin + nOffX + nAdvX * nCellX;
1221                 tools::Long nY = rMPS.nTopMargin + nOffY + nAdvY * nCellY;
1222                 aPageFile.Move( nX, nY, mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1223                 aPageFile.WindStart();
1224                 // calculate border rectangle
1225                 tools::Rectangle aSubPageRect( Point( nX, nY ),
1226                                         Size( tools::Long(double(aPageSize.aSize.Width())*fScale),
1227                                               tools::Long(double(aPageSize.aSize.Height())*fScale) ) );
1228 
1229                 // append subpage to page
1230                 appendSubPage( o_rMtf, aSubPageRect, aPageFile, rMPS.bDrawBorder );
1231             }
1232         }
1233     }
1234     o_rMtf.WindStart();
1235 
1236     // subsequent getPageFile calls have changed the paper, reset it to current value
1237     mpImplData->mxPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1238     mpImplData->mxPrinter->SetPaperSizeUser( aPaperSize );
1239 
1240     return PrinterController::PageSize( aPaperSize, true );
1241 }
1242 
getFilteredPageCount() const1243 int PrinterController::getFilteredPageCount() const
1244 {
1245     int nDiv = mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns;
1246     if( nDiv < 1 )
1247         nDiv = 1;
1248     return (getPageCountProtected() + (nDiv-1)) / nDiv;
1249 }
1250 
removeTransparencies(GDIMetaFile const & i_rIn,GDIMetaFile & o_rOut)1251 DrawModeFlags PrinterController::removeTransparencies( GDIMetaFile const & i_rIn, GDIMetaFile& o_rOut )
1252 {
1253     DrawModeFlags nRestoreDrawMode = mpImplData->mxPrinter->GetDrawMode();
1254     sal_Int32 nMaxBmpDPIX = mpImplData->mxPrinter->GetDPIX();
1255     sal_Int32 nMaxBmpDPIY = mpImplData->mxPrinter->GetDPIY();
1256 
1257     const PrinterOptions&   rPrinterOptions = mpImplData->mxPrinter->GetPrinterOptions();
1258 
1259     static const sal_Int32 OPTIMAL_BMP_RESOLUTION = 300;
1260     static const sal_Int32 NORMAL_BMP_RESOLUTION  = 200;
1261 
1262     if( rPrinterOptions.IsReduceBitmaps() )
1263     {
1264         // calculate maximum resolution for bitmap graphics
1265         if( PrinterBitmapMode::Optimal == rPrinterOptions.GetReducedBitmapMode() )
1266         {
1267             nMaxBmpDPIX = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1268             nMaxBmpDPIY = std::min( sal_Int32(OPTIMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1269         }
1270         else if( PrinterBitmapMode::Normal == rPrinterOptions.GetReducedBitmapMode() )
1271         {
1272             nMaxBmpDPIX = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIX );
1273             nMaxBmpDPIY = std::min( sal_Int32(NORMAL_BMP_RESOLUTION), nMaxBmpDPIY );
1274         }
1275         else
1276         {
1277             nMaxBmpDPIX = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIX );
1278             nMaxBmpDPIY = std::min( sal_Int32(rPrinterOptions.GetReducedBitmapResolution()), nMaxBmpDPIY );
1279         }
1280     }
1281 
1282     // convert to greyscales
1283     if( rPrinterOptions.IsConvertToGreyscales() )
1284     {
1285         mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() |
1286                                             ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
1287                                               DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
1288     }
1289 
1290     // disable transparency output
1291     if( rPrinterOptions.IsReduceTransparency() && ( PrinterTransparencyMode::NONE == rPrinterOptions.GetReducedTransparencyMode() ) )
1292     {
1293         mpImplData->mxPrinter->SetDrawMode( mpImplData->mxPrinter->GetDrawMode() | DrawModeFlags::NoTransparency );
1294     }
1295 
1296     Color aBg( COL_TRANSPARENT ); // default: let RemoveTransparenciesFromMetaFile do its own background logic
1297     if( mpImplData->maMultiPage.nRows * mpImplData->maMultiPage.nColumns > 1 )
1298     {
1299         // in N-Up printing we have no "page" background operation
1300         // we also have no way to determine the paper color
1301         // so let's go for white, which will kill 99.9% of the real cases
1302         aBg = COL_WHITE;
1303     }
1304     mpImplData->mxPrinter->RemoveTransparenciesFromMetaFile( i_rIn, o_rOut, nMaxBmpDPIX, nMaxBmpDPIY,
1305                                                              rPrinterOptions.IsReduceTransparency(),
1306                                                              rPrinterOptions.GetReducedTransparencyMode() == PrinterTransparencyMode::Auto,
1307                                                              rPrinterOptions.IsReduceBitmaps() && rPrinterOptions.IsReducedBitmapIncludesTransparency(),
1308                                                              aBg
1309                                                              );
1310     return nRestoreDrawMode;
1311 }
1312 
printFilteredPage(int i_nPage)1313 void PrinterController::printFilteredPage( int i_nPage )
1314 {
1315     if( mpImplData->meJobState != css::view::PrintableState_JOB_STARTED )
1316         return; // rhbz#657394: check that we are still printing...
1317 
1318     GDIMetaFile aPageFile;
1319     PrinterController::PageSize aPageSize = getFilteredPageFile( i_nPage, aPageFile );
1320 
1321     if( mpImplData->mxProgress )
1322     {
1323         // do nothing if printing is canceled
1324         if( mpImplData->mxProgress->isCanceled() )
1325         {
1326             setJobState( css::view::PrintableState_JOB_ABORTED );
1327             return;
1328         }
1329     }
1330 
1331     // in N-Up printing set the correct page size
1332     mpImplData->mxPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
1333     // aPageSize was filtered through mpImplData->getRealPaperSize already by getFilteredPageFile()
1334     mpImplData->mxPrinter->SetPaperSizeUser( aPageSize.aSize );
1335     if( mpImplData->mnFixedPaperBin != -1 &&
1336         mpImplData->mxPrinter->GetPaperBin() != mpImplData->mnFixedPaperBin )
1337     {
1338         mpImplData->mxPrinter->SetPaperBin( mpImplData->mnFixedPaperBin );
1339     }
1340 
1341     // if full paper is meant to be used, move the output to accommodate for pageoffset
1342     if( aPageSize.bFullPaper )
1343     {
1344         Point aPageOffset( mpImplData->mxPrinter->GetPageOffset() );
1345         aPageFile.WindStart();
1346         aPageFile.Move( -aPageOffset.X(), -aPageOffset.Y(), mpImplData->mxPrinter->GetDPIX(), mpImplData->mxPrinter->GetDPIY() );
1347     }
1348 
1349     GDIMetaFile aCleanedFile;
1350     DrawModeFlags nRestoreDrawMode = removeTransparencies( aPageFile, aCleanedFile );
1351 
1352     mpImplData->mxPrinter->EnableOutput();
1353 
1354     // actually print the page
1355     mpImplData->mxPrinter->ImplStartPage();
1356 
1357     mpImplData->mxPrinter->Push();
1358     aCleanedFile.WindStart();
1359     aCleanedFile.Play(*mpImplData->mxPrinter);
1360     mpImplData->mxPrinter->Pop();
1361 
1362     mpImplData->mxPrinter->ImplEndPage();
1363 
1364     mpImplData->mxPrinter->SetDrawMode( nRestoreDrawMode );
1365 }
1366 
jobStarted()1367 void PrinterController::jobStarted()
1368 {
1369 }
1370 
jobFinished(css::view::PrintableState)1371 void PrinterController::jobFinished( css::view::PrintableState )
1372 {
1373 }
1374 
abortJob()1375 void PrinterController::abortJob()
1376 {
1377     setJobState( css::view::PrintableState_JOB_ABORTED );
1378     // applications (well, sw) depend on a page request with "IsLastPage" = true
1379     // to free resources, else they (well, sw) will crash eventually
1380     setLastPage( true );
1381 
1382     if (mpImplData->mxProgress)
1383     {
1384         mpImplData->mxProgress->response(RET_CANCEL);
1385         mpImplData->mxProgress.reset();
1386     }
1387 
1388     GDIMetaFile aMtf;
1389     getPageFile( 0, aMtf );
1390 }
1391 
setLastPage(bool i_bLastPage)1392 void PrinterController::setLastPage( bool i_bLastPage )
1393 {
1394     mpImplData->mbLastPage = i_bLastPage;
1395 }
1396 
setReversePrint(bool i_bReverse)1397 void PrinterController::setReversePrint( bool i_bReverse )
1398 {
1399     mpImplData->mbReversePageOrder = i_bReverse;
1400 }
1401 
setPapersizeFromSetup(bool i_bPapersizeFromSetup)1402 void PrinterController::setPapersizeFromSetup( bool i_bPapersizeFromSetup )
1403 {
1404     mpImplData->mbPapersizeFromSetup = i_bPapersizeFromSetup;
1405     mpImplData->mxPrinter->SetPrinterSettingsPreferred( i_bPapersizeFromSetup );
1406     if ( i_bPapersizeFromSetup )
1407         mpImplData->mbPapersizeFromUser = !i_bPapersizeFromSetup;
1408 }
1409 
getPapersizeFromSetup() const1410 bool PrinterController::getPapersizeFromSetup() const
1411 {
1412     return mpImplData->mbPapersizeFromSetup;
1413 }
1414 
getPaperSizeSetup() const1415 Size& PrinterController::getPaperSizeSetup() const
1416 {
1417     return mpImplData->maDefaultPageSize;
1418 }
1419 
setPaperSizeFromUser(Size i_aUserSize)1420 void PrinterController::setPaperSizeFromUser( Size i_aUserSize )
1421 {
1422     mpImplData->mbPapersizeFromUser = true;
1423     mpImplData->mbPapersizeFromSetup = false;
1424     mpImplData->mxPrinter->SetPrinterSettingsPreferred( false );
1425 
1426     mpImplData->maUserPageSize = i_aUserSize;
1427 }
1428 
getPaperSizeFromUser() const1429 Size& PrinterController::getPaperSizeFromUser() const
1430 {
1431     return mpImplData->maUserPageSize;
1432 }
1433 
isPaperSizeFromUser() const1434 bool PrinterController::isPaperSizeFromUser() const
1435 {
1436     return mpImplData->mbPapersizeFromUser;
1437 }
1438 
setPrinterModified(bool i_bPrinterModified)1439 void PrinterController::setPrinterModified( bool i_bPrinterModified )
1440 {
1441     mpImplData->mbPrinterModified = i_bPrinterModified;
1442 }
1443 
getPrinterModified() const1444 bool PrinterController::getPrinterModified() const
1445 {
1446     return mpImplData->mbPrinterModified;
1447 }
1448 
getJobProperties(const css::uno::Sequence<css::beans::PropertyValue> & i_rMergeList) const1449 css::uno::Sequence< css::beans::PropertyValue > PrinterController::getJobProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rMergeList ) const
1450 {
1451     std::unordered_set< OUString > aMergeSet;
1452     size_t nResultLen = size_t(i_rMergeList.getLength()) + mpImplData->maUIProperties.size() + 3;
1453     for( const auto& rPropVal : i_rMergeList )
1454         aMergeSet.insert( rPropVal.Name );
1455 
1456     css::uno::Sequence< css::beans::PropertyValue > aResult( nResultLen );
1457     std::copy(i_rMergeList.begin(), i_rMergeList.end(), aResult.begin());
1458     int nCur = i_rMergeList.getLength();
1459     for(const css::beans::PropertyValue & rPropVal : mpImplData->maUIProperties)
1460     {
1461         if( aMergeSet.find( rPropVal.Name ) == aMergeSet.end() )
1462             aResult[nCur++] = rPropVal;
1463     }
1464     // append IsFirstPage
1465     if( aMergeSet.find( "IsFirstPage" ) == aMergeSet.end() )
1466     {
1467         css::beans::PropertyValue aVal;
1468         aVal.Name = "IsFirstPage";
1469         aVal.Value <<= mpImplData->mbFirstPage;
1470         aResult[nCur++] = aVal;
1471     }
1472     // append IsLastPage
1473     if( aMergeSet.find( "IsLastPage" ) == aMergeSet.end() )
1474     {
1475         css::beans::PropertyValue aVal;
1476         aVal.Name = "IsLastPage";
1477         aVal.Value <<= mpImplData->mbLastPage;
1478         aResult[nCur++] = aVal;
1479     }
1480     // append IsPrinter
1481     if( aMergeSet.find( "IsPrinter" ) == aMergeSet.end() )
1482     {
1483         css::beans::PropertyValue aVal;
1484         aVal.Name = "IsPrinter";
1485         aVal.Value <<= true;
1486         aResult[nCur++] = aVal;
1487     }
1488     aResult.realloc( nCur );
1489     return aResult;
1490 }
1491 
getUIOptions() const1492 const css::uno::Sequence< css::beans::PropertyValue >& PrinterController::getUIOptions() const
1493 {
1494     return mpImplData->maUIOptions;
1495 }
1496 
getValue(const OUString & i_rProperty)1497 css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty )
1498 {
1499     std::unordered_map< OUString, size_t >::const_iterator it =
1500         mpImplData->maPropertyToIndex.find( i_rProperty );
1501     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr;
1502 }
1503 
getValue(const OUString & i_rProperty) const1504 const css::beans::PropertyValue* PrinterController::getValue( const OUString& i_rProperty ) const
1505 {
1506     std::unordered_map< OUString, size_t >::const_iterator it =
1507         mpImplData->maPropertyToIndex.find( i_rProperty );
1508     return it != mpImplData->maPropertyToIndex.end() ? &mpImplData->maUIProperties[it->second] : nullptr;
1509 }
1510 
setValue(const OUString & i_rPropertyName,const css::uno::Any & i_rValue)1511 void PrinterController::setValue( const OUString& i_rPropertyName, const css::uno::Any& i_rValue )
1512 {
1513     css::beans::PropertyValue aVal;
1514     aVal.Name = i_rPropertyName;
1515     aVal.Value = i_rValue;
1516 
1517     setValue( aVal );
1518 }
1519 
setValue(const css::beans::PropertyValue & i_rPropertyValue)1520 void PrinterController::setValue( const css::beans::PropertyValue& i_rPropertyValue )
1521 {
1522     std::unordered_map< OUString, size_t >::const_iterator it =
1523         mpImplData->maPropertyToIndex.find( i_rPropertyValue.Name );
1524     if( it != mpImplData->maPropertyToIndex.end() )
1525         mpImplData->maUIProperties[ it->second ] = i_rPropertyValue;
1526     else
1527     {
1528         // insert correct index into property map
1529         mpImplData->maPropertyToIndex[ i_rPropertyValue.Name ] = mpImplData->maUIProperties.size();
1530         mpImplData->maUIProperties.push_back( i_rPropertyValue );
1531         mpImplData->maUIPropertyEnabled.push_back( true );
1532     }
1533 }
1534 
setUIOptions(const css::uno::Sequence<css::beans::PropertyValue> & i_rOptions)1535 void PrinterController::setUIOptions( const css::uno::Sequence< css::beans::PropertyValue >& i_rOptions )
1536 {
1537     SAL_WARN_IF( mpImplData->maUIOptions.hasElements(), "vcl.gdi", "setUIOptions called twice !" );
1538 
1539     mpImplData->maUIOptions = i_rOptions;
1540 
1541     for( const auto& rOpt : i_rOptions )
1542     {
1543         css::uno::Sequence< css::beans::PropertyValue > aOptProp;
1544         rOpt.Value >>= aOptProp;
1545         bool bIsEnabled = true;
1546         bool bHaveProperty = false;
1547         OUString aPropName;
1548         vcl::ImplPrinterControllerData::ControlDependency aDep;
1549         css::uno::Sequence< sal_Bool > aChoicesDisabled;
1550         for( const css::beans::PropertyValue& rEntry : std::as_const(aOptProp) )
1551         {
1552             if ( rEntry.Name == "Property" )
1553             {
1554                 css::beans::PropertyValue aVal;
1555                 rEntry.Value >>= aVal;
1556                 DBG_ASSERT( mpImplData->maPropertyToIndex.find( aVal.Name )
1557                             == mpImplData->maPropertyToIndex.end(), "duplicate property entry" );
1558                 setValue( aVal );
1559                 aPropName = aVal.Name;
1560                 bHaveProperty = true;
1561             }
1562             else if ( rEntry.Name == "Enabled" )
1563             {
1564                 bool bValue = true;
1565                 rEntry.Value >>= bValue;
1566                 bIsEnabled = bValue;
1567             }
1568             else if ( rEntry.Name == "DependsOnName" )
1569             {
1570                 rEntry.Value >>= aDep.maDependsOnName;
1571             }
1572             else if ( rEntry.Name == "DependsOnEntry" )
1573             {
1574                 rEntry.Value >>= aDep.mnDependsOnEntry;
1575             }
1576             else if ( rEntry.Name == "ChoicesDisabled" )
1577             {
1578                 rEntry.Value >>= aChoicesDisabled;
1579             }
1580         }
1581         if( bHaveProperty )
1582         {
1583             vcl::ImplPrinterControllerData::PropertyToIndexMap::const_iterator it =
1584                 mpImplData->maPropertyToIndex.find( aPropName );
1585             // sanity check
1586             if( it != mpImplData->maPropertyToIndex.end() )
1587             {
1588                 mpImplData->maUIPropertyEnabled[ it->second ] = bIsEnabled;
1589             }
1590             if( !aDep.maDependsOnName.isEmpty() )
1591                 mpImplData->maControlDependencies[ aPropName ] = aDep;
1592             if( aChoicesDisabled.hasElements() )
1593                 mpImplData->maChoiceDisableMap[ aPropName ] = aChoicesDisabled;
1594         }
1595     }
1596 }
1597 
isUIOptionEnabled(const OUString & i_rProperty) const1598 bool PrinterController::isUIOptionEnabled( const OUString& i_rProperty ) const
1599 {
1600     bool bEnabled = false;
1601     std::unordered_map< OUString, size_t >::const_iterator prop_it =
1602         mpImplData->maPropertyToIndex.find( i_rProperty );
1603     if( prop_it != mpImplData->maPropertyToIndex.end() )
1604     {
1605         bEnabled = mpImplData->maUIPropertyEnabled[prop_it->second];
1606 
1607         if( bEnabled )
1608         {
1609             // check control dependencies
1610             vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1611                 mpImplData->maControlDependencies.find( i_rProperty );
1612             if( it != mpImplData->maControlDependencies.end() )
1613             {
1614                 // check if the dependency is enabled
1615                 // if the dependency is disabled, we are too
1616                 bEnabled = isUIOptionEnabled( it->second.maDependsOnName );
1617 
1618                 if( bEnabled )
1619                 {
1620                     // does the dependency have the correct value ?
1621                     const css::beans::PropertyValue* pVal = getValue( it->second.maDependsOnName );
1622                     OSL_ENSURE( pVal, "unknown property in dependency" );
1623                     if( pVal )
1624                     {
1625                         sal_Int32 nDepVal = 0;
1626                         bool bDepVal = false;
1627                         if( pVal->Value >>= nDepVal )
1628                         {
1629                             bEnabled = (nDepVal == it->second.mnDependsOnEntry) || (it->second.mnDependsOnEntry == -1);
1630                         }
1631                         else if( pVal->Value >>= bDepVal )
1632                         {
1633                             // could be a dependency on a checked boolean
1634                             // in this case the dependency is on a non zero for checked value
1635                             bEnabled = (   bDepVal && it->second.mnDependsOnEntry != 0) ||
1636                                        ( ! bDepVal && it->second.mnDependsOnEntry == 0);
1637                         }
1638                         else
1639                         {
1640                             // if the type does not match something is awry
1641                             OSL_FAIL( "strange type in control dependency" );
1642                             bEnabled = false;
1643                         }
1644                     }
1645                 }
1646             }
1647         }
1648     }
1649     return bEnabled;
1650 }
1651 
isUIChoiceEnabled(const OUString & i_rProperty,sal_Int32 i_nValue) const1652 bool PrinterController::isUIChoiceEnabled( const OUString& i_rProperty, sal_Int32 i_nValue ) const
1653 {
1654     bool bEnabled = true;
1655     ImplPrinterControllerData::ChoiceDisableMap::const_iterator it =
1656         mpImplData->maChoiceDisableMap.find( i_rProperty );
1657     if(it != mpImplData->maChoiceDisableMap.end() )
1658     {
1659         const css::uno::Sequence< sal_Bool >& rDisabled( it->second );
1660         if( i_nValue >= 0 && i_nValue < rDisabled.getLength() )
1661             bEnabled = ! rDisabled[i_nValue];
1662     }
1663     return bEnabled;
1664 }
1665 
makeEnabled(const OUString & i_rProperty)1666 OUString PrinterController::makeEnabled( const OUString& i_rProperty )
1667 {
1668     OUString aDependency;
1669 
1670     vcl::ImplPrinterControllerData::ControlDependencyMap::const_iterator it =
1671         mpImplData->maControlDependencies.find( i_rProperty );
1672     if( it != mpImplData->maControlDependencies.end() )
1673     {
1674         if( isUIOptionEnabled( it->second.maDependsOnName ) )
1675         {
1676            aDependency = it->second.maDependsOnName;
1677            const css::beans::PropertyValue* pVal = getValue( aDependency );
1678            OSL_ENSURE( pVal, "unknown property in dependency" );
1679            if( pVal )
1680            {
1681                sal_Int32 nDepVal = 0;
1682                bool bDepVal = false;
1683                if( pVal->Value >>= nDepVal )
1684                {
1685                    if( it->second.mnDependsOnEntry != -1 )
1686                    {
1687                        setValue( aDependency, css::uno::makeAny( sal_Int32( it->second.mnDependsOnEntry ) ) );
1688                    }
1689                }
1690                else if( pVal->Value >>= bDepVal )
1691                {
1692                    setValue( aDependency, css::uno::makeAny( it->second.mnDependsOnEntry != 0 ) );
1693                }
1694                else
1695                {
1696                    // if the type does not match something is awry
1697                    OSL_FAIL( "strange type in control dependency" );
1698                }
1699            }
1700         }
1701     }
1702 
1703     return aDependency;
1704 }
1705 
createProgressDialog()1706 void PrinterController::createProgressDialog()
1707 {
1708     if (!mpImplData->mxProgress)
1709     {
1710         bool bShow = true;
1711         css::beans::PropertyValue* pMonitor = getValue( "MonitorVisible" );
1712         if( pMonitor )
1713             pMonitor->Value >>= bShow;
1714         else
1715         {
1716             const css::beans::PropertyValue* pVal = getValue( "IsApi" );
1717             if( pVal )
1718             {
1719                 bool bApi = false;
1720                 pVal->Value >>= bApi;
1721                 bShow = ! bApi;
1722             }
1723         }
1724 
1725         if( bShow && ! Application::IsHeadlessModeEnabled() )
1726         {
1727             mpImplData->mxProgress = std::make_shared<PrintProgressDialog>(getWindow(), getPageCountProtected());
1728             weld::DialogController::runAsync(mpImplData->mxProgress, [](sal_Int32 /*nResult*/){});
1729         }
1730     }
1731     else
1732     {
1733         mpImplData->mxProgress->response(RET_CANCEL);
1734         mpImplData->mxProgress.reset();
1735     }
1736 }
1737 
isProgressCanceled() const1738 bool PrinterController::isProgressCanceled() const
1739 {
1740     return mpImplData->mxProgress && mpImplData->mxProgress->isCanceled();
1741 }
1742 
setMultipage(const MultiPageSetup & i_rMPS)1743 void PrinterController::setMultipage( const MultiPageSetup& i_rMPS )
1744 {
1745     mpImplData->maMultiPage = i_rMPS;
1746 }
1747 
getMultipage() const1748 const PrinterController::MultiPageSetup& PrinterController::getMultipage() const
1749 {
1750     return mpImplData->maMultiPage;
1751 }
1752 
resetPaperToLastConfigured()1753 void PrinterController::resetPaperToLastConfigured()
1754 {
1755     mpImplData->resetPaperToLastConfigured();
1756 }
1757 
pushPropertiesToPrinter()1758 void PrinterController::pushPropertiesToPrinter()
1759 {
1760     sal_Int32 nCopyCount = 1;
1761     // set copycount and collate
1762     const css::beans::PropertyValue* pVal = getValue( "CopyCount" );
1763     if( pVal )
1764         pVal->Value >>= nCopyCount;
1765     bool bCollate = false;
1766     pVal = getValue( "Collate" );
1767     if( pVal )
1768         pVal->Value >>= bCollate;
1769     mpImplData->mxPrinter->SetCopyCount( static_cast<sal_uInt16>(nCopyCount), bCollate );
1770 
1771     pVal = getValue("SinglePrintJobs");
1772     bool bSinglePrintJobs = false;
1773     if (pVal)
1774         pVal->Value >>= bSinglePrintJobs;
1775     mpImplData->mxPrinter->SetSinglePrintJobs(bSinglePrintJobs);
1776 
1777     // duplex mode
1778     pVal = getValue( "DuplexMode" );
1779     if( pVal )
1780     {
1781         sal_Int16 nDuplex = css::view::DuplexMode::UNKNOWN;
1782         pVal->Value >>= nDuplex;
1783         switch( nDuplex )
1784         {
1785             case css::view::DuplexMode::OFF: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::Off ); break;
1786             case css::view::DuplexMode::LONGEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::LongEdge ); break;
1787             case css::view::DuplexMode::SHORTEDGE: mpImplData->mxPrinter->SetDuplexMode( DuplexMode::ShortEdge ); break;
1788         }
1789     }
1790 }
1791 
isShowDialogs() const1792 bool PrinterController::isShowDialogs() const
1793 {
1794     bool bApi = getBoolProperty( "IsApi", false );
1795     return ! bApi && ! Application::IsHeadlessModeEnabled();
1796 }
1797 
isDirectPrint() const1798 bool PrinterController::isDirectPrint() const
1799 {
1800     bool bDirect = getBoolProperty( "IsDirect", false );
1801     return bDirect;
1802 }
1803 
getBoolProperty(const OUString & i_rProperty,bool i_bFallback) const1804 bool PrinterController::getBoolProperty( const OUString& i_rProperty, bool i_bFallback ) const
1805 {
1806     bool bRet = i_bFallback;
1807     const css::beans::PropertyValue* pVal = getValue( i_rProperty );
1808     if( pVal )
1809         pVal->Value >>= bRet;
1810     return bRet;
1811 }
1812 
getIntProperty(const OUString & i_rProperty,sal_Int32 i_nFallback) const1813 sal_Int32 PrinterController::getIntProperty( const OUString& i_rProperty, sal_Int32 i_nFallback ) const
1814 {
1815     sal_Int32 nRet = i_nFallback;
1816     const css::beans::PropertyValue* pVal = getValue( i_rProperty );
1817     if( pVal )
1818         pVal->Value >>= nRet;
1819     return nRet;
1820 }
1821 
1822 /*
1823  * PrinterOptionsHelper
1824 **/
getValue(const OUString & i_rPropertyName) const1825 css::uno::Any PrinterOptionsHelper::getValue( const OUString& i_rPropertyName ) const
1826 {
1827     css::uno::Any aRet;
1828     std::unordered_map< OUString, css::uno::Any >::const_iterator it =
1829         m_aPropertyMap.find( i_rPropertyName );
1830     if( it != m_aPropertyMap.end() )
1831         aRet = it->second;
1832     return aRet;
1833 }
1834 
getBoolValue(const OUString & i_rPropertyName,bool i_bDefault) const1835 bool PrinterOptionsHelper::getBoolValue( const OUString& i_rPropertyName, bool i_bDefault ) const
1836 {
1837     bool bRet = false;
1838     css::uno::Any aVal( getValue( i_rPropertyName ) );
1839     return (aVal >>= bRet) ? bRet : i_bDefault;
1840 }
1841 
getIntValue(const OUString & i_rPropertyName,sal_Int64 i_nDefault) const1842 sal_Int64 PrinterOptionsHelper::getIntValue( const OUString& i_rPropertyName, sal_Int64 i_nDefault ) const
1843 {
1844     sal_Int64 nRet = 0;
1845     css::uno::Any aVal( getValue( i_rPropertyName ) );
1846     return (aVal >>= nRet) ? nRet : i_nDefault;
1847 }
1848 
getStringValue(const OUString & i_rPropertyName) const1849 OUString PrinterOptionsHelper::getStringValue( const OUString& i_rPropertyName ) const
1850 {
1851     OUString aRet;
1852     css::uno::Any aVal( getValue( i_rPropertyName ) );
1853     return (aVal >>= aRet) ? aRet : OUString();
1854 }
1855 
processProperties(const css::uno::Sequence<css::beans::PropertyValue> & i_rNewProp)1856 bool PrinterOptionsHelper::processProperties( const css::uno::Sequence< css::beans::PropertyValue >& i_rNewProp )
1857 {
1858     bool bChanged = false;
1859 
1860     for( const auto& rVal : i_rNewProp )
1861     {
1862         std::unordered_map< OUString, css::uno::Any >::iterator it =
1863             m_aPropertyMap.find( rVal.Name );
1864 
1865         bool bElementChanged = (it == m_aPropertyMap.end()) || (it->second != rVal.Value);
1866         if( bElementChanged )
1867         {
1868             m_aPropertyMap[ rVal.Name ] = rVal.Value;
1869             bChanged = true;
1870         }
1871     }
1872     return bChanged;
1873 }
1874 
appendPrintUIOptions(css::uno::Sequence<css::beans::PropertyValue> & io_rProps) const1875 void PrinterOptionsHelper::appendPrintUIOptions( css::uno::Sequence< css::beans::PropertyValue >& io_rProps ) const
1876 {
1877     if( !m_aUIProperties.empty() )
1878     {
1879         sal_Int32 nIndex = io_rProps.getLength();
1880         io_rProps.realloc( nIndex+1 );
1881         css::beans::PropertyValue aVal;
1882         aVal.Name = "ExtraPrintUIOptions";
1883         aVal.Value <<= comphelper::containerToSequence(m_aUIProperties);
1884         io_rProps[ nIndex ] = aVal;
1885     }
1886 }
1887 
setUIControlOpt(const css::uno::Sequence<OUString> & i_rIDs,const OUString & i_rTitle,const css::uno::Sequence<OUString> & i_rHelpIds,const OUString & i_rType,const css::beans::PropertyValue * i_pVal,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)1888 css::uno::Any PrinterOptionsHelper::setUIControlOpt(const css::uno::Sequence< OUString >& i_rIDs,
1889                                           const OUString& i_rTitle,
1890                                           const css::uno::Sequence< OUString >& i_rHelpIds,
1891                                           const OUString& i_rType,
1892                                           const css::beans::PropertyValue* i_pVal,
1893                                           const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
1894 {
1895     sal_Int32 nElements =
1896         2                                                             // ControlType + ID
1897         + (i_rTitle.isEmpty() ? 0 : 1)                                // Text
1898         + (i_rHelpIds.hasElements() ? 1 : 0)                          // HelpId
1899         + (i_pVal ? 1 : 0)                                            // Property
1900         + i_rControlOptions.maAddProps.size()                         // additional props
1901         + (i_rControlOptions.maGroupHint.isEmpty() ? 0 : 1)           // grouping
1902         + (i_rControlOptions.mbInternalOnly ? 1 : 0)                  // internal hint
1903         + (i_rControlOptions.mbEnabled ? 0 : 1)                       // enabled
1904         ;
1905     if( !i_rControlOptions.maDependsOnName.isEmpty() )
1906     {
1907         nElements += 1;
1908         if( i_rControlOptions.mnDependsOnEntry != -1 )
1909             nElements += 1;
1910         if( i_rControlOptions.mbAttachToDependency )
1911             nElements += 1;
1912     }
1913 
1914     css::uno::Sequence< css::beans::PropertyValue > aCtrl( nElements );
1915     sal_Int32 nUsed = 0;
1916     if( !i_rTitle.isEmpty() )
1917     {
1918         aCtrl[nUsed  ].Name  = "Text";
1919         aCtrl[nUsed++].Value <<= i_rTitle;
1920     }
1921     if( i_rHelpIds.hasElements() )
1922     {
1923         aCtrl[nUsed  ].Name = "HelpId";
1924         aCtrl[nUsed++].Value <<= i_rHelpIds;
1925     }
1926     aCtrl[nUsed  ].Name  = "ControlType";
1927     aCtrl[nUsed++].Value <<= i_rType;
1928     aCtrl[nUsed  ].Name  = "ID";
1929     aCtrl[nUsed++].Value <<= i_rIDs;
1930     if( i_pVal )
1931     {
1932         aCtrl[nUsed  ].Name  = "Property";
1933         aCtrl[nUsed++].Value <<= *i_pVal;
1934     }
1935     if( !i_rControlOptions.maDependsOnName.isEmpty() )
1936     {
1937         aCtrl[nUsed  ].Name  = "DependsOnName";
1938         aCtrl[nUsed++].Value <<= i_rControlOptions.maDependsOnName;
1939         if( i_rControlOptions.mnDependsOnEntry != -1 )
1940         {
1941             aCtrl[nUsed  ].Name  = "DependsOnEntry";
1942             aCtrl[nUsed++].Value <<= i_rControlOptions.mnDependsOnEntry;
1943         }
1944         if( i_rControlOptions.mbAttachToDependency )
1945         {
1946             aCtrl[nUsed  ].Name  = "AttachToDependency";
1947             aCtrl[nUsed++].Value <<= i_rControlOptions.mbAttachToDependency;
1948         }
1949     }
1950     if( !i_rControlOptions.maGroupHint.isEmpty() )
1951     {
1952         aCtrl[nUsed  ].Name    = "GroupingHint";
1953         aCtrl[nUsed++].Value <<= i_rControlOptions.maGroupHint;
1954     }
1955     if( i_rControlOptions.mbInternalOnly )
1956     {
1957         aCtrl[nUsed  ].Name    = "InternalUIOnly";
1958         aCtrl[nUsed++].Value <<= true;
1959     }
1960     if( ! i_rControlOptions.mbEnabled )
1961     {
1962         aCtrl[nUsed  ].Name    = "Enabled";
1963         aCtrl[nUsed++].Value <<= false;
1964     }
1965 
1966     sal_Int32 nAddProps = i_rControlOptions.maAddProps.size();
1967     for( sal_Int32 i = 0; i < nAddProps; i++ )
1968         aCtrl[ nUsed++ ] = i_rControlOptions.maAddProps[i];
1969 
1970     SAL_WARN_IF( nUsed != nElements, "vcl.gdi", "nUsed != nElements, probable heap corruption" );
1971 
1972     return css::uno::makeAny( aCtrl );
1973 }
1974 
setGroupControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId)1975 css::uno::Any PrinterOptionsHelper::setGroupControlOpt(const OUString& i_rID,
1976                                              const OUString& i_rTitle,
1977                                              const OUString& i_rHelpId)
1978 {
1979     css::uno::Sequence< OUString > aHelpId;
1980     if( !i_rHelpId.isEmpty() )
1981     {
1982         aHelpId.realloc( 1 );
1983         *aHelpId.getArray() = i_rHelpId;
1984     }
1985     css::uno::Sequence< OUString > aIds { i_rID };
1986     return setUIControlOpt(aIds, i_rTitle, aHelpId, "Group");
1987 }
1988 
setSubgroupControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)1989 css::uno::Any PrinterOptionsHelper::setSubgroupControlOpt(const OUString& i_rID,
1990                                                 const OUString& i_rTitle,
1991                                                 const OUString& i_rHelpId,
1992                                                 const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
1993 {
1994     css::uno::Sequence< OUString > aHelpId;
1995     if( !i_rHelpId.isEmpty() )
1996     {
1997         aHelpId.realloc( 1 );
1998         *aHelpId.getArray() = i_rHelpId;
1999     }
2000     css::uno::Sequence< OUString > aIds { i_rID };
2001     return setUIControlOpt(aIds, i_rTitle, aHelpId, "Subgroup", nullptr, i_rControlOptions);
2002 }
2003 
setBoolControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId,const OUString & i_rProperty,bool i_bValue,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)2004 css::uno::Any PrinterOptionsHelper::setBoolControlOpt(const OUString& i_rID,
2005                                             const OUString& i_rTitle,
2006                                             const OUString& i_rHelpId,
2007                                             const OUString& i_rProperty,
2008                                             bool i_bValue,
2009                                             const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2010 {
2011     css::uno::Sequence< OUString > aHelpId;
2012     if( !i_rHelpId.isEmpty() )
2013     {
2014         aHelpId.realloc( 1 );
2015         *aHelpId.getArray() = i_rHelpId;
2016     }
2017     css::beans::PropertyValue aVal;
2018     aVal.Name = i_rProperty;
2019     aVal.Value <<= i_bValue;
2020     css::uno::Sequence< OUString > aIds { i_rID };
2021     return setUIControlOpt(aIds, i_rTitle, aHelpId, "Bool", &aVal, i_rControlOptions);
2022 }
2023 
setChoiceRadiosControlOpt(const css::uno::Sequence<OUString> & i_rIDs,const OUString & i_rTitle,const css::uno::Sequence<OUString> & i_rHelpId,const OUString & i_rProperty,const css::uno::Sequence<OUString> & i_rChoices,sal_Int32 i_nValue,const css::uno::Sequence<sal_Bool> & i_rDisabledChoices,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)2024 css::uno::Any PrinterOptionsHelper::setChoiceRadiosControlOpt(const css::uno::Sequence< OUString >& i_rIDs,
2025                                               const OUString& i_rTitle,
2026                                               const css::uno::Sequence< OUString >& i_rHelpId,
2027                                               const OUString& i_rProperty,
2028                                               const css::uno::Sequence< OUString >& i_rChoices,
2029                                               sal_Int32 i_nValue,
2030                                               const css::uno::Sequence< sal_Bool >& i_rDisabledChoices,
2031                                               const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2032 {
2033     UIControlOptions aOpt( i_rControlOptions );
2034     sal_Int32 nUsed = aOpt.maAddProps.size();
2035     aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) );
2036     aOpt.maAddProps[nUsed].Name = "Choices";
2037     aOpt.maAddProps[nUsed].Value <<= i_rChoices;
2038     if( i_rDisabledChoices.hasElements() )
2039     {
2040         aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled";
2041         aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices;
2042     }
2043 
2044     css::beans::PropertyValue aVal;
2045     aVal.Name = i_rProperty;
2046     aVal.Value <<= i_nValue;
2047     return setUIControlOpt(i_rIDs, i_rTitle, i_rHelpId, "Radio", &aVal, aOpt);
2048 }
2049 
setChoiceListControlOpt(const OUString & i_rID,const OUString & i_rTitle,const css::uno::Sequence<OUString> & i_rHelpId,const OUString & i_rProperty,const css::uno::Sequence<OUString> & i_rChoices,sal_Int32 i_nValue,const css::uno::Sequence<sal_Bool> & i_rDisabledChoices,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)2050 css::uno::Any PrinterOptionsHelper::setChoiceListControlOpt(const OUString& i_rID,
2051                                               const OUString& i_rTitle,
2052                                               const css::uno::Sequence< OUString >& i_rHelpId,
2053                                               const OUString& i_rProperty,
2054                                               const css::uno::Sequence< OUString >& i_rChoices,
2055                                               sal_Int32 i_nValue,
2056                                               const css::uno::Sequence< sal_Bool >& i_rDisabledChoices,
2057                                               const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2058 {
2059     UIControlOptions aOpt( i_rControlOptions );
2060     sal_Int32 nUsed = aOpt.maAddProps.size();
2061     aOpt.maAddProps.resize( nUsed + 1 + (i_rDisabledChoices.hasElements() ? 1 : 0) );
2062     aOpt.maAddProps[nUsed].Name = "Choices";
2063     aOpt.maAddProps[nUsed].Value <<= i_rChoices;
2064     if( i_rDisabledChoices.hasElements() )
2065     {
2066         aOpt.maAddProps[nUsed+1].Name = "ChoicesDisabled";
2067         aOpt.maAddProps[nUsed+1].Value <<= i_rDisabledChoices;
2068     }
2069 
2070     css::beans::PropertyValue aVal;
2071     aVal.Name = i_rProperty;
2072     aVal.Value <<= i_nValue;
2073     css::uno::Sequence< OUString > aIds { i_rID };
2074     return setUIControlOpt(aIds, i_rTitle, i_rHelpId, "List", &aVal, aOpt);
2075 }
2076 
setRangeControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId,const OUString & i_rProperty,sal_Int32 i_nValue,sal_Int32 i_nMinValue,sal_Int32 i_nMaxValue,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)2077 css::uno::Any PrinterOptionsHelper::setRangeControlOpt(const OUString& i_rID,
2078                                              const OUString& i_rTitle,
2079                                              const OUString& i_rHelpId,
2080                                              const OUString& i_rProperty,
2081                                              sal_Int32 i_nValue,
2082                                              sal_Int32 i_nMinValue,
2083                                              sal_Int32 i_nMaxValue,
2084                                              const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2085 {
2086     UIControlOptions aOpt( i_rControlOptions );
2087     if( i_nMaxValue >= i_nMinValue )
2088     {
2089         sal_Int32 nUsed = aOpt.maAddProps.size();
2090         aOpt.maAddProps.resize( nUsed + 2 );
2091         aOpt.maAddProps[nUsed  ].Name  = "MinValue";
2092         aOpt.maAddProps[nUsed++].Value <<= i_nMinValue;
2093         aOpt.maAddProps[nUsed  ].Name  = "MaxValue";
2094         aOpt.maAddProps[nUsed++].Value <<= i_nMaxValue;
2095     }
2096 
2097     css::uno::Sequence< OUString > aHelpId;
2098     if( !i_rHelpId.isEmpty() )
2099     {
2100         aHelpId.realloc( 1 );
2101         *aHelpId.getArray() = i_rHelpId;
2102     }
2103     css::beans::PropertyValue aVal;
2104     aVal.Name = i_rProperty;
2105     aVal.Value <<= i_nValue;
2106     css::uno::Sequence< OUString > aIds { i_rID };
2107     return setUIControlOpt(aIds, i_rTitle, aHelpId, "Range", &aVal, aOpt);
2108 }
2109 
setEditControlOpt(const OUString & i_rID,const OUString & i_rTitle,const OUString & i_rHelpId,const OUString & i_rProperty,const OUString & i_rValue,const PrinterOptionsHelper::UIControlOptions & i_rControlOptions)2110 css::uno::Any PrinterOptionsHelper::setEditControlOpt(const OUString& i_rID,
2111                                             const OUString& i_rTitle,
2112                                             const OUString& i_rHelpId,
2113                                             const OUString& i_rProperty,
2114                                             const OUString& i_rValue,
2115                                             const PrinterOptionsHelper::UIControlOptions& i_rControlOptions)
2116 {
2117     css::uno::Sequence< OUString > aHelpId;
2118     if( !i_rHelpId.isEmpty() )
2119     {
2120         aHelpId.realloc( 1 );
2121         *aHelpId.getArray() = i_rHelpId;
2122     }
2123     css::beans::PropertyValue aVal;
2124     aVal.Name = i_rProperty;
2125     aVal.Value <<= i_rValue;
2126     css::uno::Sequence< OUString > aIds { i_rID };
2127     return setUIControlOpt(aIds, i_rTitle, aHelpId, "Edit", &aVal, i_rControlOptions);
2128 }
2129 
2130 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2131