1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <officecfg/Office/Common.hxx>
22 #include <vcl/gdimtf.hxx>
23 #include <vcl/print.hxx>
24 #include <sal/macros.h>
25 #include <osl/diagnose.h>
27 #include <osx/salinst.h>
28 #include <osx/salprn.h>
29 #include <osx/printview.h>
30 #include <quartz/salgdi.h>
31 #include <osx/saldata.hxx>
32 #include <quartz/utils.h>
35 #include <salptype.hxx>
37 #include <com/sun/star/beans/PropertyValue.hpp>
38 #include <com/sun/star/awt/Size.hpp>
39 #include <com/sun/star/uno/Sequence.hxx>
45 using namespace com::sun::star
;
46 using namespace com::sun::star::beans
;
48 AquaSalInfoPrinter::AquaSalInfoPrinter( const SalPrinterQueueInfo
& i_rQueue
) :
49 mpGraphics( nullptr ),
54 mePageOrientation( Orientation::Portrait
),
55 mnStartPageOffsetX( 0 ),
56 mnStartPageOffsetY( 0 ),
57 mnCurPageRangeStart( 0 ),
58 mnCurPageRangeCount( 0 )
60 NSString
* pStr
= CreateNSString( i_rQueue
.maPrinterName
);
61 mpPrinter
= [NSPrinter printerWithName
: pStr
];
64 NSPrintInfo
* pShared
= [NSPrintInfo sharedPrintInfo
];
67 mpPrintInfo
= [pShared copy
];
68 [mpPrintInfo setPrinter
: mpPrinter
];
69 mePageOrientation
= ([mpPrintInfo orientation
] == NSPaperOrientationLandscape
) ? Orientation::Landscape
: Orientation::Portrait
;
70 [mpPrintInfo setOrientation
: NSPaperOrientationPortrait
];
73 mpGraphics
= new AquaSalGraphics();
75 const int nWidth
= 100, nHeight
= 100;
76 mpContextMemory
.reset(new (std::nothrow
) sal_uInt8
[nWidth
* 4 * nHeight
]);
80 mrContext
= CGBitmapContextCreate(mpContextMemory
.get(),
81 nWidth
, nHeight
, 8, nWidth
* 4,
82 GetSalData()->mxRGBSpace
, kCGImageAlphaNoneSkipFirst
);
84 SetupPrinterGraphics( mrContext
);
88 AquaSalInfoPrinter::~AquaSalInfoPrinter()
92 [mpPrintInfo release
];
94 CFRelease( mrContext
);
97 void AquaSalInfoPrinter::SetupPrinterGraphics( CGContextRef i_rContext
) const
103 // FIXME: get printer resolution
104 long nDPIX
= 720, nDPIY
= 720;
105 NSSize aPaperSize
= [mpPrintInfo paperSize
];
107 NSRect aImageRect
= [mpPrintInfo imageablePageBounds
];
108 if( mePageOrientation
== Orientation::Portrait
)
110 // move mirrored CTM back into paper
111 double dX
= 0, dY
= aPaperSize
.height
;
112 // move CTM to reflect imageable area
113 dX
+= aImageRect
.origin
.x
;
114 dY
-= aPaperSize
.height
- aImageRect
.size
.height
- aImageRect
.origin
.y
;
115 CGContextTranslateCTM( i_rContext
, dX
+ mnStartPageOffsetX
, dY
- mnStartPageOffsetY
);
116 // scale to be top/down and reflect our "virtual" DPI
117 CGContextScaleCTM( i_rContext
, 72.0/double(nDPIX
), -(72.0/double(nDPIY
)) );
121 // move CTM to reflect imageable area
122 double dX
= aImageRect
.origin
.x
, dY
= aPaperSize
.height
- aImageRect
.size
.height
- aImageRect
.origin
.y
;
123 CGContextTranslateCTM( i_rContext
, -dX
, -dY
);
125 CGContextRotateCTM( i_rContext
, M_PI
/2 );
126 // move turned CTM back into paper
127 dX
= aPaperSize
.height
;
128 dY
= -aPaperSize
.width
;
129 CGContextTranslateCTM( i_rContext
, dX
+ mnStartPageOffsetY
, dY
- mnStartPageOffsetX
);
130 // scale to be top/down and reflect our "virtual" DPI
131 CGContextScaleCTM( i_rContext
, -(72.0/double(nDPIY
)), (72.0/double(nDPIX
)) );
133 mpGraphics
->SetPrinterGraphics( i_rContext
, nDPIX
, nDPIY
);
136 OSL_FAIL( "no print info in SetupPrinterGraphics" );
140 SalGraphics
* AquaSalInfoPrinter::AcquireGraphics()
142 SalGraphics
* pGraphics
= mbGraphics
? nullptr : mpGraphics
;
147 void AquaSalInfoPrinter::ReleaseGraphics( SalGraphics
* )
152 bool AquaSalInfoPrinter::Setup( weld::Window
*, ImplJobSetup
* )
157 bool AquaSalInfoPrinter::SetPrinterData( ImplJobSetup
* io_pSetupData
)
159 // FIXME: implement driver data
160 if( io_pSetupData
&& io_pSetupData
->GetDriverData() )
161 return SetData( JobSetFlags::ALL
, io_pSetupData
);
163 bool bSuccess
= true;
166 io_pSetupData
->SetSystem( JOBSETUP_SYSTEM_MAC
);
171 NSSize aPaperSize
= [mpPrintInfo paperSize
];
172 double width
= aPaperSize
.width
, height
= aPaperSize
.height
;
174 PaperInfo
aInfo( PtTo10Mu( width
), PtTo10Mu( height
) );
176 io_pSetupData
->SetPaperFormat( aInfo
.getPaper() );
177 if( io_pSetupData
->GetPaperFormat() == PAPER_USER
)
179 io_pSetupData
->SetPaperWidth( PtTo10Mu( width
) );
180 io_pSetupData
->SetPaperHeight( PtTo10Mu( height
) );
184 io_pSetupData
->SetPaperWidth( 0 );
185 io_pSetupData
->SetPaperHeight( 0 );
189 io_pSetupData
->SetOrientation( mePageOrientation
);
191 io_pSetupData
->SetPaperBin( 0 );
192 io_pSetupData
->SetDriverData( static_cast<sal_uInt8
*>(std::malloc( 4 )) );
193 io_pSetupData
->SetDriverDataLen( 4 );
201 void AquaSalInfoPrinter::setPaperSize( long i_nWidth
, long i_nHeight
, Orientation i_eSetOrientation
)
204 Orientation ePaperOrientation
= Orientation::Portrait
;
205 const PaperInfo
* pPaper
= matchPaper( i_nWidth
, i_nHeight
, ePaperOrientation
);
209 NSString
* pPaperName
= [CreateNSString( OStringToOUString(PaperInfo::toPSName(pPaper
->getPaper()), RTL_TEXTENCODING_ASCII_US
) ) autorelease
];
210 [mpPrintInfo setPaperName
: pPaperName
];
212 else if( i_nWidth
> 0 && i_nHeight
> 0 )
214 NSSize aPaperSize
= { static_cast<CGFloat
>(TenMuToPt(i_nWidth
)), static_cast<CGFloat
>(TenMuToPt(i_nHeight
)) };
215 [mpPrintInfo setPaperSize
: aPaperSize
];
217 // this seems counterintuitive
218 mePageOrientation
= i_eSetOrientation
;
221 bool AquaSalInfoPrinter::SetData( JobSetFlags i_nFlags
, ImplJobSetup
* io_pSetupData
)
223 if( ! io_pSetupData
|| io_pSetupData
->GetSystem() != JOBSETUP_SYSTEM_MAC
)
228 if( i_nFlags
& JobSetFlags::ORIENTATION
)
229 mePageOrientation
= io_pSetupData
->GetOrientation();
231 if( i_nFlags
& JobSetFlags::PAPERSIZE
)
234 long width
= 21000, height
= 29700;
235 if( io_pSetupData
->GetPaperFormat() == PAPER_USER
)
237 // #i101108# sanity check
238 if( io_pSetupData
->GetPaperWidth() && io_pSetupData
->GetPaperHeight() )
240 width
= io_pSetupData
->GetPaperWidth();
241 height
= io_pSetupData
->GetPaperHeight();
246 PaperInfo
aInfo( io_pSetupData
->GetPaperFormat() );
247 width
= aInfo
.getWidth();
248 height
= aInfo
.getHeight();
251 setPaperSize( width
, height
, mePageOrientation
);
255 return mpPrintInfo
!= nil
;
258 sal_uInt16
AquaSalInfoPrinter::GetPaperBinCount( const ImplJobSetup
* )
263 OUString
AquaSalInfoPrinter::GetPaperBinName( const ImplJobSetup
*, sal_uInt16
)
268 sal_uInt32
AquaSalInfoPrinter::GetCapabilities( const ImplJobSetup
*, PrinterCapType i_nType
)
272 case PrinterCapType::SupportDialog
:
274 case PrinterCapType::Copies
:
276 case PrinterCapType::CollateCopies
:
278 case PrinterCapType::SetOrientation
:
280 case PrinterCapType::SetPaperSize
:
282 case PrinterCapType::SetPaper
:
284 case PrinterCapType::ExternalDialog
:
285 return officecfg::Office::Common::Misc::UseSystemPrintDialog::get()
287 case PrinterCapType::PDF
:
289 case PrinterCapType::UsePullModel
:
296 void AquaSalInfoPrinter::GetPageInfo( const ImplJobSetup
*,
297 long& o_rOutWidth
, long& o_rOutHeight
,
303 sal_Int32 nDPIX
= 72, nDPIY
= 72;
304 mpGraphics
->GetResolution( nDPIX
, nDPIY
);
305 const double fXScaling
= static_cast<double>(nDPIX
)/72.0,
306 fYScaling
= static_cast<double>(nDPIY
)/72.0;
308 NSSize aPaperSize
= [mpPrintInfo paperSize
];
309 rPaperSize
.setWidth( static_cast<long>( double(aPaperSize
.width
) * fXScaling
) );
310 rPaperSize
.setHeight( static_cast<long>( double(aPaperSize
.height
) * fYScaling
) );
312 NSRect aImageRect
= [mpPrintInfo imageablePageBounds
];
313 rPageOffset
.setX( static_cast<long>( aImageRect
.origin
.x
* fXScaling
) );
314 rPageOffset
.setY( static_cast<long>( (aPaperSize
.height
- aImageRect
.size
.height
- aImageRect
.origin
.y
) * fYScaling
) );
315 o_rOutWidth
= static_cast<long>( aImageRect
.size
.width
* fXScaling
);
316 o_rOutHeight
= static_cast<long>( aImageRect
.size
.height
* fYScaling
);
318 if( mePageOrientation
== Orientation::Landscape
)
320 std::swap( o_rOutWidth
, o_rOutHeight
);
321 // swap width and height
322 long n
= rPaperSize
.Width();
323 rPaperSize
.setWidth(rPaperSize
.Height());
324 rPaperSize
.setHeight(n
);
325 // swap offset x and y
327 rPageOffset
.setX(rPageOffset
.Y());
333 static Size
getPageSize( vcl::PrinterController
const & i_rController
, sal_Int32 i_nPage
)
336 uno::Sequence
< PropertyValue
> aPageParms( i_rController
.getPageParameters( i_nPage
) );
337 for( sal_Int32 nProperty
= 0, nPropertyCount
= aPageParms
.getLength(); nProperty
< nPropertyCount
; ++nProperty
)
339 if ( aPageParms
[ nProperty
].Name
== "PageSize" )
342 aPageParms
[ nProperty
].Value
>>= aSize
;
343 aPageSize
.setWidth( aSize
.Width
);
344 aPageSize
.setHeight( aSize
.Height
);
351 bool AquaSalInfoPrinter::StartJob( const OUString
* i_pFileName
,
352 const OUString
& i_rJobName
,
353 const OUString
& /*i_rAppName*/,
354 ImplJobSetup
* i_pSetupData
,
355 vcl::PrinterController
& i_rController
361 bool bSuccess
= false;
362 bool bWasAborted
= false;
363 AquaSalInstance
* pInst
= GetSalData()->mpInstance
;
364 PrintAccessoryViewState aAccViewState
;
365 sal_Int32 nAllPages
= 0;
368 i_rController
.setLastPage( false );
372 SetData( JobSetFlags::ALL
, i_pSetupData
);
374 // do we want a progress panel ?
375 bool bShowProgressPanel
= true;
376 beans::PropertyValue
* pMonitor
= i_rController
.getValue( OUString( "MonitorVisible" ) );
378 pMonitor
->Value
>>= bShowProgressPanel
;
379 if( ! i_rController
.isShowDialogs() )
380 bShowProgressPanel
= false;
382 // possibly create one job for collated output
383 bool bSinglePrintJobs
= false;
384 beans::PropertyValue
* pSingleValue
= i_rController
.getValue( OUString( "PrintCollateAsSingleJobs" ) );
387 pSingleValue
->Value
>>= bSinglePrintJobs
;
390 // FIXME: jobStarted() should be done after the print dialog has ended (if there is one)
391 // how do I know when that might be ?
392 i_rController
.jobStarted();
394 int nCopies
= i_rController
.getPrinter()->GetCopyCount();
396 if( bSinglePrintJobs
)
402 for( int nCurJob
= 0; nCurJob
< nJobs
; nCurJob
++ )
404 aAccViewState
.bNeedRestart
= true;
407 if( aAccViewState
.bNeedRestart
)
409 mnCurPageRangeStart
= 0;
410 mnCurPageRangeCount
= 0;
411 nAllPages
= i_rController
.getFilteredPageCount();
414 aAccViewState
.bNeedRestart
= false;
416 Size
aCurSize( 21000, 29700 );
419 mnCurPageRangeCount
= 1;
420 aCurSize
= getPageSize( i_rController
, mnCurPageRangeStart
);
421 Size
aNextSize( aCurSize
);
423 // print pages up to a different size
424 while( mnCurPageRangeCount
+ mnCurPageRangeStart
< nAllPages
)
426 aNextSize
= getPageSize( i_rController
, mnCurPageRangeStart
+ mnCurPageRangeCount
);
427 if( aCurSize
== aNextSize
// same page size
429 (aCurSize
.Width() == aNextSize
.Height() && aCurSize
.Height() == aNextSize
.Width()) // same size, but different orientation
432 mnCurPageRangeCount
++;
439 mnCurPageRangeCount
= 0;
441 // now for the current run
442 mnStartPageOffsetX
= mnStartPageOffsetY
= 0;
443 // setup the paper size and orientation
444 // do this on our associated Printer object, since that is
445 // out interface to the applications which occasionally rely on the paper
446 // information (e.g. brochure printing scales to the found paper size)
447 // also SetPaperSizeUser has the advantage that we can share a
448 // platform independent paper matching algorithm
449 VclPtr
<Printer
> pPrinter( i_rController
.getPrinter() );
450 pPrinter
->SetMapMode( MapMode( MapUnit::Map100thMM
) );
451 pPrinter
->SetPaperSizeUser( aCurSize
, true );
454 NSView
* pPrintView
= [[AquaPrintView alloc
] initWithController
: &i_rController withInfoPrinter
: this];
456 NSMutableDictionary
* pPrintDict
= [mpPrintInfo dictionary
];
461 [mpPrintInfo setJobDisposition
: NSPrintSaveJob
];
462 NSString
* pPath
= CreateNSString( *i_pFileName
);
463 [pPrintDict setObject
:[NSURL fileURLWithPath
:pPath
] forKey
:NSPrintJobSavingURL
];
467 [pPrintDict setObject
: [[NSNumber numberWithInt
: nCopies
] autorelease
] forKey
: NSPrintCopies
];
469 [pPrintDict setObject
: [[NSNumber numberWithBool
: pPrinter
->IsCollateCopy()] autorelease
] forKey
: NSPrintMustCollate
];
470 [pPrintDict setObject
: [[NSNumber numberWithBool
: YES
] autorelease
] forKey
: NSPrintDetailedErrorReporting
];
471 [pPrintDict setObject
: [[NSNumber numberWithInt
: 1] autorelease
] forKey
: NSPrintFirstPage
];
472 // #i103253# weird: for some reason, autoreleasing the value below like the others above
473 // leads do a double free malloc error. Why this value should behave differently from all the others
475 [pPrintDict setObject
: [NSNumber numberWithInt
: mnCurPageRangeCount
] forKey
: NSPrintLastPage
];
477 // create print operation
478 NSPrintOperation
* pPrintOperation
= [NSPrintOperation printOperationWithView
: pPrintView printInfo
: mpPrintInfo
];
480 if( pPrintOperation
)
482 NSObject
* pReleaseAfterUse
= nil
;
483 bool bShowPanel
= !i_rController
.isDirectPrint()
484 && (officecfg::Office::Common::Misc::UseSystemPrintDialog::
486 && i_rController
.isShowDialogs();
487 [pPrintOperation setShowsPrintPanel
: bShowPanel
? YES
: NO
];
488 [pPrintOperation setShowsProgressPanel
: bShowProgressPanel
? YES
: NO
];
490 // set job title (since MacOSX 10.5)
491 if( [pPrintOperation respondsToSelector
: @
selector(setJobTitle
:)] )
492 [pPrintOperation performSelector
: @
selector(setJobTitle
:) withObject
: [CreateNSString( i_rJobName
) autorelease
]];
494 if( bShowPanel
&& mnCurPageRangeStart
== 0 && nCurJob
== 0) // only the first range of pages (in the first job) gets the accessory view
495 pReleaseAfterUse
= [AquaPrintAccessoryView setupPrinterPanel
: pPrintOperation withController
: &i_rController withState
: &aAccViewState
];
499 pInst
->startedPrintJob();
500 BOOL wasSuccessful
= [pPrintOperation runOperation
];
501 pInst
->endedPrintJob();
502 bSuccess
= wasSuccessful
;
503 bWasAborted
= [[[pPrintOperation printInfo
] jobDisposition
] compare
: NSPrintCancelJob
] == NSOrderedSame
;
505 if( pReleaseAfterUse
)
506 [pReleaseAfterUse release
];
509 mnCurPageRangeStart
+= mnCurPageRangeCount
;
510 mnCurPageRangeCount
= 1;
511 } while( aAccViewState
.bNeedRestart
|| mnCurPageRangeStart
+ mnCurPageRangeCount
< nAllPages
);
514 // inform application that it can release its data
515 // this is awkward, but the XRenderable interface has no method for this,
516 // so we need to call XRenderable::render one last time with IsLastPage = true
517 i_rController
.setLastPage( true );
518 GDIMetaFile aPageFile
;
520 SetupPrinterGraphics( mrContext
);
521 i_rController
.getFilteredPageFile( 0, aPageFile
);
523 i_rController
.setJobState( bWasAborted
524 ? view::PrintableState_JOB_ABORTED
525 : view::PrintableState_JOB_SPOOLED
);
527 mnCurPageRangeStart
= mnCurPageRangeCount
= 0;
532 bool AquaSalInfoPrinter::EndJob()
534 mnStartPageOffsetX
= mnStartPageOffsetY
= 0;
539 bool AquaSalInfoPrinter::AbortJob()
543 // FIXME: implementation
547 SalGraphics
* AquaSalInfoPrinter::StartPage( ImplJobSetup
* i_pSetupData
, bool i_bNewJobData
)
549 if( i_bNewJobData
&& i_pSetupData
)
550 SetPrinterData( i_pSetupData
);
552 CGContextRef rContext
= [[NSGraphicsContext currentContext
] CGContext
];
554 SetupPrinterGraphics( rContext
);
559 bool AquaSalInfoPrinter::EndPage()
561 mpGraphics
->InvalidateContext();
565 AquaSalPrinter::AquaSalPrinter( AquaSalInfoPrinter
* i_pInfoPrinter
) :
566 mpInfoPrinter( i_pInfoPrinter
)
570 AquaSalPrinter::~AquaSalPrinter()
574 bool AquaSalPrinter::StartJob( const OUString
* i_pFileName
,
575 const OUString
& i_rJobName
,
576 const OUString
& i_rAppName
,
577 ImplJobSetup
* i_pSetupData
,
578 vcl::PrinterController
& i_rController
)
580 return mpInfoPrinter
->StartJob( i_pFileName
, i_rJobName
, i_rAppName
, i_pSetupData
, i_rController
);
583 bool AquaSalPrinter::StartJob( const OUString
* /*i_pFileName*/,
584 const OUString
& /*i_rJobName*/,
585 const OUString
& /*i_rAppName*/,
586 sal_uInt32
/*i_nCopies*/,
591 OSL_FAIL( "should never be called" );
595 bool AquaSalPrinter::EndJob()
597 return mpInfoPrinter
->EndJob();
600 SalGraphics
* AquaSalPrinter::StartPage( ImplJobSetup
* i_pSetupData
, bool i_bNewJobData
)
602 return mpInfoPrinter
->StartPage( i_pSetupData
, i_bNewJobData
);
605 void AquaSalPrinter::EndPage()
607 mpInfoPrinter
->EndPage();
610 void AquaSalInfoPrinter::InitPaperFormats( const ImplJobSetup
* )
612 m_aPaperFormats
.clear();
613 m_bPapersInit
= true;
617 SAL_WNODEPRECATED_DECLARATIONS_PUSH
618 //TODO: 10.9 statusForTable:, stringListForKey:inTable:
619 if( [mpPrinter statusForTable
: @
"PPD"] == NSPrinterTableOK
)
621 NSArray
* pPaperNames
= [mpPrinter stringListForKey
: @
"PageSize" inTable
: @
"PPD"];
624 unsigned int nPapers
= [pPaperNames count
];
625 for( unsigned int i
= 0; i
< nPapers
; i
++ )
627 NSString
* pPaper
= [pPaperNames objectAtIndex
: i
];
628 // first try to match the name
629 OString
aPaperName( [pPaper UTF8String
] );
630 Paper ePaper
= PaperInfo::fromPSName( aPaperName
);
631 if( ePaper
!= PAPER_USER
)
633 m_aPaperFormats
.push_back( PaperInfo( ePaper
) );
637 NSSize aPaperSize
= [mpPrinter pageSizeForPaper
: pPaper
];
638 if( aPaperSize
.width
> 0 && aPaperSize
.height
> 0 )
640 PaperInfo
aInfo( PtTo10Mu( aPaperSize
.width
),
641 PtTo10Mu( aPaperSize
.height
) );
642 if( aInfo
.getPaper() == PAPER_USER
)
644 m_aPaperFormats
.push_back( aInfo
);
650 SAL_WNODEPRECATED_DECLARATIONS_POP
654 const PaperInfo
* AquaSalInfoPrinter::matchPaper( long i_nWidth
, long i_nHeight
, Orientation
& o_rOrientation
) const
656 if( ! m_bPapersInit
)
657 const_cast<AquaSalInfoPrinter
*>(this)->InitPaperFormats( nullptr );
659 const PaperInfo
* pMatch
= nullptr;
660 o_rOrientation
= Orientation::Portrait
;
661 for( int n
= 0; n
< 2 ; n
++ )
663 for( size_t i
= 0; i
< m_aPaperFormats
.size(); i
++ )
665 if( std::abs( m_aPaperFormats
[i
].getWidth() - i_nWidth
) < 50 &&
666 std::abs( m_aPaperFormats
[i
].getHeight() - i_nHeight
) < 50 )
668 pMatch
= &m_aPaperFormats
[i
];
672 o_rOrientation
= Orientation::Landscape
;
673 std::swap( i_nWidth
, i_nHeight
);
678 int AquaSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup
* )
683 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */