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 .
21 this file implements the sal printer interface ( SalPrinter, SalInfoPrinter
22 and some printer relevant methods of SalInstance and SalGraphicsData )
24 as aunderlying library the printer features of psprint are used.
26 The query methods of a SalInfoPrinter are implemented by querying psprint
28 The job methods of a SalPrinter are implemented by calling psprint
29 printer job functions.
32 // For spawning PDF and FAX generation
35 # include <sys/wait.h>
36 # include <sys/stat.h>
39 #include "rtl/ustring.hxx"
41 #include "vcl/button.hxx"
42 #include "vcl/dialog.hxx"
43 #include "vcl/edit.hxx"
44 #include "vcl/fixed.hxx"
45 #include "vcl/idle.hxx"
46 #include "vcl/svapp.hxx"
47 #include "vcl/print.hxx"
48 #include "vcl/pdfwriter.hxx"
49 #include "vcl/printerinfomanager.hxx"
50 #include "vcl/settings.hxx"
52 #include "saldatabasic.hxx"
53 #include "generic/genprn.h"
54 #include "generic/geninst.h"
55 #include "generic/genpspgraphics.h"
59 #include "prtsetup.hxx"
60 #include "salptype.hxx"
62 #include <com/sun/star/beans/PropertyValue.hpp>
65 using namespace com::sun::star
;
70 static OUString
getPdfDir( const PrinterInfo
& rInfo
)
76 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
77 if( aToken
.startsWith( "pdf=" ) )
80 aDir
= aToken
.getToken( 1, '=', nPos
);
81 if( aDir
.isEmpty() && getenv( "HOME" ) )
82 aDir
= OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
91 class QueryString
: public ModalDialog
94 VclPtr
<OKButton
> m_pOKButton
;
95 VclPtr
<FixedText
> m_pFixedText
;
97 OUString
& m_rReturnValue
;
99 DECL_LINK( ClickBtnHdl
, Button
* );
102 // parent window, Query text, initial value
103 QueryString(vcl::Window
*, OUString
&, OUString
&);
104 virtual ~QueryString() { disposeOnce(); }
105 virtual void dispose() SAL_OVERRIDE
108 m_pFixedText
.clear();
110 ModalDialog::dispose();
117 QueryString::QueryString(vcl::Window
* pParent
, OUString
& rQuery
, OUString
& rRet
)
118 : ModalDialog(pParent
, "QueryDialog",
119 "vcl/ui/querydialog.ui" )
120 , m_rReturnValue( rRet
)
122 get(m_pOKButton
, "ok");
123 get(m_pFixedText
, "label");
124 get(m_pEdit
, "entry");
126 m_pOKButton
->SetClickHdl(LINK(this, QueryString
, ClickBtnHdl
));
127 m_pFixedText
->SetText(rQuery
);
128 m_pEdit
->SetText(m_rReturnValue
);
132 IMPL_LINK( QueryString
, ClickBtnHdl
, Button
*, pButton
)
134 if (pButton
== m_pOKButton
)
136 m_rReturnValue
= m_pEdit
->GetText();
144 int QueryFaxNumber(OUString
& rNumber
)
146 OUString
aTmpString(VclResId(SV_PRINT_QUERYFAXNUMBER_TXT
));
147 ScopedVclPtrInstance
< QueryString
> aQuery( nullptr, aTmpString
, rNumber
);
148 return aQuery
->Execute();
152 inline int PtTo10Mu( int nPoints
) { return (int)((((double)nPoints
)*35.27777778)+0.5); }
154 inline int TenMuToPt( int nUnits
) { return (int)((((double)nUnits
)/35.27777778)+0.5); }
156 static void copyJobDataToJobSetup( ImplJobSetup
* pJobSetup
, JobData
& rData
)
158 pJobSetup
->meOrientation
= (Orientation
)(rData
.m_eOrientation
== orientation::Landscape
? ORIENTATION_LANDSCAPE
: ORIENTATION_PORTRAIT
);
164 rData
.m_aContext
.getPageSize( aPaper
, width
, height
);
165 pJobSetup
->mePaperFormat
= PaperInfo::fromPSName(OUStringToOString( aPaper
, RTL_TEXTENCODING_ISO_8859_1
));
167 pJobSetup
->mnPaperWidth
= 0;
168 pJobSetup
->mnPaperHeight
= 0;
169 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
171 // transform to 100dth mm
172 width
= PtTo10Mu( width
);
173 height
= PtTo10Mu( height
);
175 if( rData
.m_eOrientation
== psp::orientation::Portrait
)
177 pJobSetup
->mnPaperWidth
= width
;
178 pJobSetup
->mnPaperHeight
= height
;
182 pJobSetup
->mnPaperWidth
= height
;
183 pJobSetup
->mnPaperHeight
= width
;
188 const PPDKey
* pKey
= NULL
;
189 const PPDValue
* pValue
= NULL
;
191 pJobSetup
->mnPaperBin
= 0;
192 if( rData
.m_pParser
)
193 pKey
= rData
.m_pParser
->getKey( OUString("InputSlot") );
195 pValue
= rData
.m_aContext
.getValue( pKey
);
198 for( pJobSetup
->mnPaperBin
= 0;
199 pValue
!= pKey
->getValue( pJobSetup
->mnPaperBin
) &&
200 pJobSetup
->mnPaperBin
< pKey
->countValues();
201 pJobSetup
->mnPaperBin
++ )
203 if( pJobSetup
->mnPaperBin
>= pKey
->countValues() )
204 pJobSetup
->mnPaperBin
= 0;
211 pJobSetup
->meDuplexMode
= DUPLEX_UNKNOWN
;
212 if( rData
.m_pParser
)
213 pKey
= rData
.m_pParser
->getKey( OUString("Duplex") );
215 pValue
= rData
.m_aContext
.getValue( pKey
);
218 if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "None" ) ||
219 pValue
->m_aOption
.startsWithIgnoreAsciiCase( "Simplex" )
222 pJobSetup
->meDuplexMode
= DUPLEX_OFF
;
224 else if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
226 pJobSetup
->meDuplexMode
= DUPLEX_LONGEDGE
;
228 else if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "DuplexTumble" ) )
230 pJobSetup
->meDuplexMode
= DUPLEX_SHORTEDGE
;
234 // copy the whole context
235 if( pJobSetup
->mpDriverData
)
236 rtl_freeMemory( pJobSetup
->mpDriverData
);
239 void* pBuffer
= NULL
;
240 if( rData
.getStreamBuffer( pBuffer
, nBytes
) )
242 pJobSetup
->mnDriverDataLen
= nBytes
;
243 pJobSetup
->mpDriverData
= static_cast<sal_uInt8
*>(pBuffer
);
247 pJobSetup
->mnDriverDataLen
= 0;
248 pJobSetup
->mpDriverData
= NULL
;
252 // Needs a cleaner abstraction ...
254 static bool passFileToCommandLine( const OUString
& rFilename
, const OUString
& rCommandLine
, bool bRemoveFile
= true )
256 bool bSuccess
= false;
258 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
259 OString
aCmdLine(OUStringToOString(rCommandLine
, aEncoding
));
260 OString
aFilename(OUStringToOString(rFilename
, aEncoding
));
262 bool bPipe
= aCmdLine
.indexOf( "(TMP)" ) == -1;
264 // setup command line for exec
266 aCmdLine
= aCmdLine
.replaceAll(OString("(TMP)"), aFilename
);
268 #if OSL_DEBUG_LEVEL > 1
269 fprintf( stderr
, "%s commandline: \"%s\"\n",
270 bPipe
? "piping to" : "executing",
273 if( stat( aFilename
.getStr(), &aStat
) )
274 fprintf( stderr
, "stat( %s ) failed\n", aFilename
.getStr() );
275 fprintf( stderr
, "Tmp file %s has modes: 0%03lo\n", aFilename
.getStr(), (long)aStat
.st_mode
);
278 if( ! ( argv
[ 0 ] = getenv( "SHELL" ) ) )
279 argv
[ 0 ] = "/bin/sh";
281 argv
[ 2 ] = aCmdLine
.getStr();
284 bool bHavePipes
= false;
288 bHavePipes
= pipe( fd
) == 0;
289 if( ( pid
= fork() ) > 0 )
291 if( bPipe
&& bHavePipes
)
294 char aBuffer
[ 2048 ];
295 FILE* fp
= fopen( aFilename
.getStr(), "r" );
296 while (fp
&& !feof(fp
))
298 size_t nBytesRead
= fread(aBuffer
, 1, sizeof( aBuffer
), fp
);
301 size_t nBytesWritten
= write(fd
[1], aBuffer
, nBytesRead
);
302 OSL_ENSURE(nBytesWritten
== nBytesRead
, "short write");
303 if (nBytesWritten
!= nBytesRead
)
311 if(waitpid( pid
, &status
, 0 ) != -1)
319 if( bPipe
&& bHavePipes
)
322 if( fd
[0] != STDIN_FILENO
) // not probable, but who knows :)
323 dup2( fd
[0], STDIN_FILENO
);
325 execv( argv
[0], const_cast<char**>(argv
) );
326 fprintf( stderr
, "failed to execute \"%s\"\n", aCmdLine
.getStr() );
330 fprintf( stderr
, "failed to fork\n" );
334 unlink( aFilename
.getStr() );
340 static std::vector
<OUString
> getFaxNumbers()
342 std::vector
<OUString
> aFaxNumbers
;
345 if (QueryFaxNumber(aNewNr
))
347 sal_Int32 nIndex
= 0;
350 OUString sToken
= aNewNr
.getToken( 0, ';', nIndex
);
351 aFaxNumbers
.push_back(sToken
);
359 static bool createPdf( const OUString
& rToFile
, const OUString
& rFromFile
, const OUString
& rCommandLine
)
362 OUString
aCommandLine(
363 rCommandLine
.replaceAll("(OUTFILE)", rToFile
));
365 return passFileToCommandLine( rFromFile
, aCommandLine
);
367 (void)rToFile
; (void)rFromFile
; (void)rCommandLine
;
376 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter
*pPrinter
,
377 SalPrinterQueueInfo
* pQueueInfo
, ImplJobSetup
* pJobSetup
)
381 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
382 PrinterInfo
aInfo( rManager
.getPrinterInfo( pQueueInfo
->maPrinterName
) );
383 pPrinter
->m_aJobData
= aInfo
;
384 pPrinter
->m_aPrinterGfx
.Init( pPrinter
->m_aJobData
);
386 if( pJobSetup
->mpDriverData
)
387 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
389 pJobSetup
->mnSystem
= JOBSETUP_SYSTEM_UNIX
;
390 pJobSetup
->maPrinterName
= pQueueInfo
->maPrinterName
;
391 pJobSetup
->maDriver
= aInfo
.m_aDriverName
;
392 copyJobDataToJobSetup( pJobSetup
, aInfo
);
396 SalInfoPrinter
* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
397 ImplJobSetup
* pJobSetup
)
399 mbPrinterInit
= true;
400 // create and initialize SalInfoPrinter
401 PspSalInfoPrinter
* pPrinter
= new PspSalInfoPrinter();
402 configurePspInfoPrinter(pPrinter
, pQueueInfo
, pJobSetup
);
406 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
411 SalPrinter
* SalGenericInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
413 mbPrinterInit
= true;
414 // create and initialize SalPrinter
415 PspSalPrinter
* pPrinter
= new PspSalPrinter( pInfoPrinter
);
416 pPrinter
->m_aJobData
= static_cast<PspSalInfoPrinter
*>(pInfoPrinter
)->m_aJobData
;
421 void SalGenericInstance::DestroyPrinter( SalPrinter
* pPrinter
)
426 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
428 mbPrinterInit
= true;
429 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
430 static const char* pNoSyncDetection
= getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
431 if( ! pNoSyncDetection
|| ! *pNoSyncDetection
)
433 // #i62663# synchronize possible asynchronouse printer detection now
434 rManager
.checkPrintersChanged( true );
436 ::std::list
< OUString
> aPrinters
;
437 rManager
.listPrinters( aPrinters
);
439 for( ::std::list
< OUString
>::iterator it
= aPrinters
.begin(); it
!= aPrinters
.end(); ++it
)
441 const PrinterInfo
& rInfo( rManager
.getPrinterInfo( *it
) );
442 // Neuen Eintrag anlegen
443 SalPrinterQueueInfo
* pInfo
= new SalPrinterQueueInfo
;
444 pInfo
->maPrinterName
= *it
;
445 pInfo
->maDriver
= rInfo
.m_aDriverName
;
446 pInfo
->maLocation
= rInfo
.m_aLocation
;
447 pInfo
->maComment
= rInfo
.m_aComment
;
448 pInfo
->mpSysData
= NULL
;
450 sal_Int32 nIndex
= 0;
451 while( nIndex
!= -1 )
453 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
454 if( aToken
.match( "pdf=" ) )
456 pInfo
->maLocation
= getPdfDir( rInfo
);
465 void SalGenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo
* pInfo
)
470 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
472 mbPrinterInit
= true;
475 OUString
SalGenericInstance::GetDefaultPrinter()
477 mbPrinterInit
= true;
478 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
479 return rManager
.getDefaultPrinter();
482 PspSalInfoPrinter::PspSalInfoPrinter()
483 : m_pGraphics( NULL
)
487 PspSalInfoPrinter::~PspSalInfoPrinter()
496 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup
* )
498 m_aPaperFormats
.clear();
499 m_bPapersInit
= true;
501 if( m_aJobData
.m_pParser
)
503 const PPDKey
* pKey
= m_aJobData
.m_pParser
->getKey( OUString("PageSize") );
506 int nValues
= pKey
->countValues();
507 for( int i
= 0; i
< nValues
; i
++ )
509 const PPDValue
* pValue
= pKey
->getValue( i
);
510 int nWidth
= 0, nHeight
= 0;
511 m_aJobData
.m_pParser
->getPaperDimension( pValue
->m_aOption
, nWidth
, nHeight
);
512 PaperInfo
aInfo(PtTo10Mu( nWidth
), PtTo10Mu( nHeight
));
513 m_aPaperFormats
.push_back( aInfo
);
519 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup
* )
524 SalGraphics
* PspSalInfoPrinter::AcquireGraphics()
526 // return a valid pointer only once
527 // the reasoning behind this is that we could have different
528 // SalGraphics that can run in multiple threads
530 SalGraphics
* pRet
= NULL
;
533 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
534 m_pGraphics
->Init(&m_aJobData
, &m_aPrinterGfx
, this);
540 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics
* pGraphics
)
542 if( pGraphics
== m_pGraphics
)
550 bool PspSalInfoPrinter::Setup( SalFrame
* pFrame
, ImplJobSetup
* pJobSetup
)
552 if( ! pFrame
|| ! pJobSetup
)
555 PrinterInfoManager
& rManager
= PrinterInfoManager::get();
557 PrinterInfo
aInfo( rManager
.getPrinterInfo( pJobSetup
->maPrinterName
) );
558 if ( pJobSetup
->mpDriverData
)
560 SetData( ~0, pJobSetup
);
561 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
563 aInfo
.m_bPapersizeFromSetup
= pJobSetup
->mbPapersizeFromSetup
;
565 if (SetupPrinterDriver(aInfo
))
567 aInfo
.resolveDefaultBackend();
568 rtl_freeMemory( pJobSetup
->mpDriverData
);
569 pJobSetup
->mpDriverData
= NULL
;
572 void* pBuffer
= NULL
;
573 aInfo
.getStreamBuffer( pBuffer
, nBytes
);
574 pJobSetup
->mnDriverDataLen
= nBytes
;
575 pJobSetup
->mpDriverData
= static_cast<sal_uInt8
*>(pBuffer
);
577 // copy everything to job setup
578 copyJobDataToJobSetup( pJobSetup
, aInfo
);
579 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
585 // This function gets the driver data and puts it into pJobSetup
586 // If pJobSetup->mpDriverData is NOT NULL, then the independent
587 // data should be merged into the driver data
588 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
589 // should be merged into the independent data
590 bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup
* pJobSetup
)
592 if( pJobSetup
->mpDriverData
)
593 return SetData( ~0, pJobSetup
);
595 copyJobDataToJobSetup( pJobSetup
, m_aJobData
);
600 // This function merges the independ driver data
601 // and sets the new independ data in pJobSetup
602 // Only the data must be changed, where the bit
603 // in nGetDataFlags is set
604 bool PspSalInfoPrinter::SetData(
605 sal_uLong nSetDataFlags
,
606 ImplJobSetup
* pJobSetup
)
609 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
611 if( aData
.m_pParser
)
614 const PPDValue
* pValue
;
616 // merge papersize if necessary
617 if( nSetDataFlags
& SAL_JOBSET_PAPERSIZE
)
621 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
622 aPaper
= aData
.m_pParser
->matchPaper(
623 TenMuToPt( pJobSetup
->mnPaperWidth
),
624 TenMuToPt( pJobSetup
->mnPaperHeight
) );
626 aPaper
= OStringToOUString(PaperInfo::toPSName(pJobSetup
->mePaperFormat
), RTL_TEXTENCODING_ISO_8859_1
);
628 pKey
= aData
.m_pParser
->getKey( OUString("PageSize") );
629 pValue
= pKey
? pKey
->getValueCaseInsensitive( aPaper
) : NULL
;
631 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
632 // try to find the correct paper anyway using the size
633 if( pKey
&& ! pValue
&& pJobSetup
->mePaperFormat
!= PAPER_USER
)
635 PaperInfo
aInfo( pJobSetup
->mePaperFormat
);
636 aPaper
= aData
.m_pParser
->matchPaper(
637 TenMuToPt( aInfo
.getWidth() ),
638 TenMuToPt( aInfo
.getHeight() ) );
639 pValue
= pKey
->getValueCaseInsensitive( aPaper
);
642 if( ! ( pKey
&& pValue
&& aData
.m_aContext
.setValue( pKey
, pValue
, false ) == pValue
) )
646 // merge paperbin if necessary
647 if( nSetDataFlags
& SAL_JOBSET_PAPERBIN
)
649 pKey
= aData
.m_pParser
->getKey( OUString("InputSlot") );
652 int nPaperBin
= pJobSetup
->mnPaperBin
;
653 if( nPaperBin
>= pKey
->countValues() )
654 pValue
= pKey
->getDefaultValue();
656 pValue
= pKey
->getValue( pJobSetup
->mnPaperBin
);
658 // may fail due to constraints;
659 // real paper bin is copied back to jobsetup in that case
660 aData
.m_aContext
.setValue( pKey
, pValue
);
662 // if printer has no InputSlot key simply ignore this setting
663 // (e.g. SGENPRT has no InputSlot)
666 // merge orientation if necessary
667 if( nSetDataFlags
& SAL_JOBSET_ORIENTATION
)
668 aData
.m_eOrientation
= pJobSetup
->meOrientation
== ORIENTATION_LANDSCAPE
? orientation::Landscape
: orientation::Portrait
;
670 // merge duplex if necessary
671 if( nSetDataFlags
& SAL_JOBSET_DUPLEXMODE
)
673 pKey
= aData
.m_pParser
->getKey( OUString("Duplex") );
677 switch( pJobSetup
->meDuplexMode
)
680 pValue
= pKey
->getValue( OUString("None") );
682 pValue
= pKey
->getValue( OUString("SimplexNoTumble") );
684 case DUPLEX_SHORTEDGE
:
685 pValue
= pKey
->getValue( OUString("DuplexTumble") );
687 case DUPLEX_LONGEDGE
:
688 pValue
= pKey
->getValue( OUString("DuplexNoTumble") );
696 pValue
= pKey
->getDefaultValue();
697 aData
.m_aContext
.setValue( pKey
, pValue
);
702 copyJobDataToJobSetup( pJobSetup
, aData
);
709 void PspSalInfoPrinter::GetPageInfo(
710 const ImplJobSetup
* pJobSetup
,
711 long& rOutWidth
, long& rOutHeight
,
712 long& rPageOffX
, long& rPageOffY
,
713 long& rPageWidth
, long& rPageHeight
)
719 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
721 // get the selected page size
722 if( aData
.m_pParser
)
727 int left
= 0, top
= 0, right
= 0, bottom
= 0;
728 int nDPI
= aData
.m_aContext
.getRenderResolution();
730 if( aData
.m_eOrientation
== psp::orientation::Portrait
)
732 aData
.m_aContext
.getPageSize( aPaper
, width
, height
);
733 aData
.m_pParser
->getMargins( aPaper
, left
, right
, top
, bottom
);
737 aData
.m_aContext
.getPageSize( aPaper
, height
, width
);
738 aData
.m_pParser
->getMargins( aPaper
, top
, bottom
, right
, left
);
741 rPageWidth
= width
* nDPI
/ 72;
742 rPageHeight
= height
* nDPI
/ 72;
743 rPageOffX
= left
* nDPI
/ 72;
744 rPageOffY
= top
* nDPI
/ 72;
745 rOutWidth
= ( width
- left
- right
) * nDPI
/ 72;
746 rOutHeight
= ( height
- top
- bottom
) * nDPI
/ 72;
750 sal_uLong
PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup
* pJobSetup
)
756 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
758 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString("InputSlot") ): NULL
;
759 return pKey
? pKey
->countValues() : 0;
762 OUString
PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup
* pJobSetup
, sal_uLong nPaperBin
)
765 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
768 if( aData
.m_pParser
)
770 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString("InputSlot") ): NULL
;
771 if( ! pKey
|| nPaperBin
>= (sal_uLong
)pKey
->countValues() )
772 aRet
= aData
.m_pParser
->getDefaultInputSlot();
775 const PPDValue
* pValue
= pKey
->getValue( nPaperBin
);
777 aRet
= aData
.m_pParser
->translateOption( pKey
->getKey(), pValue
->m_aOption
);
784 sal_uLong
PspSalInfoPrinter::GetCapabilities( const ImplJobSetup
* pJobSetup
, sal_uInt16 nType
)
788 case PRINTER_CAPABILITIES_SUPPORTDIALOG
:
790 case PRINTER_CAPABILITIES_COPIES
:
792 case PRINTER_CAPABILITIES_COLLATECOPIES
:
794 // PPDs don't mention the number of possible collated copies.
795 // so let's guess as many as we want ?
798 case PRINTER_CAPABILITIES_SETORIENTATION
:
800 case PRINTER_CAPABILITIES_SETDUPLEX
:
802 case PRINTER_CAPABILITIES_SETPAPERBIN
:
804 case PRINTER_CAPABILITIES_SETPAPERSIZE
:
806 case PRINTER_CAPABILITIES_SETPAPER
:
808 case PRINTER_CAPABILITIES_FAX
:
810 // see if the PPD contains the fax4CUPS "Dial" option and that it's not set
812 JobData aData
= PrinterInfoManager::get().getPrinterInfo(pJobSetup
->maPrinterName
);
813 if( pJobSetup
->mpDriverData
)
814 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
815 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey(OUString("Dial")) : NULL
;
816 const PPDValue
* pValue
= pKey
? aData
.m_aContext
.getValue(pKey
) : NULL
;
817 if (pValue
&& !pValue
->m_aOption
.equalsIgnoreAsciiCase("Manually"))
822 case PRINTER_CAPABILITIES_PDF
:
823 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "pdf" ) )
827 // see if the PPD contains a value to set PDF device
828 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
829 if( pJobSetup
->mpDriverData
)
830 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
831 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
833 case PRINTER_CAPABILITIES_EXTERNALDIALOG
:
834 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "external_dialog" ) ? 1 : 0;
835 case PRINTER_CAPABILITIES_USEPULLMODEL
:
837 // see if the PPD contains a value to set PDF device
838 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
839 if( pJobSetup
->mpDriverData
)
840 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
841 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
851 PspSalPrinter::PspSalPrinter( SalInfoPrinter
* pInfoPrinter
)
853 m_bIsPDFWriterJob( false ),
857 m_pInfoPrinter( pInfoPrinter
)
861 PspSalPrinter::~PspSalPrinter()
865 static OUString
getTmpName()
868 osl_createTempFile( NULL
, NULL
, &aTmp
.pData
);
869 osl_getSystemPathFromFileURL( aTmp
.pData
, &aSys
.pData
);
874 bool PspSalPrinter::StartJob(
875 const OUString
* pFileName
,
876 const OUString
& rJobName
,
877 const OUString
& rAppName
,
881 ImplJobSetup
* pJobSetup
)
883 OSL_TRACE("PspSalPrinter::StartJob");
884 GetSalData()->m_pInstance
->jobStartedPrinterUpdate();
887 m_aFileName
= *pFileName
;
892 m_bCollate
= bCollate
;
894 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
897 // in case user did not do anything (m_nCopies=1)
898 // take the default from jobsetup
899 m_aJobData
.m_nCopies
= m_nCopies
;
900 m_aJobData
.setCollate( bCollate
);
905 // check whether this printer is configured as fax
906 sal_Int32 nIndex
= 0;
907 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
908 while( nIndex
!= -1 )
910 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
911 if( aToken
.startsWith( "pdf=" ) )
914 m_aTmpFile
= getTmpName();
915 nMode
= S_IRUSR
| S_IWUSR
;
917 if( m_aFileName
.isEmpty() )
919 OUStringBuffer
aFileName( getPdfDir( rInfo
) );
920 aFileName
.append( '/' );
921 aFileName
.append( rJobName
);
922 aFileName
.appendAscii( ".pdf" );
923 m_aFileName
= aFileName
.makeStringAndClear();
929 m_aPrinterGfx
.Init( m_aJobData
);
931 return m_aPrintJob
.StartJob( ! m_aTmpFile
.isEmpty() ? m_aTmpFile
: m_aFileName
, nMode
, rJobName
, rAppName
, m_aJobData
, &m_aPrinterGfx
, bDirect
);
934 bool PspSalPrinter::EndJob()
936 bool bSuccess
= false;
937 if( m_bIsPDFWriterJob
)
941 bSuccess
= m_aPrintJob
.EndJob();
942 OSL_TRACE("PspSalPrinter::EndJob %d", bSuccess
);
944 if( bSuccess
&& m_bPdf
)
946 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
947 bSuccess
= createPdf( m_aFileName
, m_aTmpFile
, rInfo
.m_aCommand
);
950 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
954 bool PspSalPrinter::AbortJob()
956 bool bAbort
= m_aPrintJob
.AbortJob();
957 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
961 SalGraphics
* PspSalPrinter::StartPage( ImplJobSetup
* pJobSetup
, bool )
963 OSL_TRACE("PspSalPrinter::StartPage");
965 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
966 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
967 m_pGraphics
->Init(&m_aJobData
, &m_aPrinterGfx
, m_pInfoPrinter
);
971 // in case user did not do anything (m_nCopies=1)
972 // take the default from jobsetup
973 m_aJobData
.m_nCopies
= m_nCopies
;
974 m_aJobData
.setCollate( m_nCopies
> 1 && m_bCollate
);
977 m_aPrintJob
.StartPage( m_aJobData
);
978 m_aPrinterGfx
.Init( m_aPrintJob
);
983 bool PspSalPrinter::EndPage()
985 bool bResult
= m_aPrintJob
.EndPage();
986 m_aPrinterGfx
.Clear();
987 OSL_TRACE("PspSalPrinter::EndPage");
991 sal_uLong
PspSalPrinter::GetErrorCode()
996 struct PDFNewJobParameters
999 sal_uInt16 mnPaperBin
;
1001 PDFNewJobParameters( const Size
& i_rSize
= Size(),
1002 sal_uInt16 i_nPaperBin
= 0xffff )
1003 : maPageSize( i_rSize
), mnPaperBin( i_nPaperBin
) {}
1005 bool operator==(const PDFNewJobParameters
& rComp
) const
1007 const long nRotatedWidth
= rComp
.maPageSize
.Height();
1008 const long nRotatedHeight
= rComp
.maPageSize
.Width();
1009 Size
aCompLSSize(nRotatedWidth
, nRotatedHeight
);
1011 (maPageSize
== rComp
.maPageSize
|| maPageSize
== aCompLSSize
)
1012 && mnPaperBin
== rComp
.mnPaperBin
1016 bool operator!=(const PDFNewJobParameters
& rComp
) const
1018 return ! this->operator==(rComp
);
1025 PDFNewJobParameters maParameters
;
1027 PDFPrintFile( const OUString
& i_rURL
, const PDFNewJobParameters
& i_rNewParameters
)
1028 : maTmpURL( i_rURL
)
1029 , maParameters( i_rNewParameters
) {}
1032 bool PspSalPrinter::StartJob( const OUString
* i_pFileName
, const OUString
& i_rJobName
, const OUString
& i_rAppName
,
1033 ImplJobSetup
* i_pSetupData
, vcl::PrinterController
& i_rController
)
1035 OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName
? OUStringToOString( *i_pFileName
, RTL_TEXTENCODING_UTF8
).getStr() : "<nil>" );
1037 m_bIsPDFWriterJob
= true;
1039 i_rController
.setLastPage( false );
1040 // is this a fax device
1041 bool bFax
= m_pInfoPrinter
->GetCapabilities(i_pSetupData
, PRINTER_CAPABILITIES_FAX
) == 1;
1045 JobData::constructFromStreamBuffer( i_pSetupData
->mpDriverData
, i_pSetupData
->mnDriverDataLen
, m_aJobData
);
1047 OSL_ASSERT( m_aJobData
.m_nPDFDevice
> 0 );
1048 m_aJobData
.m_nPDFDevice
= 1;
1050 // possibly create one job for collated output
1051 bool bSinglePrintJobs
= false;
1052 beans::PropertyValue
* pSingleValue
= i_rController
.getValue( OUString( "PrintCollateAsSingleJobs" ) );
1055 pSingleValue
->Value
>>= bSinglePrintJobs
;
1058 int nCopies
= i_rController
.getPrinter()->GetCopyCount();
1059 bool bCollate
= i_rController
.getPrinter()->IsCollateCopy();
1061 // notify start of real print job
1062 i_rController
.jobStarted();
1064 // setup PDFWriter context
1065 vcl::PDFWriter::PDFWriterContext aContext
;
1066 aContext
.Version
= vcl::PDFWriter::PDF_1_4
;
1067 aContext
.Tagged
= false;
1068 aContext
.DocumentLocale
= Application::GetSettings().GetLanguageTag().getLocale();
1069 aContext
.ColorMode
= i_rController
.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
1070 ? vcl::PDFWriter::DrawGreyscale
: vcl::PDFWriter::DrawColor
;
1073 aContext
.DocumentInfo
.Title
= i_rJobName
;
1074 aContext
.DocumentInfo
.Creator
= i_rAppName
;
1075 aContext
.DocumentInfo
.Producer
= i_rAppName
;
1077 // define how we handle metafiles in PDFWriter
1078 vcl::PDFWriter::PlayMetafileContext aMtfContext
;
1079 aMtfContext
.m_bOnlyLosslessCompression
= true;
1081 std::shared_ptr
<vcl::PDFWriter
> xWriter
;
1082 std::vector
< PDFPrintFile
> aPDFFiles
;
1083 VclPtr
<Printer
> xPrinter( i_rController
.getPrinter() );
1084 int nAllPages
= i_rController
.getFilteredPageCount();
1085 i_rController
.createProgressDialog();
1086 bool bAborted
= false;
1087 PDFNewJobParameters aLastParm
;
1089 aContext
.DPIx
= xPrinter
->GetDPIX();
1090 aContext
.DPIy
= xPrinter
->GetDPIY();
1091 for( int nPage
= 0; nPage
< nAllPages
&& ! bAborted
; nPage
++ )
1093 if( nPage
== nAllPages
-1 )
1094 i_rController
.setLastPage( true );
1096 // get the page's metafile
1097 GDIMetaFile aPageFile
;
1098 vcl::PrinterController::PageSize aPageSize
= i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1099 if( i_rController
.isProgressCanceled() )
1102 if( nPage
!= nAllPages
-1 )
1104 i_rController
.createProgressDialog();
1105 i_rController
.setLastPage( true );
1106 i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1111 xPrinter
->SetMapMode( MapMode( MAP_100TH_MM
) );
1112 xPrinter
->SetPaperSizeUser( aPageSize
.aSize
, true );
1113 PDFNewJobParameters
aNewParm(xPrinter
->GetPaperSize(), xPrinter
->GetPaperBin());
1115 // create PDF writer on demand
1116 // either on first page
1117 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1118 // so we need to start a new job to get a new paper format from the printer
1119 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1121 (aNewParm
!= aLastParm
&& ! i_pFileName
) )
1130 aPDFUrl
= *i_pFileName
;
1132 osl_createTempFile( NULL
, NULL
, &aPDFUrl
.pData
);
1133 // normalize to file URL
1134 if( !aPDFUrl
.startsWith( "file:" ) )
1136 // this is not a file URL, but it should
1137 // form it into a osl friendly file URL
1139 osl_getFileURLFromSystemPath( aPDFUrl
.pData
, &aTmp
.pData
);
1142 // save current file and paper format
1143 aLastParm
= aNewParm
;
1144 aPDFFiles
.push_back( PDFPrintFile( aPDFUrl
, aNewParm
) );
1146 aContext
.URL
= aPDFUrl
;
1148 // create and initialize PDFWriter
1149 xWriter
.reset( new vcl::PDFWriter( aContext
, uno::Reference
< beans::XMaterialHolder
>() ) );
1152 xWriter
->NewPage( TenMuToPt( aNewParm
.maPageSize
.Width() ),
1153 TenMuToPt( aNewParm
.maPageSize
.Height() ),
1154 vcl::PDFWriter::Portrait
);
1156 xWriter
->PlayMetafile( aPageFile
, aMtfContext
, NULL
);
1160 // emit the last file
1164 // handle collate, copy count and multiple jobs correctly
1166 if( bSinglePrintJobs
)
1168 nOuterJobs
= nCopies
;
1169 m_aJobData
.m_nCopies
= 1;
1175 if (aPDFFiles
.size() == 1 && xPrinter
->HasSupport(SUPPORT_COLLATECOPY
))
1177 m_aJobData
.setCollate( true );
1178 m_aJobData
.m_nCopies
= nCopies
;
1182 nOuterJobs
= nCopies
;
1183 m_aJobData
.m_nCopies
= 1;
1188 m_aJobData
.setCollate( false );
1189 m_aJobData
.m_nCopies
= nCopies
;
1193 std::vector
<OUString
> aFaxNumbers
;
1195 // check for fax numbers
1196 if (!bAborted
&& bFax
)
1198 aFaxNumbers
= getFaxNumbers();
1199 bAborted
= aFaxNumbers
.empty();
1202 bool bSuccess(true);
1204 if( ! i_pFileName
&& ! bAborted
)
1208 OUString sFaxNumber
;
1209 if (!aFaxNumbers
.empty())
1211 sFaxNumber
= aFaxNumbers
.back();
1212 aFaxNumbers
.pop_back();
1215 bool bFirstJob
= true;
1216 for( int nCurJob
= 0; nCurJob
< nOuterJobs
; nCurJob
++ )
1218 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1220 oslFileHandle pFile
= NULL
;
1221 osl_openFile( aPDFFiles
[i
].maTmpURL
.pData
, &pFile
, osl_File_OpenFlag_Read
);
1222 if (pFile
&& (osl_setFilePos(pFile
, osl_Pos_Absolut
, 0) == osl_File_E_None
))
1224 std::vector
< char > buffer( 0x10000, 0 );
1225 // update job data with current page size
1226 Size
aPageSize( aPDFFiles
[i
].maParameters
.maPageSize
);
1227 m_aJobData
.setPaper( TenMuToPt( aPageSize
.Width() ), TenMuToPt( aPageSize
.Height() ) );
1228 // update job data with current paperbin
1229 m_aJobData
.setPaperBin( aPDFFiles
[i
].maParameters
.mnPaperBin
);
1231 // spool current file
1232 FILE* fp
= PrinterInfoManager::get().startSpool(xPrinter
->GetName(), i_rController
.isDirectPrint());
1235 sal_uInt64 nBytesRead
= 0;
1238 osl_readFile( pFile
, &buffer
[0], buffer
.size(), &nBytesRead
);
1239 if( nBytesRead
> 0 )
1241 size_t nBytesWritten
= fwrite(&buffer
[0], 1, nBytesRead
, fp
);
1242 OSL_ENSURE(nBytesRead
== nBytesWritten
, "short write");
1243 if (nBytesRead
!= nBytesWritten
)
1246 } while( nBytesRead
== buffer
.size() );
1247 OUStringBuffer
aBuf( i_rJobName
.getLength() + 8 );
1248 aBuf
.append( i_rJobName
);
1249 if( i
> 0 || nCurJob
> 0 )
1252 aBuf
.append( sal_Int32( i
+ nCurJob
* aPDFFiles
.size() ) );
1255 PrinterInfoManager::get().endSpool(xPrinter
->GetName(), aBuf
.makeStringAndClear(), fp
, m_aJobData
, bFirstJob
, sFaxNumber
);
1259 osl_closeFile( pFile
);
1263 while (!aFaxNumbers
.empty());
1266 // job has been spooled
1267 i_rController
.setJobState( (bAborted
)
1268 ? view::PrintableState_JOB_ABORTED
1269 : ((bSuccess
) ? view::PrintableState_JOB_SPOOLED
1270 : view::PrintableState_JOB_SPOOLING_FAILED
));
1272 // clean up the temporary PDF files
1273 if( ! i_pFileName
|| bAborted
)
1275 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1277 osl_removeFile( aPDFFiles
[i
].maTmpURL
.pData
);
1278 OSL_TRACE( "removed print PDF file %s", OUStringToOString( aPDFFiles
[i
].maTmpURL
, RTL_TEXTENCODING_UTF8
).getStr() );
1287 static Idle
* pPrinterUpdateIdle
;
1288 static int nActiveJobs
;
1290 static void doUpdate();
1291 DECL_STATIC_LINK_TYPED( PrinterUpdate
, UpdateTimerHdl
, Idle
*, void );
1293 static void update(SalGenericInstance
&rInstance
);
1294 static void jobStarted() { nActiveJobs
++; }
1295 static void jobEnded();
1298 Idle
* PrinterUpdate::pPrinterUpdateIdle
= NULL
;
1299 int PrinterUpdate::nActiveJobs
= 0;
1301 void PrinterUpdate::doUpdate()
1303 ::psp::PrinterInfoManager
& rManager( ::psp::PrinterInfoManager::get() );
1304 SalGenericInstance
*pInst
= static_cast<SalGenericInstance
*>( GetSalData()->m_pInstance
);
1305 if( pInst
&& rManager
.checkPrintersChanged( false ) )
1306 pInst
->PostPrintersChanged();
1309 IMPL_STATIC_LINK_NOARG_TYPED( PrinterUpdate
, UpdateTimerHdl
, Idle
*, void )
1311 if( nActiveJobs
< 1 )
1314 delete pPrinterUpdateIdle
;
1315 pPrinterUpdateIdle
= NULL
;
1318 pPrinterUpdateIdle
->Start();
1321 void PrinterUpdate::update(SalGenericInstance
&rInstance
)
1323 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1326 if( ! rInstance
.isPrinterInit() )
1328 // #i45389# start background printer detection
1329 psp::PrinterInfoManager::get();
1333 if( nActiveJobs
< 1 )
1335 else if( ! pPrinterUpdateIdle
)
1337 pPrinterUpdateIdle
= new Idle();
1338 pPrinterUpdateIdle
->SetPriority( SchedulerPriority::LOWEST
);
1339 pPrinterUpdateIdle
->SetIdleHdl( LINK( NULL
, PrinterUpdate
, UpdateTimerHdl
) );
1340 pPrinterUpdateIdle
->Start();
1344 void SalGenericInstance::updatePrinterUpdate()
1346 PrinterUpdate::update(*this);
1349 void SalGenericInstance::jobStartedPrinterUpdate()
1351 PrinterUpdate::jobStarted();
1354 void PrinterUpdate::jobEnded()
1357 if( nActiveJobs
< 1 )
1359 if( pPrinterUpdateIdle
)
1361 pPrinterUpdateIdle
->Stop();
1362 delete pPrinterUpdateIdle
;
1363 pPrinterUpdateIdle
= NULL
;
1369 void SalGenericInstance::jobEndedPrinterUpdate()
1371 PrinterUpdate::jobEnded();
1374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */