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 .
22 this file implements the sal printer interface ( SalPrinter, SalInfoPrinter
23 and some printer relevant methods of SalInstance and SalGraphicsData )
25 as aunderlying library the printer features of psprint are used.
27 The query methods of a SalInfoPrinter are implemented by querying psprint
29 The job methods of a SalPrinter are implemented by calling psprint
30 printer job functions.
33 // For spawning PDF and FAX generation
36 # include <sys/wait.h>
37 # include <sys/stat.h>
40 #include "rtl/ustring.hxx"
42 #include "osl/module.h"
44 #include "vcl/svapp.hxx"
45 #include "vcl/print.hxx"
46 #include "vcl/pdfwriter.hxx"
47 #include "vcl/printerinfomanager.hxx"
49 #include "saldatabasic.hxx"
50 #include "generic/genprn.h"
51 #include "generic/geninst.h"
52 #include "generic/genpspgraphics.h"
56 #include "salptype.hxx"
58 #include <com/sun/star/beans/PropertyValue.hpp>
61 using namespace com::sun::star
;
63 using ::rtl::OUString
;
64 using ::rtl::OUStringHash
;
65 using ::rtl::OUStringToOString
;
71 #if defined( UNX ) && !( defined( QUARTZ ) || defined( IOS ) || defined( ANDROID ) )
72 static oslModule driverLib
= NULL
;
76 typedef int(*setupFunction
)(PrinterInfo
&);
77 static setupFunction pSetupFunction
= NULL
;
78 typedef int(*faxFunction
)(OUString
&);
79 static faxFunction pFaxNrFunction
= NULL
;
82 static rtl::OUString
getPdfDir( const PrinterInfo
& rInfo
)
88 rtl::OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
89 if( ! aToken
.compareToAscii( "pdf=", 4 ) )
92 aDir
= aToken
.getToken( 1, '=', nPos
);
93 if( aDir
.isEmpty() && getenv( "HOME" ) )
94 aDir
= rtl::OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
101 static void getPaLib()
103 #if defined( UNX ) && !( defined( QUARTZ ) || defined( IOS ) || defined( ANDROID ) )
106 OUString
aLibName( RTL_CONSTASCII_USTRINGPARAM( _XSALSET_LIBNAME
) );
107 driverLib
= osl_loadModuleRelative( (oslGenericFunction
)getPaLib
, aLibName
.pData
, SAL_LOADMODULE_DEFAULT
);
113 pSetupFunction
= (setupFunction
)osl_getAsciiFunctionSymbol( driverLib
, "Sal_SetupPrinterDriver" );
114 if ( !pSetupFunction
)
115 fprintf( stderr
, "could not resolve Sal_SetupPrinterDriver\n" );
117 pFaxNrFunction
= (faxFunction
)osl_getAsciiFunctionSymbol( driverLib
, "Sal_queryFaxNumber" );
118 if ( !pFaxNrFunction
)
119 fprintf( stderr
, "could not resolve Sal_queryFaxNumber\n" );
124 inline int PtTo10Mu( int nPoints
) { return (int)((((double)nPoints
)*35.27777778)+0.5); }
126 inline int TenMuToPt( int nUnits
) { return (int)((((double)nUnits
)/35.27777778)+0.5); }
128 static void copyJobDataToJobSetup( ImplJobSetup
* pJobSetup
, JobData
& rData
)
130 pJobSetup
->meOrientation
= (Orientation
)(rData
.m_eOrientation
== orientation::Landscape
? ORIENTATION_LANDSCAPE
: ORIENTATION_PORTRAIT
);
136 rData
.m_aContext
.getPageSize( aPaper
, width
, height
);
137 pJobSetup
->mePaperFormat
= PaperInfo::fromPSName(OUStringToOString( aPaper
, RTL_TEXTENCODING_ISO_8859_1
));
139 pJobSetup
->mnPaperWidth
= 0;
140 pJobSetup
->mnPaperHeight
= 0;
141 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
143 // transform to 100dth mm
144 width
= PtTo10Mu( width
);
145 height
= PtTo10Mu( height
);
147 if( rData
.m_eOrientation
== psp::orientation::Portrait
)
149 pJobSetup
->mnPaperWidth
= width
;
150 pJobSetup
->mnPaperHeight
= height
;
154 pJobSetup
->mnPaperWidth
= height
;
155 pJobSetup
->mnPaperHeight
= width
;
160 const PPDKey
* pKey
= NULL
;
161 const PPDValue
* pValue
= NULL
;
163 pJobSetup
->mnPaperBin
= 0;
164 if( rData
.m_pParser
)
165 pKey
= rData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
167 pValue
= rData
.m_aContext
.getValue( pKey
);
170 for( pJobSetup
->mnPaperBin
= 0;
171 pValue
!= pKey
->getValue( pJobSetup
->mnPaperBin
) &&
172 pJobSetup
->mnPaperBin
< pKey
->countValues();
173 pJobSetup
->mnPaperBin
++ )
175 if( pJobSetup
->mnPaperBin
>= pKey
->countValues() )
176 pJobSetup
->mnPaperBin
= 0;
183 pJobSetup
->meDuplexMode
= DUPLEX_UNKNOWN
;
184 if( rData
.m_pParser
)
185 pKey
= rData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
187 pValue
= rData
.m_aContext
.getValue( pKey
);
190 if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "None" ) ||
191 pValue
->m_aOption
.EqualsIgnoreCaseAscii( "Simplex", 0, 7 )
194 pJobSetup
->meDuplexMode
= DUPLEX_OFF
;
196 else if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) )
198 pJobSetup
->meDuplexMode
= DUPLEX_LONGEDGE
;
200 else if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "DuplexTumble" ) )
202 pJobSetup
->meDuplexMode
= DUPLEX_SHORTEDGE
;
206 // copy the whole context
207 if( pJobSetup
->mpDriverData
)
208 rtl_freeMemory( pJobSetup
->mpDriverData
);
211 void* pBuffer
= NULL
;
212 if( rData
.getStreamBuffer( pBuffer
, nBytes
) )
214 pJobSetup
->mnDriverDataLen
= nBytes
;
215 pJobSetup
->mpDriverData
= (sal_uInt8
*)pBuffer
;
219 pJobSetup
->mnDriverDataLen
= 0;
220 pJobSetup
->mpDriverData
= NULL
;
224 // Needs a cleaner abstraction ...
226 static bool passFileToCommandLine( const OUString
& rFilename
, const OUString
& rCommandLine
, bool bRemoveFile
= true )
228 bool bSuccess
= false;
230 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
231 rtl::OString
aCmdLine(rtl::OUStringToOString(rCommandLine
, aEncoding
));
232 rtl::OString
aFilename(rtl::OUStringToOString(rFilename
, aEncoding
));
234 bool bPipe
= aCmdLine
.indexOf( "(TMP)" ) != -1 ? false : true;
236 // setup command line for exec
238 aCmdLine
= aCmdLine
.replaceAll(rtl::OString("(TMP)"), aFilename
);
240 #if OSL_DEBUG_LEVEL > 1
241 fprintf( stderr
, "%s commandline: \"%s\"\n",
242 bPipe
? "piping to" : "executing",
245 if( stat( aFilename
.getStr(), &aStat
) )
246 fprintf( stderr
, "stat( %s ) failed\n", aFilename
.getStr() );
247 fprintf( stderr
, "Tmp file %s has modes: 0%03lo\n", aFilename
.getStr(), (long)aStat
.st_mode
);
250 if( ! ( argv
[ 0 ] = getenv( "SHELL" ) ) )
251 argv
[ 0 ] = "/bin/sh";
253 argv
[ 2 ] = aCmdLine
.getStr();
256 bool bHavePipes
= false;
260 bHavePipes
= pipe( fd
) ? false : true;
261 if( ( pid
= fork() ) > 0 )
263 if( bPipe
&& bHavePipes
)
266 char aBuffer
[ 2048 ];
267 FILE* fp
= fopen( aFilename
.getStr(), "r" );
268 while (fp
&& !feof(fp
))
270 size_t nBytesRead
= fread(aBuffer
, 1, sizeof( aBuffer
), fp
);
273 size_t nBytesWritten
= write(fd
[1], aBuffer
, nBytesRead
);
274 OSL_ENSURE(nBytesWritten
== nBytesRead
, "short write");
275 if (nBytesWritten
!= nBytesRead
)
283 waitpid( pid
, &status
, 0 );
289 if( bPipe
&& bHavePipes
)
292 if( fd
[0] != STDIN_FILENO
) // not probable, but who knows :)
293 dup2( fd
[0], STDIN_FILENO
);
295 execv( argv
[0], const_cast<char**>(argv
) );
296 fprintf( stderr
, "failed to execute \"%s\"\n", aCmdLine
.getStr() );
300 fprintf( stderr
, "failed to fork\n" );
304 unlink( aFilename
.getStr() );
310 static bool sendAFax( const OUString
& rFaxNumber
, const OUString
& rFileName
, const OUString
& rCommand
)
313 std::list
< OUString
> aFaxNumbers
;
315 if( rFaxNumber
.isEmpty() )
321 if( pFaxNrFunction( aNewNr
) )
322 aFaxNumbers
.push_back( aNewNr
);
327 sal_Int32 nIndex
= 0;
328 OUString
aFaxes( rFaxNumber
);
329 OUString
aBeginToken( RTL_CONSTASCII_USTRINGPARAM("<Fax#>") );
330 OUString
aEndToken( RTL_CONSTASCII_USTRINGPARAM("</Fax#>") );
331 while( nIndex
!= -1 )
333 nIndex
= aFaxes
.indexOf( aBeginToken
, nIndex
);
336 sal_Int32 nBegin
= nIndex
+ aBeginToken
.getLength();
337 nIndex
= aFaxes
.indexOf( aEndToken
, nIndex
);
340 aFaxNumbers
.push_back( aFaxes
.copy( nBegin
, nIndex
-nBegin
) );
341 nIndex
+= aEndToken
.getLength();
347 bool bSuccess
= true;
348 if( aFaxNumbers
.begin() != aFaxNumbers
.end() )
350 while( aFaxNumbers
.begin() != aFaxNumbers
.end() && bSuccess
)
352 OUString
aFaxNumber( aFaxNumbers
.front() );
353 aFaxNumbers
.pop_front();
355 rCommand
.replaceAll("(PHONE)", aFaxNumber
));
356 #if OSL_DEBUG_LEVEL > 1
357 fprintf( stderr
, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber
, osl_getThreadTextEncoding() ).getStr() );
359 bSuccess
= passFileToCommandLine( rFileName
, aCmdLine
, false );
365 // clean up temp file
366 unlink(rtl::OUStringToOString(rFileName
, osl_getThreadTextEncoding()).getStr());
370 (void)rFaxNumber
; (void)rFileName
; (void)rCommand
;
375 static bool createPdf( const OUString
& rToFile
, const OUString
& rFromFile
, const OUString
& rCommandLine
)
378 OUString
aCommandLine(
379 rCommandLine
.replaceAll("(OUTFILE)", rToFile
));
381 return passFileToCommandLine( rFromFile
, aCommandLine
);
383 (void)rToFile
; (void)rFromFile
; (void)rCommandLine
;
392 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter
*pPrinter
,
393 SalPrinterQueueInfo
* pQueueInfo
, ImplJobSetup
* pJobSetup
)
397 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
398 PrinterInfo
aInfo( rManager
.getPrinterInfo( pQueueInfo
->maPrinterName
) );
399 pPrinter
->m_aJobData
= aInfo
;
400 pPrinter
->m_aPrinterGfx
.Init( pPrinter
->m_aJobData
);
402 if( pJobSetup
->mpDriverData
)
403 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
405 pJobSetup
->mnSystem
= JOBSETUP_SYSTEM_UNIX
;
406 pJobSetup
->maPrinterName
= pQueueInfo
->maPrinterName
;
407 pJobSetup
->maDriver
= aInfo
.m_aDriverName
;
408 copyJobDataToJobSetup( pJobSetup
, aInfo
);
412 SalInfoPrinter
* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
413 ImplJobSetup
* pJobSetup
)
415 mbPrinterInit
= true;
416 // create and initialize SalInfoPrinter
417 PspSalInfoPrinter
* pPrinter
= new PspSalInfoPrinter();
418 configurePspInfoPrinter(pPrinter
, pQueueInfo
, pJobSetup
);
422 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
427 SalPrinter
* SalGenericInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
429 mbPrinterInit
= true;
430 // create and initialize SalPrinter
431 PspSalPrinter
* pPrinter
= new PspSalPrinter( pInfoPrinter
);
432 pPrinter
->m_aJobData
= static_cast<PspSalInfoPrinter
*>(pInfoPrinter
)->m_aJobData
;
437 void SalGenericInstance::DestroyPrinter( SalPrinter
* pPrinter
)
442 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
444 mbPrinterInit
= true;
445 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
446 static const char* pNoSyncDetection
= getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
447 if( ! pNoSyncDetection
|| ! *pNoSyncDetection
)
449 // #i62663# synchronize possible asynchronouse printer detection now
450 rManager
.checkPrintersChanged( true );
452 ::std::list
< OUString
> aPrinters
;
453 rManager
.listPrinters( aPrinters
);
455 for( ::std::list
< OUString
>::iterator it
= aPrinters
.begin(); it
!= aPrinters
.end(); ++it
)
457 const PrinterInfo
& rInfo( rManager
.getPrinterInfo( *it
) );
458 // Neuen Eintrag anlegen
459 SalPrinterQueueInfo
* pInfo
= new SalPrinterQueueInfo
;
460 pInfo
->maPrinterName
= *it
;
461 pInfo
->maDriver
= rInfo
.m_aDriverName
;
462 pInfo
->maLocation
= rInfo
.m_aLocation
;
463 pInfo
->maComment
= rInfo
.m_aComment
;
464 pInfo
->mpSysData
= NULL
;
466 sal_Int32 nIndex
= 0;
467 while( nIndex
!= -1 )
469 rtl::OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
470 if( aToken
.matchAsciiL( RTL_CONSTASCII_STRINGPARAM("pdf=") ) )
472 pInfo
->maLocation
= getPdfDir( rInfo
);
481 void SalGenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo
* pInfo
)
486 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
488 mbPrinterInit
= true;
491 rtl::OUString
SalGenericInstance::GetDefaultPrinter()
493 mbPrinterInit
= true;
494 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
495 return rManager
.getDefaultPrinter();
498 PspSalInfoPrinter::PspSalInfoPrinter()
499 : m_pGraphics( NULL
)
503 PspSalInfoPrinter::~PspSalInfoPrinter()
512 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup
* )
514 m_aPaperFormats
.clear();
515 m_bPapersInit
= true;
517 if( m_aJobData
.m_pParser
)
519 const PPDKey
* pKey
= m_aJobData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
522 int nValues
= pKey
->countValues();
523 for( int i
= 0; i
< nValues
; i
++ )
525 const PPDValue
* pValue
= pKey
->getValue( i
);
526 int nWidth
= 0, nHeight
= 0;
527 m_aJobData
.m_pParser
->getPaperDimension( pValue
->m_aOption
, nWidth
, nHeight
);
528 PaperInfo
aInfo(PtTo10Mu( nWidth
), PtTo10Mu( nHeight
));
529 m_aPaperFormats
.push_back( aInfo
);
535 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup
* )
540 SalGraphics
* PspSalInfoPrinter::GetGraphics()
542 // return a valid pointer only once
543 // the reasoning behind this is that we could have different
544 // SalGraphics that can run in multiple threads
546 SalGraphics
* pRet
= NULL
;
549 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
550 m_pGraphics
->Init( &m_aJobData
, &m_aPrinterGfx
, NULL
, false, this );
556 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics
* pGraphics
)
558 if( pGraphics
== m_pGraphics
)
566 sal_Bool
PspSalInfoPrinter::Setup( SalFrame
* pFrame
, ImplJobSetup
* pJobSetup
)
568 if( ! pFrame
|| ! pJobSetup
)
573 if( ! pSetupFunction
)
576 PrinterInfoManager
& rManager
= PrinterInfoManager::get();
578 PrinterInfo
aInfo( rManager
.getPrinterInfo( pJobSetup
->maPrinterName
) );
579 if ( pJobSetup
->mpDriverData
)
581 SetData( ~0, pJobSetup
);
582 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
585 if( pSetupFunction( aInfo
) )
587 aInfo
.resolveDefaultBackend();
588 rtl_freeMemory( pJobSetup
->mpDriverData
);
589 pJobSetup
->mpDriverData
= NULL
;
592 void* pBuffer
= NULL
;
593 aInfo
.getStreamBuffer( pBuffer
, nBytes
);
594 pJobSetup
->mnDriverDataLen
= nBytes
;
595 pJobSetup
->mpDriverData
= (sal_uInt8
*)pBuffer
;
597 // copy everything to job setup
598 copyJobDataToJobSetup( pJobSetup
, aInfo
);
599 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
605 // This function gets the driver data and puts it into pJobSetup
606 // If pJobSetup->mpDriverData is NOT NULL, then the independend
607 // data should be merged into the driver data
608 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
609 // should be merged into the independent data
610 sal_Bool
PspSalInfoPrinter::SetPrinterData( ImplJobSetup
* pJobSetup
)
612 if( pJobSetup
->mpDriverData
)
613 return SetData( ~0, pJobSetup
);
615 copyJobDataToJobSetup( pJobSetup
, m_aJobData
);
620 // This function merges the independ driver data
621 // and sets the new independ data in pJobSetup
622 // Only the data must be changed, where the bit
623 // in nGetDataFlags is set
624 sal_Bool
PspSalInfoPrinter::SetData(
625 sal_uLong nSetDataFlags
,
626 ImplJobSetup
* pJobSetup
)
629 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
631 if( aData
.m_pParser
)
634 const PPDValue
* pValue
;
636 // merge papersize if necessary
637 if( nSetDataFlags
& SAL_JOBSET_PAPERSIZE
)
641 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
642 aPaper
= aData
.m_pParser
->matchPaper(
643 TenMuToPt( pJobSetup
->mnPaperWidth
),
644 TenMuToPt( pJobSetup
->mnPaperHeight
) );
646 aPaper
= rtl::OStringToOUString(PaperInfo::toPSName(pJobSetup
->mePaperFormat
), RTL_TEXTENCODING_ISO_8859_1
);
648 pKey
= aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
649 pValue
= pKey
? pKey
->getValueCaseInsensitive( aPaper
) : NULL
;
651 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
652 // try to find the correct paper anyway using the size
653 if( pKey
&& ! pValue
&& pJobSetup
->mePaperFormat
!= PAPER_USER
)
655 PaperInfo
aInfo( pJobSetup
->mePaperFormat
);
656 aPaper
= aData
.m_pParser
->matchPaper(
657 TenMuToPt( aInfo
.getWidth() ),
658 TenMuToPt( aInfo
.getHeight() ) );
659 pValue
= pKey
->getValueCaseInsensitive( aPaper
);
662 if( ! ( pKey
&& pValue
&& aData
.m_aContext
.setValue( pKey
, pValue
, false ) == pValue
) )
666 // merge paperbin if necessary
667 if( nSetDataFlags
& SAL_JOBSET_PAPERBIN
)
669 pKey
= aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
672 int nPaperBin
= pJobSetup
->mnPaperBin
;
673 if( nPaperBin
>= pKey
->countValues() )
674 pValue
= pKey
->getDefaultValue();
676 pValue
= pKey
->getValue( pJobSetup
->mnPaperBin
);
678 // may fail due to constraints;
679 // real paper bin is copied back to jobsetup in that case
680 aData
.m_aContext
.setValue( pKey
, pValue
);
682 // if printer has no InputSlot key simply ignore this setting
683 // (e.g. SGENPRT has no InputSlot)
686 // merge orientation if necessary
687 if( nSetDataFlags
& SAL_JOBSET_ORIENTATION
)
688 aData
.m_eOrientation
= pJobSetup
->meOrientation
== ORIENTATION_LANDSCAPE
? orientation::Landscape
: orientation::Portrait
;
690 // merge duplex if necessary
691 if( nSetDataFlags
& SAL_JOBSET_DUPLEXMODE
)
693 pKey
= aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
697 switch( pJobSetup
->meDuplexMode
)
700 pValue
= pKey
->getValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
702 pValue
= pKey
->getValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "SimplexNoTumble" ) ) );
704 case DUPLEX_SHORTEDGE
:
705 pValue
= pKey
->getValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexTumble" ) ) );
707 case DUPLEX_LONGEDGE
:
708 pValue
= pKey
->getValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "DuplexNoTumble" ) ) );
716 pValue
= pKey
->getDefaultValue();
717 aData
.m_aContext
.setValue( pKey
, pValue
);
722 copyJobDataToJobSetup( pJobSetup
, aData
);
729 void PspSalInfoPrinter::GetPageInfo(
730 const ImplJobSetup
* pJobSetup
,
731 long& rOutWidth
, long& rOutHeight
,
732 long& rPageOffX
, long& rPageOffY
,
733 long& rPageWidth
, long& rPageHeight
)
739 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
741 // get the selected page size
742 if( aData
.m_pParser
)
747 int left
= 0, top
= 0, right
= 0, bottom
= 0;
748 int nDPI
= aData
.m_aContext
.getRenderResolution();
751 if( aData
.m_eOrientation
== psp::orientation::Portrait
)
753 aData
.m_aContext
.getPageSize( aPaper
, width
, height
);
754 aData
.m_pParser
->getMargins( aPaper
, left
, right
, top
, bottom
);
758 aData
.m_aContext
.getPageSize( aPaper
, height
, width
);
759 aData
.m_pParser
->getMargins( aPaper
, top
, bottom
, right
, left
);
762 rPageWidth
= width
* nDPI
/ 72;
763 rPageHeight
= height
* nDPI
/ 72;
764 rPageOffX
= left
* nDPI
/ 72;
765 rPageOffY
= top
* nDPI
/ 72;
766 rOutWidth
= ( width
- left
- right
) * nDPI
/ 72;
767 rOutHeight
= ( height
- top
- bottom
) * nDPI
/ 72;
771 sal_uLong
PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup
* pJobSetup
)
777 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
779 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL
;
780 return pKey
? pKey
->countValues() : 0;
783 rtl::OUString
PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup
* pJobSetup
, sal_uLong nPaperBin
)
786 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
789 if( aData
.m_pParser
)
791 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL
;
792 if( ! pKey
|| nPaperBin
>= (sal_uLong
)pKey
->countValues() )
793 aRet
= aData
.m_pParser
->getDefaultInputSlot();
796 const PPDValue
* pValue
= pKey
->getValue( nPaperBin
);
798 aRet
= aData
.m_pParser
->translateOption( pKey
->getKey(), pValue
->m_aOption
);
805 sal_uLong
PspSalInfoPrinter::GetCapabilities( const ImplJobSetup
* pJobSetup
, sal_uInt16 nType
)
809 case PRINTER_CAPABILITIES_SUPPORTDIALOG
:
811 case PRINTER_CAPABILITIES_COPIES
:
813 case PRINTER_CAPABILITIES_COLLATECOPIES
:
815 // see if the PPD contains a value to set Collate to True
817 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
819 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ) : NULL
;
820 const PPDValue
* pVal
= pKey
? pKey
->getValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ) : NULL
;
822 // PPDs don't mention the number of possible collated copies.
823 // so let's guess as many as we want ?
824 return pVal
? 0xffff : 0;
826 case PRINTER_CAPABILITIES_SETORIENTATION
:
828 case PRINTER_CAPABILITIES_SETDUPLEX
:
830 case PRINTER_CAPABILITIES_SETPAPERBIN
:
832 case PRINTER_CAPABILITIES_SETPAPERSIZE
:
834 case PRINTER_CAPABILITIES_SETPAPER
:
836 case PRINTER_CAPABILITIES_FAX
:
837 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "fax" ) ? 1 : 0;
838 case PRINTER_CAPABILITIES_PDF
:
839 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "pdf" ) )
843 // see if the PPD contains a value to set Collate to True
844 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
845 if( pJobSetup
->mpDriverData
)
846 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
847 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
849 case PRINTER_CAPABILITIES_EXTERNALDIALOG
:
850 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "external_dialog" ) ? 1 : 0;
851 case PRINTER_CAPABILITIES_USEPULLMODEL
:
853 // see if the PPD contains a value to set Collate to True
854 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
855 if( pJobSetup
->mpDriverData
)
856 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
857 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
867 PspSalPrinter::PspSalPrinter( SalInfoPrinter
* pInfoPrinter
)
870 m_bSwallowFaxNo( false ),
871 m_bIsPDFWriterJob( false ),
875 m_pInfoPrinter( pInfoPrinter
)
879 PspSalPrinter::~PspSalPrinter()
883 static rtl::OUString
getTmpName()
885 rtl::OUString aTmp
, aSys
;
886 osl_createTempFile( NULL
, NULL
, &aTmp
.pData
);
887 osl_getSystemPathFromFileURL( aTmp
.pData
, &aSys
.pData
);
892 sal_Bool
PspSalPrinter::StartJob(
893 const rtl::OUString
* pFileName
,
894 const rtl::OUString
& rJobName
,
895 const rtl::OUString
& rAppName
,
899 ImplJobSetup
* pJobSetup
)
901 OSL_TRACE("PspSalPrinter::StartJob");
902 GetSalData()->m_pInstance
->jobStartedPrinterUpdate();
906 m_aFileName
= pFileName
? *pFileName
: rtl::OUString();
907 m_aTmpFile
= rtl::OUString();
909 m_bCollate
= bCollate
;
911 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
914 // in case user did not do anything (m_nCopies=1)
915 // take the default from jobsetup
916 m_aJobData
.m_nCopies
= m_nCopies
;
917 m_aJobData
.setCollate( bCollate
);
922 // check whether this printer is configured as fax
923 sal_Int32 nIndex
= 0;
924 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
925 while( nIndex
!= -1 )
927 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
928 if( ! aToken
.compareToAscii( "fax", 3 ) )
931 m_aTmpFile
= getTmpName();
932 nMode
= S_IRUSR
| S_IWUSR
;
934 ::boost::unordered_map
< ::rtl::OUString
, ::rtl::OUString
, ::rtl::OUStringHash
>::const_iterator it
;
935 it
= pJobSetup
->maValueMap
.find( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FAX#")) );
936 if( it
!= pJobSetup
->maValueMap
.end() )
937 m_aFaxNr
= it
->second
;
940 m_bSwallowFaxNo
= ! aToken
.getToken( 1, '=', nPos
).compareToAscii( "swallow", 7 ) ? true : false;
944 if( ! aToken
.compareToAscii( "pdf=", 4 ) )
947 m_aTmpFile
= getTmpName();
948 nMode
= S_IRUSR
| S_IWUSR
;
950 if( m_aFileName
.isEmpty() )
952 rtl::OUStringBuffer
aFileName( getPdfDir( rInfo
) );
953 aFileName
.append( '/' );
954 aFileName
.append( rJobName
);
955 aFileName
.appendAscii( RTL_CONSTASCII_STRINGPARAM( ".pdf" ) );
956 m_aFileName
= aFileName
.makeStringAndClear();
962 m_aPrinterGfx
.Init( m_aJobData
);
964 return m_aPrintJob
.StartJob( ! m_aTmpFile
.isEmpty() ? m_aTmpFile
: m_aFileName
, nMode
, rJobName
, rAppName
, m_aJobData
, &m_aPrinterGfx
, bDirect
) ? sal_True
: sal_False
;
967 sal_Bool
PspSalPrinter::EndJob()
969 sal_Bool bSuccess
= sal_False
;
970 if( m_bIsPDFWriterJob
)
974 bSuccess
= m_aPrintJob
.EndJob();
975 OSL_TRACE("PspSalPrinter::EndJob %d", bSuccess
);
982 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
983 // sendAFax removes the file after use
984 bSuccess
= sendAFax( m_aFaxNr
, m_aTmpFile
, rInfo
.m_aCommand
);
988 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
989 bSuccess
= createPdf( m_aFileName
, m_aTmpFile
, rInfo
.m_aCommand
);
993 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
997 sal_Bool
PspSalPrinter::AbortJob()
999 sal_Bool bAbort
= m_aPrintJob
.AbortJob() ? sal_True
: sal_False
;
1000 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
1004 SalGraphics
* PspSalPrinter::StartPage( ImplJobSetup
* pJobSetup
, sal_Bool
)
1006 OSL_TRACE("PspSalPrinter::StartPage");
1008 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
1009 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
1010 m_pGraphics
->Init( &m_aJobData
, &m_aPrinterGfx
, m_bFax
? &m_aFaxNr
: NULL
,
1011 m_bSwallowFaxNo
, m_pInfoPrinter
);
1014 // in case user did not do anything (m_nCopies=1)
1015 // take the default from jobsetup
1016 m_aJobData
.m_nCopies
= m_nCopies
;
1017 m_aJobData
.setCollate( m_nCopies
> 1 && m_bCollate
);
1020 m_aPrintJob
.StartPage( m_aJobData
);
1021 m_aPrinterGfx
.Init( m_aPrintJob
);
1026 sal_Bool
PspSalPrinter::EndPage()
1028 sal_Bool bResult
= m_aPrintJob
.EndPage();
1029 m_aPrinterGfx
.Clear();
1030 OSL_TRACE("PspSalPrinter::EndPage");
1031 return bResult
? sal_True
: sal_False
;
1034 sal_uLong
PspSalPrinter::GetErrorCode()
1039 struct PDFNewJobParameters
1042 sal_uInt16 mnPaperBin
;
1044 PDFNewJobParameters( const Size
& i_rSize
= Size(),
1045 sal_uInt16 i_nPaperBin
= 0xffff )
1046 : maPageSize( i_rSize
), mnPaperBin( i_nPaperBin
) {}
1048 bool operator!=(const PDFNewJobParameters
& rComp
) const
1050 Size
aCompLSSize( rComp
.maPageSize
.Height(), rComp
.maPageSize
.Width() );
1052 (maPageSize
!= rComp
.maPageSize
&& maPageSize
!= aCompLSSize
)
1053 || mnPaperBin
!= rComp
.mnPaperBin
1057 bool operator==(const PDFNewJobParameters
& rComp
) const
1059 return ! this->operator!=(rComp
);
1065 rtl::OUString maTmpURL
;
1066 PDFNewJobParameters maParameters
;
1068 PDFPrintFile( const rtl::OUString
& i_rURL
, const PDFNewJobParameters
& i_rNewParameters
)
1069 : maTmpURL( i_rURL
)
1070 , maParameters( i_rNewParameters
) {}
1073 sal_Bool
PspSalPrinter::StartJob( const rtl::OUString
* i_pFileName
, const rtl::OUString
& i_rJobName
, const rtl::OUString
& i_rAppName
,
1074 ImplJobSetup
* i_pSetupData
, vcl::PrinterController
& i_rController
)
1076 OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName
? rtl::OUStringToOString( *i_pFileName
, RTL_TEXTENCODING_UTF8
).getStr() : "<nil>" );
1078 m_bIsPDFWriterJob
= true;
1080 i_rController
.setLastPage( sal_False
);
1084 JobData::constructFromStreamBuffer( i_pSetupData
->mpDriverData
, i_pSetupData
->mnDriverDataLen
, m_aJobData
);
1086 OSL_ASSERT( m_aJobData
.m_nPDFDevice
> 0 );
1087 m_aJobData
.m_nPDFDevice
= 1;
1089 // possibly create one job for collated output
1090 sal_Bool bSinglePrintJobs
= sal_False
;
1091 beans::PropertyValue
* pSingleValue
= i_rController
.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) );
1094 pSingleValue
->Value
>>= bSinglePrintJobs
;
1097 int nCopies
= i_rController
.getPrinter()->GetCopyCount();
1098 bool bCollate
= i_rController
.getPrinter()->IsCollateCopy();
1100 // notify start of real print job
1101 i_rController
.jobStarted();
1103 // setup PDFWriter context
1104 vcl::PDFWriter::PDFWriterContext aContext
;
1105 aContext
.Version
= vcl::PDFWriter::PDF_1_4
;
1106 aContext
.Tagged
= false;
1107 aContext
.EmbedStandardFonts
= true;
1108 aContext
.DocumentLocale
= Application::GetSettings().GetLanguageTag().getLocale();
1109 aContext
.ColorMode
= i_rController
.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
1110 ? vcl::PDFWriter::DrawGreyscale
: vcl::PDFWriter::DrawColor
;
1113 aContext
.DocumentInfo
.Title
= i_rJobName
;
1114 aContext
.DocumentInfo
.Creator
= i_rAppName
;
1115 aContext
.DocumentInfo
.Producer
= i_rAppName
;
1117 // define how we handle metafiles in PDFWriter
1118 vcl::PDFWriter::PlayMetafileContext aMtfContext
;
1119 aMtfContext
.m_bOnlyLosslessCompression
= true;
1121 boost::shared_ptr
<vcl::PDFWriter
> pWriter
;
1122 std::vector
< PDFPrintFile
> aPDFFiles
;
1123 boost::shared_ptr
<Printer
> pPrinter( i_rController
.getPrinter() );
1124 int nAllPages
= i_rController
.getFilteredPageCount();
1125 i_rController
.createProgressDialog();
1126 bool bAborted
= false;
1127 PDFNewJobParameters aLastParm
;
1129 aContext
.DPIx
= pPrinter
->ImplGetDPIX();
1130 aContext
.DPIy
= pPrinter
->ImplGetDPIY();
1131 for( int nPage
= 0; nPage
< nAllPages
&& ! bAborted
; nPage
++ )
1133 if( nPage
== nAllPages
-1 )
1134 i_rController
.setLastPage( sal_True
);
1136 // get the page's metafile
1137 GDIMetaFile aPageFile
;
1138 vcl::PrinterController::PageSize aPageSize
= i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1139 if( i_rController
.isProgressCanceled() )
1142 if( nPage
!= nAllPages
-1 )
1144 i_rController
.createProgressDialog();
1145 i_rController
.setLastPage( sal_True
);
1146 i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1151 pPrinter
->SetMapMode( MapMode( MAP_100TH_MM
) );
1152 pPrinter
->SetPaperSizeUser( aPageSize
.aSize
, true );
1153 PDFNewJobParameters
aNewParm( pPrinter
->GetPaperSize(), pPrinter
->GetPaperBin() );
1155 // create PDF writer on demand
1156 // either on first page
1157 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1158 // so we need to start a new job to get a new paper format from the printer
1159 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1161 (aNewParm
!= aLastParm
&& ! i_pFileName
) )
1170 aPDFUrl
= *i_pFileName
;
1172 osl_createTempFile( NULL
, NULL
, &aPDFUrl
.pData
);
1173 // normalize to file URL
1174 if( aPDFUrl
.compareToAscii( "file:", 5 ) != 0 )
1176 // this is not a file URL, but it should
1177 // form it into a osl friendly file URL
1179 osl_getFileURLFromSystemPath( aPDFUrl
.pData
, &aTmp
.pData
);
1182 // save current file and paper format
1183 aLastParm
= aNewParm
;
1184 aPDFFiles
.push_back( PDFPrintFile( aPDFUrl
, aNewParm
) );
1186 aContext
.URL
= aPDFUrl
;
1188 // create and initialize PDFWriter
1189 #if defined __SUNPRO_CC
1190 #pragma disable_warn
1192 pWriter
.reset( new vcl::PDFWriter( aContext
, uno::Reference
< beans::XMaterialHolder
>() ) );
1193 #if defined __SUNPRO_CC
1198 pWriter
->NewPage( TenMuToPt( aNewParm
.maPageSize
.Width() ),
1199 TenMuToPt( aNewParm
.maPageSize
.Height() ),
1200 vcl::PDFWriter::Portrait
);
1202 pWriter
->PlayMetafile( aPageFile
, aMtfContext
, NULL
);
1206 // emit the last file
1210 // handle collate, copy count and multiple jobs correctly
1212 if( bSinglePrintJobs
)
1214 nOuterJobs
= nCopies
;
1215 m_aJobData
.m_nCopies
= 1;
1221 if( aPDFFiles
.size() == 1 && pPrinter
->HasSupport( SUPPORT_COLLATECOPY
) )
1223 m_aJobData
.setCollate( true );
1224 m_aJobData
.m_nCopies
= nCopies
;
1228 nOuterJobs
= nCopies
;
1229 m_aJobData
.m_nCopies
= 1;
1234 m_aJobData
.setCollate( false );
1235 m_aJobData
.m_nCopies
= nCopies
;
1240 if( ! i_pFileName
&& ! bAborted
)
1242 bool bFirstJob
= true;
1243 for( int nCurJob
= 0; nCurJob
< nOuterJobs
; nCurJob
++ )
1245 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1247 oslFileHandle pFile
= NULL
;
1248 osl_openFile( aPDFFiles
[i
].maTmpURL
.pData
, &pFile
, osl_File_OpenFlag_Read
);
1249 if (pFile
&& (osl_setFilePos(pFile
, osl_Pos_Absolut
, 0) == osl_File_E_None
))
1251 std::vector
< char > buffer( 0x10000, 0 );
1252 // update job data with current page size
1253 Size
aPageSize( aPDFFiles
[i
].maParameters
.maPageSize
);
1254 m_aJobData
.setPaper( TenMuToPt( aPageSize
.Width() ), TenMuToPt( aPageSize
.Height() ) );
1255 // update job data with current paperbin
1256 m_aJobData
.setPaperBin( aPDFFiles
[i
].maParameters
.mnPaperBin
);
1258 // spool current file
1259 FILE* fp
= PrinterInfoManager::get().startSpool( pPrinter
->GetName(), i_rController
.isDirectPrint() );
1262 sal_uInt64 nBytesRead
= 0;
1265 osl_readFile( pFile
, &buffer
[0], buffer
.size(), &nBytesRead
);
1266 if( nBytesRead
> 0 )
1268 size_t nBytesWritten
= fwrite(&buffer
[0], 1, nBytesRead
, fp
);
1269 OSL_ENSURE(nBytesRead
== nBytesWritten
, "short write");
1270 if (nBytesRead
!= nBytesWritten
)
1273 } while( nBytesRead
== buffer
.size() );
1274 rtl::OUStringBuffer
aBuf( i_rJobName
.getLength() + 8 );
1275 aBuf
.append( i_rJobName
);
1276 if( i
> 0 || nCurJob
> 0 )
1278 aBuf
.append( sal_Unicode(' ') );
1279 aBuf
.append( sal_Int32( i
+ nCurJob
* aPDFFiles
.size() ) );
1281 PrinterInfoManager::get().endSpool( pPrinter
->GetName(), aBuf
.makeStringAndClear(), fp
, m_aJobData
, bFirstJob
);
1285 osl_closeFile( pFile
);
1290 // job has been spooled
1291 i_rController
.setJobState( bAborted
? view::PrintableState_JOB_ABORTED
: view::PrintableState_JOB_SPOOLED
);
1293 // clean up the temporary PDF files
1294 if( ! i_pFileName
|| bAborted
)
1296 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1298 osl_removeFile( aPDFFiles
[i
].maTmpURL
.pData
);
1299 OSL_TRACE( "removed print PDF file %s", rtl::OUStringToOString( aPDFFiles
[i
].maTmpURL
, RTL_TEXTENCODING_UTF8
).getStr() );
1309 static Timer
* pPrinterUpdateTimer
;
1310 static int nActiveJobs
;
1312 static void doUpdate();
1313 DECL_STATIC_LINK( PrinterUpdate
, UpdateTimerHdl
, void* );
1315 static void update(SalGenericInstance
&rInstance
);
1316 static void jobStarted() { nActiveJobs
++; }
1317 static void jobEnded();
1320 Timer
* PrinterUpdate::pPrinterUpdateTimer
= NULL
;
1321 int PrinterUpdate::nActiveJobs
= 0;
1323 void PrinterUpdate::doUpdate()
1325 ::psp::PrinterInfoManager
& rManager( ::psp::PrinterInfoManager::get() );
1326 SalGenericInstance
*pInst
= static_cast<SalGenericInstance
*>( GetSalData()->m_pInstance
);
1327 if( pInst
&& rManager
.checkPrintersChanged( false ) )
1328 pInst
->PostPrintersChanged();
1331 // -----------------------------------------------------------------------
1333 IMPL_STATIC_LINK_NOINSTANCE( PrinterUpdate
, UpdateTimerHdl
, void*, EMPTYARG
)
1335 if( nActiveJobs
< 1 )
1338 delete pPrinterUpdateTimer
;
1339 pPrinterUpdateTimer
= NULL
;
1342 pPrinterUpdateTimer
->Start();
1347 void PrinterUpdate::update(SalGenericInstance
&rInstance
)
1349 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1352 if( ! rInstance
.isPrinterInit() )
1354 // #i45389# start background printer detection
1355 psp::PrinterInfoManager::get();
1359 if( nActiveJobs
< 1 )
1361 else if( ! pPrinterUpdateTimer
)
1363 pPrinterUpdateTimer
= new Timer();
1364 pPrinterUpdateTimer
->SetTimeout( 500 );
1365 pPrinterUpdateTimer
->SetTimeoutHdl( STATIC_LINK( NULL
, PrinterUpdate
, UpdateTimerHdl
) );
1366 pPrinterUpdateTimer
->Start();
1370 void SalGenericInstance::updatePrinterUpdate()
1372 PrinterUpdate::update(*this);
1375 void SalGenericInstance::jobStartedPrinterUpdate()
1377 PrinterUpdate::jobStarted();
1380 void PrinterUpdate::jobEnded()
1383 if( nActiveJobs
< 1 )
1385 if( pPrinterUpdateTimer
)
1387 pPrinterUpdateTimer
->Stop();
1388 delete pPrinterUpdateTimer
;
1389 pPrinterUpdateTimer
= NULL
;
1395 void SalGenericInstance::jobEndedPrinterUpdate()
1397 PrinterUpdate::jobEnded();
1400 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */