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
;
68 #if defined( UNX ) && !( defined( MACOSX ) || defined( IOS ) || defined( ANDROID ) )
69 static oslModule driverLib
= NULL
;
73 typedef int(*setupFunction
)(PrinterInfo
&);
74 static setupFunction pSetupFunction
= NULL
;
75 typedef int(*faxFunction
)(OUString
&);
76 static faxFunction pFaxNrFunction
= NULL
;
79 static OUString
getPdfDir( const PrinterInfo
& rInfo
)
85 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
86 if( aToken
.startsWith( "pdf=" ) )
89 aDir
= aToken
.getToken( 1, '=', nPos
);
90 if( aDir
.isEmpty() && getenv( "HOME" ) )
91 aDir
= OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
98 static void getPaLib()
100 #if defined( UNX ) && !( defined( MACOSX ) || defined( IOS ) || defined( ANDROID ) )
103 driverLib
= osl_loadModuleRelativeAscii( (oslGenericFunction
)getPaLib
,
105 SAL_LOADMODULE_DEFAULT
);
109 pSetupFunction
= (setupFunction
)osl_getAsciiFunctionSymbol( driverLib
, "Sal_SetupPrinterDriver" );
110 if ( !pSetupFunction
)
111 fprintf( stderr
, "could not resolve Sal_SetupPrinterDriver\n" );
113 pFaxNrFunction
= (faxFunction
)osl_getAsciiFunctionSymbol( driverLib
, "Sal_queryFaxNumber" );
114 if ( !pFaxNrFunction
)
115 fprintf( stderr
, "could not resolve Sal_queryFaxNumber\n" );
120 inline int PtTo10Mu( int nPoints
) { return (int)((((double)nPoints
)*35.27777778)+0.5); }
122 inline int TenMuToPt( int nUnits
) { return (int)((((double)nUnits
)/35.27777778)+0.5); }
124 static void copyJobDataToJobSetup( ImplJobSetup
* pJobSetup
, JobData
& rData
)
126 pJobSetup
->meOrientation
= (Orientation
)(rData
.m_eOrientation
== orientation::Landscape
? ORIENTATION_LANDSCAPE
: ORIENTATION_PORTRAIT
);
132 rData
.m_aContext
.getPageSize( aPaper
, width
, height
);
133 pJobSetup
->mePaperFormat
= PaperInfo::fromPSName(OUStringToOString( aPaper
, RTL_TEXTENCODING_ISO_8859_1
));
135 pJobSetup
->mnPaperWidth
= 0;
136 pJobSetup
->mnPaperHeight
= 0;
137 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
139 // transform to 100dth mm
140 width
= PtTo10Mu( width
);
141 height
= PtTo10Mu( height
);
143 if( rData
.m_eOrientation
== psp::orientation::Portrait
)
145 pJobSetup
->mnPaperWidth
= width
;
146 pJobSetup
->mnPaperHeight
= height
;
150 pJobSetup
->mnPaperWidth
= height
;
151 pJobSetup
->mnPaperHeight
= width
;
156 const PPDKey
* pKey
= NULL
;
157 const PPDValue
* pValue
= NULL
;
159 pJobSetup
->mnPaperBin
= 0;
160 if( rData
.m_pParser
)
161 pKey
= rData
.m_pParser
->getKey( OUString("InputSlot") );
163 pValue
= rData
.m_aContext
.getValue( pKey
);
166 for( pJobSetup
->mnPaperBin
= 0;
167 pValue
!= pKey
->getValue( pJobSetup
->mnPaperBin
) &&
168 pJobSetup
->mnPaperBin
< pKey
->countValues();
169 pJobSetup
->mnPaperBin
++ )
171 if( pJobSetup
->mnPaperBin
>= pKey
->countValues() )
172 pJobSetup
->mnPaperBin
= 0;
179 pJobSetup
->meDuplexMode
= DUPLEX_UNKNOWN
;
180 if( rData
.m_pParser
)
181 pKey
= rData
.m_pParser
->getKey( OUString("Duplex") );
183 pValue
= rData
.m_aContext
.getValue( pKey
);
186 if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "None" ) ||
187 pValue
->m_aOption
.EqualsIgnoreCaseAscii( "Simplex", 0, 7 )
190 pJobSetup
->meDuplexMode
= DUPLEX_OFF
;
192 else if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) )
194 pJobSetup
->meDuplexMode
= DUPLEX_LONGEDGE
;
196 else if( pValue
->m_aOption
.EqualsIgnoreCaseAscii( "DuplexTumble" ) )
198 pJobSetup
->meDuplexMode
= DUPLEX_SHORTEDGE
;
202 // copy the whole context
203 if( pJobSetup
->mpDriverData
)
204 rtl_freeMemory( pJobSetup
->mpDriverData
);
207 void* pBuffer
= NULL
;
208 if( rData
.getStreamBuffer( pBuffer
, nBytes
) )
210 pJobSetup
->mnDriverDataLen
= nBytes
;
211 pJobSetup
->mpDriverData
= (sal_uInt8
*)pBuffer
;
215 pJobSetup
->mnDriverDataLen
= 0;
216 pJobSetup
->mpDriverData
= NULL
;
220 // Needs a cleaner abstraction ...
222 static bool passFileToCommandLine( const OUString
& rFilename
, const OUString
& rCommandLine
, bool bRemoveFile
= true )
224 bool bSuccess
= false;
226 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
227 OString
aCmdLine(OUStringToOString(rCommandLine
, aEncoding
));
228 OString
aFilename(OUStringToOString(rFilename
, aEncoding
));
230 bool bPipe
= aCmdLine
.indexOf( "(TMP)" ) != -1 ? false : true;
232 // setup command line for exec
234 aCmdLine
= aCmdLine
.replaceAll(OString("(TMP)"), aFilename
);
236 #if OSL_DEBUG_LEVEL > 1
237 fprintf( stderr
, "%s commandline: \"%s\"\n",
238 bPipe
? "piping to" : "executing",
241 if( stat( aFilename
.getStr(), &aStat
) )
242 fprintf( stderr
, "stat( %s ) failed\n", aFilename
.getStr() );
243 fprintf( stderr
, "Tmp file %s has modes: 0%03lo\n", aFilename
.getStr(), (long)aStat
.st_mode
);
246 if( ! ( argv
[ 0 ] = getenv( "SHELL" ) ) )
247 argv
[ 0 ] = "/bin/sh";
249 argv
[ 2 ] = aCmdLine
.getStr();
252 bool bHavePipes
= false;
256 bHavePipes
= pipe( fd
) ? false : true;
257 if( ( pid
= fork() ) > 0 )
259 if( bPipe
&& bHavePipes
)
262 char aBuffer
[ 2048 ];
263 FILE* fp
= fopen( aFilename
.getStr(), "r" );
264 while (fp
&& !feof(fp
))
266 size_t nBytesRead
= fread(aBuffer
, 1, sizeof( aBuffer
), fp
);
269 size_t nBytesWritten
= write(fd
[1], aBuffer
, nBytesRead
);
270 OSL_ENSURE(nBytesWritten
== nBytesRead
, "short write");
271 if (nBytesWritten
!= nBytesRead
)
279 waitpid( pid
, &status
, 0 );
285 if( bPipe
&& bHavePipes
)
288 if( fd
[0] != STDIN_FILENO
) // not probable, but who knows :)
289 dup2( fd
[0], STDIN_FILENO
);
291 execv( argv
[0], const_cast<char**>(argv
) );
292 fprintf( stderr
, "failed to execute \"%s\"\n", aCmdLine
.getStr() );
296 fprintf( stderr
, "failed to fork\n" );
300 unlink( aFilename
.getStr() );
306 static bool sendAFax( const OUString
& rFaxNumber
, const OUString
& rFileName
, const OUString
& rCommand
)
309 std::list
< OUString
> aFaxNumbers
;
311 if( rFaxNumber
.isEmpty() )
317 if( pFaxNrFunction( aNewNr
) )
318 aFaxNumbers
.push_back( aNewNr
);
323 sal_Int32 nIndex
= 0;
324 OUString
aFaxes( rFaxNumber
);
325 OUString
aBeginToken( "<Fax#>" );
326 OUString
aEndToken( "</Fax#>" );
327 while( nIndex
!= -1 )
329 nIndex
= aFaxes
.indexOf( aBeginToken
, nIndex
);
332 sal_Int32 nBegin
= nIndex
+ aBeginToken
.getLength();
333 nIndex
= aFaxes
.indexOf( aEndToken
, nIndex
);
336 aFaxNumbers
.push_back( aFaxes
.copy( nBegin
, nIndex
-nBegin
) );
337 nIndex
+= aEndToken
.getLength();
343 bool bSuccess
= true;
344 if( aFaxNumbers
.begin() != aFaxNumbers
.end() )
346 while( aFaxNumbers
.begin() != aFaxNumbers
.end() && bSuccess
)
348 OUString
aFaxNumber( aFaxNumbers
.front() );
349 aFaxNumbers
.pop_front();
351 rCommand
.replaceAll("(PHONE)", aFaxNumber
));
352 #if OSL_DEBUG_LEVEL > 1
353 fprintf( stderr
, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber
, osl_getThreadTextEncoding() ).getStr() );
355 bSuccess
= passFileToCommandLine( rFileName
, aCmdLine
, false );
361 // clean up temp file
362 unlink(OUStringToOString(rFileName
, osl_getThreadTextEncoding()).getStr());
366 (void)rFaxNumber
; (void)rFileName
; (void)rCommand
;
371 static bool createPdf( const OUString
& rToFile
, const OUString
& rFromFile
, const OUString
& rCommandLine
)
374 OUString
aCommandLine(
375 rCommandLine
.replaceAll("(OUTFILE)", rToFile
));
377 return passFileToCommandLine( rFromFile
, aCommandLine
);
379 (void)rToFile
; (void)rFromFile
; (void)rCommandLine
;
388 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter
*pPrinter
,
389 SalPrinterQueueInfo
* pQueueInfo
, ImplJobSetup
* pJobSetup
)
393 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
394 PrinterInfo
aInfo( rManager
.getPrinterInfo( pQueueInfo
->maPrinterName
) );
395 pPrinter
->m_aJobData
= aInfo
;
396 pPrinter
->m_aPrinterGfx
.Init( pPrinter
->m_aJobData
);
398 if( pJobSetup
->mpDriverData
)
399 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
401 pJobSetup
->mnSystem
= JOBSETUP_SYSTEM_UNIX
;
402 pJobSetup
->maPrinterName
= pQueueInfo
->maPrinterName
;
403 pJobSetup
->maDriver
= aInfo
.m_aDriverName
;
404 copyJobDataToJobSetup( pJobSetup
, aInfo
);
408 SalInfoPrinter
* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
409 ImplJobSetup
* pJobSetup
)
411 mbPrinterInit
= true;
412 // create and initialize SalInfoPrinter
413 PspSalInfoPrinter
* pPrinter
= new PspSalInfoPrinter();
414 configurePspInfoPrinter(pPrinter
, pQueueInfo
, pJobSetup
);
418 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
423 SalPrinter
* SalGenericInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
425 mbPrinterInit
= true;
426 // create and initialize SalPrinter
427 PspSalPrinter
* pPrinter
= new PspSalPrinter( pInfoPrinter
);
428 pPrinter
->m_aJobData
= static_cast<PspSalInfoPrinter
*>(pInfoPrinter
)->m_aJobData
;
433 void SalGenericInstance::DestroyPrinter( SalPrinter
* pPrinter
)
438 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
440 mbPrinterInit
= true;
441 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
442 static const char* pNoSyncDetection
= getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
443 if( ! pNoSyncDetection
|| ! *pNoSyncDetection
)
445 // #i62663# synchronize possible asynchronouse printer detection now
446 rManager
.checkPrintersChanged( true );
448 ::std::list
< OUString
> aPrinters
;
449 rManager
.listPrinters( aPrinters
);
451 for( ::std::list
< OUString
>::iterator it
= aPrinters
.begin(); it
!= aPrinters
.end(); ++it
)
453 const PrinterInfo
& rInfo( rManager
.getPrinterInfo( *it
) );
454 // Neuen Eintrag anlegen
455 SalPrinterQueueInfo
* pInfo
= new SalPrinterQueueInfo
;
456 pInfo
->maPrinterName
= *it
;
457 pInfo
->maDriver
= rInfo
.m_aDriverName
;
458 pInfo
->maLocation
= rInfo
.m_aLocation
;
459 pInfo
->maComment
= rInfo
.m_aComment
;
460 pInfo
->mpSysData
= NULL
;
462 sal_Int32 nIndex
= 0;
463 while( nIndex
!= -1 )
465 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
466 if( aToken
.match( "pdf=" ) )
468 pInfo
->maLocation
= getPdfDir( rInfo
);
477 void SalGenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo
* pInfo
)
482 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
484 mbPrinterInit
= true;
487 OUString
SalGenericInstance::GetDefaultPrinter()
489 mbPrinterInit
= true;
490 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
491 return rManager
.getDefaultPrinter();
494 PspSalInfoPrinter::PspSalInfoPrinter()
495 : m_pGraphics( NULL
)
499 PspSalInfoPrinter::~PspSalInfoPrinter()
508 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup
* )
510 m_aPaperFormats
.clear();
511 m_bPapersInit
= true;
513 if( m_aJobData
.m_pParser
)
515 const PPDKey
* pKey
= m_aJobData
.m_pParser
->getKey( OUString("PageSize") );
518 int nValues
= pKey
->countValues();
519 for( int i
= 0; i
< nValues
; i
++ )
521 const PPDValue
* pValue
= pKey
->getValue( i
);
522 int nWidth
= 0, nHeight
= 0;
523 m_aJobData
.m_pParser
->getPaperDimension( pValue
->m_aOption
, nWidth
, nHeight
);
524 PaperInfo
aInfo(PtTo10Mu( nWidth
), PtTo10Mu( nHeight
));
525 m_aPaperFormats
.push_back( aInfo
);
531 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup
* )
536 SalGraphics
* PspSalInfoPrinter::GetGraphics()
538 // return a valid pointer only once
539 // the reasoning behind this is that we could have different
540 // SalGraphics that can run in multiple threads
542 SalGraphics
* pRet
= NULL
;
545 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
546 m_pGraphics
->Init( &m_aJobData
, &m_aPrinterGfx
, NULL
, false, this );
552 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics
* pGraphics
)
554 if( pGraphics
== m_pGraphics
)
562 sal_Bool
PspSalInfoPrinter::Setup( SalFrame
* pFrame
, ImplJobSetup
* pJobSetup
)
564 if( ! pFrame
|| ! pJobSetup
)
569 if( ! pSetupFunction
)
572 PrinterInfoManager
& rManager
= PrinterInfoManager::get();
574 PrinterInfo
aInfo( rManager
.getPrinterInfo( pJobSetup
->maPrinterName
) );
575 if ( pJobSetup
->mpDriverData
)
577 SetData( ~0, pJobSetup
);
578 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aInfo
);
581 if( pSetupFunction( aInfo
) )
583 aInfo
.resolveDefaultBackend();
584 rtl_freeMemory( pJobSetup
->mpDriverData
);
585 pJobSetup
->mpDriverData
= NULL
;
588 void* pBuffer
= NULL
;
589 aInfo
.getStreamBuffer( pBuffer
, nBytes
);
590 pJobSetup
->mnDriverDataLen
= nBytes
;
591 pJobSetup
->mpDriverData
= (sal_uInt8
*)pBuffer
;
593 // copy everything to job setup
594 copyJobDataToJobSetup( pJobSetup
, aInfo
);
595 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
601 // This function gets the driver data and puts it into pJobSetup
602 // If pJobSetup->mpDriverData is NOT NULL, then the independend
603 // data should be merged into the driver data
604 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
605 // should be merged into the independent data
606 sal_Bool
PspSalInfoPrinter::SetPrinterData( ImplJobSetup
* pJobSetup
)
608 if( pJobSetup
->mpDriverData
)
609 return SetData( ~0, pJobSetup
);
611 copyJobDataToJobSetup( pJobSetup
, m_aJobData
);
616 // This function merges the independ driver data
617 // and sets the new independ data in pJobSetup
618 // Only the data must be changed, where the bit
619 // in nGetDataFlags is set
620 sal_Bool
PspSalInfoPrinter::SetData(
621 sal_uLong nSetDataFlags
,
622 ImplJobSetup
* pJobSetup
)
625 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
627 if( aData
.m_pParser
)
630 const PPDValue
* pValue
;
632 // merge papersize if necessary
633 if( nSetDataFlags
& SAL_JOBSET_PAPERSIZE
)
637 if( pJobSetup
->mePaperFormat
== PAPER_USER
)
638 aPaper
= aData
.m_pParser
->matchPaper(
639 TenMuToPt( pJobSetup
->mnPaperWidth
),
640 TenMuToPt( pJobSetup
->mnPaperHeight
) );
642 aPaper
= OStringToOUString(PaperInfo::toPSName(pJobSetup
->mePaperFormat
), RTL_TEXTENCODING_ISO_8859_1
);
644 pKey
= aData
.m_pParser
->getKey( OUString("PageSize") );
645 pValue
= pKey
? pKey
->getValueCaseInsensitive( aPaper
) : NULL
;
647 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
648 // try to find the correct paper anyway using the size
649 if( pKey
&& ! pValue
&& pJobSetup
->mePaperFormat
!= PAPER_USER
)
651 PaperInfo
aInfo( pJobSetup
->mePaperFormat
);
652 aPaper
= aData
.m_pParser
->matchPaper(
653 TenMuToPt( aInfo
.getWidth() ),
654 TenMuToPt( aInfo
.getHeight() ) );
655 pValue
= pKey
->getValueCaseInsensitive( aPaper
);
658 if( ! ( pKey
&& pValue
&& aData
.m_aContext
.setValue( pKey
, pValue
, false ) == pValue
) )
662 // merge paperbin if necessary
663 if( nSetDataFlags
& SAL_JOBSET_PAPERBIN
)
665 pKey
= aData
.m_pParser
->getKey( OUString("InputSlot") );
668 int nPaperBin
= pJobSetup
->mnPaperBin
;
669 if( nPaperBin
>= pKey
->countValues() )
670 pValue
= pKey
->getDefaultValue();
672 pValue
= pKey
->getValue( pJobSetup
->mnPaperBin
);
674 // may fail due to constraints;
675 // real paper bin is copied back to jobsetup in that case
676 aData
.m_aContext
.setValue( pKey
, pValue
);
678 // if printer has no InputSlot key simply ignore this setting
679 // (e.g. SGENPRT has no InputSlot)
682 // merge orientation if necessary
683 if( nSetDataFlags
& SAL_JOBSET_ORIENTATION
)
684 aData
.m_eOrientation
= pJobSetup
->meOrientation
== ORIENTATION_LANDSCAPE
? orientation::Landscape
: orientation::Portrait
;
686 // merge duplex if necessary
687 if( nSetDataFlags
& SAL_JOBSET_DUPLEXMODE
)
689 pKey
= aData
.m_pParser
->getKey( OUString("Duplex") );
693 switch( pJobSetup
->meDuplexMode
)
696 pValue
= pKey
->getValue( OUString("None") );
698 pValue
= pKey
->getValue( OUString("SimplexNoTumble") );
700 case DUPLEX_SHORTEDGE
:
701 pValue
= pKey
->getValue( OUString("DuplexTumble") );
703 case DUPLEX_LONGEDGE
:
704 pValue
= pKey
->getValue( OUString("DuplexNoTumble") );
712 pValue
= pKey
->getDefaultValue();
713 aData
.m_aContext
.setValue( pKey
, pValue
);
718 copyJobDataToJobSetup( pJobSetup
, aData
);
725 void PspSalInfoPrinter::GetPageInfo(
726 const ImplJobSetup
* pJobSetup
,
727 long& rOutWidth
, long& rOutHeight
,
728 long& rPageOffX
, long& rPageOffY
,
729 long& rPageWidth
, long& rPageHeight
)
735 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
737 // get the selected page size
738 if( aData
.m_pParser
)
743 int left
= 0, top
= 0, right
= 0, bottom
= 0;
744 int nDPI
= aData
.m_aContext
.getRenderResolution();
747 if( aData
.m_eOrientation
== psp::orientation::Portrait
)
749 aData
.m_aContext
.getPageSize( aPaper
, width
, height
);
750 aData
.m_pParser
->getMargins( aPaper
, left
, right
, top
, bottom
);
754 aData
.m_aContext
.getPageSize( aPaper
, height
, width
);
755 aData
.m_pParser
->getMargins( aPaper
, top
, bottom
, right
, left
);
758 rPageWidth
= width
* nDPI
/ 72;
759 rPageHeight
= height
* nDPI
/ 72;
760 rPageOffX
= left
* nDPI
/ 72;
761 rPageOffY
= top
* nDPI
/ 72;
762 rOutWidth
= ( width
- left
- right
) * nDPI
/ 72;
763 rOutHeight
= ( height
- top
- bottom
) * nDPI
/ 72;
767 sal_uLong
PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup
* pJobSetup
)
773 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
775 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString("InputSlot") ): NULL
;
776 return pKey
? pKey
->countValues() : 0;
779 OUString
PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup
* pJobSetup
, sal_uLong nPaperBin
)
782 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
785 if( aData
.m_pParser
)
787 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString("InputSlot") ): NULL
;
788 if( ! pKey
|| nPaperBin
>= (sal_uLong
)pKey
->countValues() )
789 aRet
= aData
.m_pParser
->getDefaultInputSlot();
792 const PPDValue
* pValue
= pKey
->getValue( nPaperBin
);
794 aRet
= aData
.m_pParser
->translateOption( pKey
->getKey(), pValue
->m_aOption
);
801 sal_uLong
PspSalInfoPrinter::GetCapabilities( const ImplJobSetup
* pJobSetup
, sal_uInt16 nType
)
805 case PRINTER_CAPABILITIES_SUPPORTDIALOG
:
807 case PRINTER_CAPABILITIES_COPIES
:
809 case PRINTER_CAPABILITIES_COLLATECOPIES
:
811 // see if the PPD contains a value to set Collate to True
813 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
815 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( OUString("Collate") ) : NULL
;
816 const PPDValue
* pVal
= pKey
? pKey
->getValue(OUString("True")) : NULL
;
818 // PPDs don't mention the number of possible collated copies.
819 // so let's guess as many as we want ?
820 return pVal
? 0xffff : 0;
822 case PRINTER_CAPABILITIES_SETORIENTATION
:
824 case PRINTER_CAPABILITIES_SETDUPLEX
:
826 case PRINTER_CAPABILITIES_SETPAPERBIN
:
828 case PRINTER_CAPABILITIES_SETPAPERSIZE
:
830 case PRINTER_CAPABILITIES_SETPAPER
:
832 case PRINTER_CAPABILITIES_FAX
:
833 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "fax" ) ? 1 : 0;
834 case PRINTER_CAPABILITIES_PDF
:
835 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "pdf" ) )
839 // see if the PPD contains a value to set Collate to True
840 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
841 if( pJobSetup
->mpDriverData
)
842 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
843 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
845 case PRINTER_CAPABILITIES_EXTERNALDIALOG
:
846 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->maPrinterName
, "external_dialog" ) ? 1 : 0;
847 case PRINTER_CAPABILITIES_USEPULLMODEL
:
849 // see if the PPD contains a value to set Collate to True
850 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->maPrinterName
);
851 if( pJobSetup
->mpDriverData
)
852 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, aData
);
853 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
863 PspSalPrinter::PspSalPrinter( SalInfoPrinter
* pInfoPrinter
)
866 m_bSwallowFaxNo( false ),
867 m_bIsPDFWriterJob( false ),
871 m_pInfoPrinter( pInfoPrinter
)
875 PspSalPrinter::~PspSalPrinter()
879 static OUString
getTmpName()
882 osl_createTempFile( NULL
, NULL
, &aTmp
.pData
);
883 osl_getSystemPathFromFileURL( aTmp
.pData
, &aSys
.pData
);
888 sal_Bool
PspSalPrinter::StartJob(
889 const OUString
* pFileName
,
890 const OUString
& rJobName
,
891 const OUString
& rAppName
,
895 ImplJobSetup
* pJobSetup
)
897 OSL_TRACE("PspSalPrinter::StartJob");
898 GetSalData()->m_pInstance
->jobStartedPrinterUpdate();
902 m_aFileName
= pFileName
? *pFileName
: OUString();
903 m_aTmpFile
= OUString();
905 m_bCollate
= bCollate
;
907 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
910 // in case user did not do anything (m_nCopies=1)
911 // take the default from jobsetup
912 m_aJobData
.m_nCopies
= m_nCopies
;
913 m_aJobData
.setCollate( bCollate
);
918 // check whether this printer is configured as fax
919 sal_Int32 nIndex
= 0;
920 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
921 while( nIndex
!= -1 )
923 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
924 if( aToken
.startsWith( "fax" ) )
927 m_aTmpFile
= getTmpName();
928 nMode
= S_IRUSR
| S_IWUSR
;
930 ::boost::unordered_map
< OUString
, OUString
, OUStringHash
>::const_iterator it
;
931 it
= pJobSetup
->maValueMap
.find( OUString("FAX#") );
932 if( it
!= pJobSetup
->maValueMap
.end() )
933 m_aFaxNr
= it
->second
;
936 m_bSwallowFaxNo
= aToken
.getToken( 1, '=', nPos
).startsWith( "swallow" ) ? true : false;
940 if( aToken
.startsWith( "pdf=" ) )
943 m_aTmpFile
= getTmpName();
944 nMode
= S_IRUSR
| S_IWUSR
;
946 if( m_aFileName
.isEmpty() )
948 OUStringBuffer
aFileName( getPdfDir( rInfo
) );
949 aFileName
.append( '/' );
950 aFileName
.append( rJobName
);
951 aFileName
.appendAscii( ".pdf" );
952 m_aFileName
= aFileName
.makeStringAndClear();
958 m_aPrinterGfx
.Init( m_aJobData
);
960 return m_aPrintJob
.StartJob( ! m_aTmpFile
.isEmpty() ? m_aTmpFile
: m_aFileName
, nMode
, rJobName
, rAppName
, m_aJobData
, &m_aPrinterGfx
, bDirect
) ? sal_True
: sal_False
;
963 sal_Bool
PspSalPrinter::EndJob()
965 sal_Bool bSuccess
= sal_False
;
966 if( m_bIsPDFWriterJob
)
970 bSuccess
= m_aPrintJob
.EndJob();
971 OSL_TRACE("PspSalPrinter::EndJob %d", bSuccess
);
978 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
979 // sendAFax removes the file after use
980 bSuccess
= sendAFax( m_aFaxNr
, m_aTmpFile
, rInfo
.m_aCommand
);
984 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
985 bSuccess
= createPdf( m_aFileName
, m_aTmpFile
, rInfo
.m_aCommand
);
989 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
993 sal_Bool
PspSalPrinter::AbortJob()
995 sal_Bool bAbort
= m_aPrintJob
.AbortJob() ? sal_True
: sal_False
;
996 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
1000 SalGraphics
* PspSalPrinter::StartPage( ImplJobSetup
* pJobSetup
, sal_Bool
)
1002 OSL_TRACE("PspSalPrinter::StartPage");
1004 JobData::constructFromStreamBuffer( pJobSetup
->mpDriverData
, pJobSetup
->mnDriverDataLen
, m_aJobData
);
1005 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
1006 m_pGraphics
->Init( &m_aJobData
, &m_aPrinterGfx
, m_bFax
? &m_aFaxNr
: NULL
,
1007 m_bSwallowFaxNo
, m_pInfoPrinter
);
1010 // in case user did not do anything (m_nCopies=1)
1011 // take the default from jobsetup
1012 m_aJobData
.m_nCopies
= m_nCopies
;
1013 m_aJobData
.setCollate( m_nCopies
> 1 && m_bCollate
);
1016 m_aPrintJob
.StartPage( m_aJobData
);
1017 m_aPrinterGfx
.Init( m_aPrintJob
);
1022 sal_Bool
PspSalPrinter::EndPage()
1024 sal_Bool bResult
= m_aPrintJob
.EndPage();
1025 m_aPrinterGfx
.Clear();
1026 OSL_TRACE("PspSalPrinter::EndPage");
1027 return bResult
? sal_True
: sal_False
;
1030 sal_uLong
PspSalPrinter::GetErrorCode()
1035 struct PDFNewJobParameters
1038 sal_uInt16 mnPaperBin
;
1040 PDFNewJobParameters( const Size
& i_rSize
= Size(),
1041 sal_uInt16 i_nPaperBin
= 0xffff )
1042 : maPageSize( i_rSize
), mnPaperBin( i_nPaperBin
) {}
1044 bool operator!=(const PDFNewJobParameters
& rComp
) const
1046 Size
aCompLSSize( rComp
.maPageSize
.Height(), rComp
.maPageSize
.Width() );
1048 (maPageSize
!= rComp
.maPageSize
&& maPageSize
!= aCompLSSize
)
1049 || mnPaperBin
!= rComp
.mnPaperBin
1053 bool operator==(const PDFNewJobParameters
& rComp
) const
1055 return ! this->operator!=(rComp
);
1062 PDFNewJobParameters maParameters
;
1064 PDFPrintFile( const OUString
& i_rURL
, const PDFNewJobParameters
& i_rNewParameters
)
1065 : maTmpURL( i_rURL
)
1066 , maParameters( i_rNewParameters
) {}
1069 sal_Bool
PspSalPrinter::StartJob( const OUString
* i_pFileName
, const OUString
& i_rJobName
, const OUString
& i_rAppName
,
1070 ImplJobSetup
* i_pSetupData
, vcl::PrinterController
& i_rController
)
1072 OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName
? OUStringToOString( *i_pFileName
, RTL_TEXTENCODING_UTF8
).getStr() : "<nil>" );
1074 m_bIsPDFWriterJob
= true;
1076 i_rController
.setLastPage( sal_False
);
1080 JobData::constructFromStreamBuffer( i_pSetupData
->mpDriverData
, i_pSetupData
->mnDriverDataLen
, m_aJobData
);
1082 OSL_ASSERT( m_aJobData
.m_nPDFDevice
> 0 );
1083 m_aJobData
.m_nPDFDevice
= 1;
1085 // possibly create one job for collated output
1086 sal_Bool bSinglePrintJobs
= sal_False
;
1087 beans::PropertyValue
* pSingleValue
= i_rController
.getValue( OUString( "PrintCollateAsSingleJobs" ) );
1090 pSingleValue
->Value
>>= bSinglePrintJobs
;
1093 int nCopies
= i_rController
.getPrinter()->GetCopyCount();
1094 bool bCollate
= i_rController
.getPrinter()->IsCollateCopy();
1096 // notify start of real print job
1097 i_rController
.jobStarted();
1099 // setup PDFWriter context
1100 vcl::PDFWriter::PDFWriterContext aContext
;
1101 aContext
.Version
= vcl::PDFWriter::PDF_1_4
;
1102 aContext
.Tagged
= false;
1103 aContext
.EmbedStandardFonts
= true;
1104 aContext
.DocumentLocale
= Application::GetSettings().GetLanguageTag().getLocale();
1105 aContext
.ColorMode
= i_rController
.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
1106 ? vcl::PDFWriter::DrawGreyscale
: vcl::PDFWriter::DrawColor
;
1109 aContext
.DocumentInfo
.Title
= i_rJobName
;
1110 aContext
.DocumentInfo
.Creator
= i_rAppName
;
1111 aContext
.DocumentInfo
.Producer
= i_rAppName
;
1113 // define how we handle metafiles in PDFWriter
1114 vcl::PDFWriter::PlayMetafileContext aMtfContext
;
1115 aMtfContext
.m_bOnlyLosslessCompression
= true;
1117 boost::shared_ptr
<vcl::PDFWriter
> pWriter
;
1118 std::vector
< PDFPrintFile
> aPDFFiles
;
1119 boost::shared_ptr
<Printer
> pPrinter( i_rController
.getPrinter() );
1120 int nAllPages
= i_rController
.getFilteredPageCount();
1121 i_rController
.createProgressDialog();
1122 bool bAborted
= false;
1123 PDFNewJobParameters aLastParm
;
1125 aContext
.DPIx
= pPrinter
->ImplGetDPIX();
1126 aContext
.DPIy
= pPrinter
->ImplGetDPIY();
1127 for( int nPage
= 0; nPage
< nAllPages
&& ! bAborted
; nPage
++ )
1129 if( nPage
== nAllPages
-1 )
1130 i_rController
.setLastPage( sal_True
);
1132 // get the page's metafile
1133 GDIMetaFile aPageFile
;
1134 vcl::PrinterController::PageSize aPageSize
= i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1135 if( i_rController
.isProgressCanceled() )
1138 if( nPage
!= nAllPages
-1 )
1140 i_rController
.createProgressDialog();
1141 i_rController
.setLastPage( sal_True
);
1142 i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1147 pPrinter
->SetMapMode( MapMode( MAP_100TH_MM
) );
1148 pPrinter
->SetPaperSizeUser( aPageSize
.aSize
, true );
1149 PDFNewJobParameters
aNewParm( pPrinter
->GetPaperSize(), pPrinter
->GetPaperBin() );
1151 // create PDF writer on demand
1152 // either on first page
1153 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1154 // so we need to start a new job to get a new paper format from the printer
1155 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1157 (aNewParm
!= aLastParm
&& ! i_pFileName
) )
1166 aPDFUrl
= *i_pFileName
;
1168 osl_createTempFile( NULL
, NULL
, &aPDFUrl
.pData
);
1169 // normalize to file URL
1170 if( !aPDFUrl
.startsWith( "file:" ) )
1172 // this is not a file URL, but it should
1173 // form it into a osl friendly file URL
1175 osl_getFileURLFromSystemPath( aPDFUrl
.pData
, &aTmp
.pData
);
1178 // save current file and paper format
1179 aLastParm
= aNewParm
;
1180 aPDFFiles
.push_back( PDFPrintFile( aPDFUrl
, aNewParm
) );
1182 aContext
.URL
= aPDFUrl
;
1184 // create and initialize PDFWriter
1185 #if defined __SUNPRO_CC
1186 #pragma disable_warn
1188 pWriter
.reset( new vcl::PDFWriter( aContext
, uno::Reference
< beans::XMaterialHolder
>() ) );
1189 #if defined __SUNPRO_CC
1194 pWriter
->NewPage( TenMuToPt( aNewParm
.maPageSize
.Width() ),
1195 TenMuToPt( aNewParm
.maPageSize
.Height() ),
1196 vcl::PDFWriter::Portrait
);
1198 pWriter
->PlayMetafile( aPageFile
, aMtfContext
, NULL
);
1202 // emit the last file
1206 // handle collate, copy count and multiple jobs correctly
1208 if( bSinglePrintJobs
)
1210 nOuterJobs
= nCopies
;
1211 m_aJobData
.m_nCopies
= 1;
1217 if( aPDFFiles
.size() == 1 && pPrinter
->HasSupport( SUPPORT_COLLATECOPY
) )
1219 m_aJobData
.setCollate( true );
1220 m_aJobData
.m_nCopies
= nCopies
;
1224 nOuterJobs
= nCopies
;
1225 m_aJobData
.m_nCopies
= 1;
1230 m_aJobData
.setCollate( false );
1231 m_aJobData
.m_nCopies
= nCopies
;
1236 if( ! i_pFileName
&& ! bAborted
)
1238 bool bFirstJob
= true;
1239 for( int nCurJob
= 0; nCurJob
< nOuterJobs
; nCurJob
++ )
1241 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1243 oslFileHandle pFile
= NULL
;
1244 osl_openFile( aPDFFiles
[i
].maTmpURL
.pData
, &pFile
, osl_File_OpenFlag_Read
);
1245 if (pFile
&& (osl_setFilePos(pFile
, osl_Pos_Absolut
, 0) == osl_File_E_None
))
1247 std::vector
< char > buffer( 0x10000, 0 );
1248 // update job data with current page size
1249 Size
aPageSize( aPDFFiles
[i
].maParameters
.maPageSize
);
1250 m_aJobData
.setPaper( TenMuToPt( aPageSize
.Width() ), TenMuToPt( aPageSize
.Height() ) );
1251 // update job data with current paperbin
1252 m_aJobData
.setPaperBin( aPDFFiles
[i
].maParameters
.mnPaperBin
);
1254 // spool current file
1255 FILE* fp
= PrinterInfoManager::get().startSpool( pPrinter
->GetName(), i_rController
.isDirectPrint() );
1258 sal_uInt64 nBytesRead
= 0;
1261 osl_readFile( pFile
, &buffer
[0], buffer
.size(), &nBytesRead
);
1262 if( nBytesRead
> 0 )
1264 size_t nBytesWritten
= fwrite(&buffer
[0], 1, nBytesRead
, fp
);
1265 OSL_ENSURE(nBytesRead
== nBytesWritten
, "short write");
1266 if (nBytesRead
!= nBytesWritten
)
1269 } while( nBytesRead
== buffer
.size() );
1270 OUStringBuffer
aBuf( i_rJobName
.getLength() + 8 );
1271 aBuf
.append( i_rJobName
);
1272 if( i
> 0 || nCurJob
> 0 )
1274 aBuf
.append( sal_Unicode(' ') );
1275 aBuf
.append( sal_Int32( i
+ nCurJob
* aPDFFiles
.size() ) );
1277 PrinterInfoManager::get().endSpool( pPrinter
->GetName(), aBuf
.makeStringAndClear(), fp
, m_aJobData
, bFirstJob
);
1281 osl_closeFile( pFile
);
1286 // job has been spooled
1287 i_rController
.setJobState( bAborted
? view::PrintableState_JOB_ABORTED
: view::PrintableState_JOB_SPOOLED
);
1289 // clean up the temporary PDF files
1290 if( ! i_pFileName
|| bAborted
)
1292 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1294 osl_removeFile( aPDFFiles
[i
].maTmpURL
.pData
);
1295 OSL_TRACE( "removed print PDF file %s", OUStringToOString( aPDFFiles
[i
].maTmpURL
, RTL_TEXTENCODING_UTF8
).getStr() );
1305 static Timer
* pPrinterUpdateTimer
;
1306 static int nActiveJobs
;
1308 static void doUpdate();
1309 DECL_STATIC_LINK( PrinterUpdate
, UpdateTimerHdl
, void* );
1311 static void update(SalGenericInstance
&rInstance
);
1312 static void jobStarted() { nActiveJobs
++; }
1313 static void jobEnded();
1316 Timer
* PrinterUpdate::pPrinterUpdateTimer
= NULL
;
1317 int PrinterUpdate::nActiveJobs
= 0;
1319 void PrinterUpdate::doUpdate()
1321 ::psp::PrinterInfoManager
& rManager( ::psp::PrinterInfoManager::get() );
1322 SalGenericInstance
*pInst
= static_cast<SalGenericInstance
*>( GetSalData()->m_pInstance
);
1323 if( pInst
&& rManager
.checkPrintersChanged( false ) )
1324 pInst
->PostPrintersChanged();
1327 // -----------------------------------------------------------------------
1329 IMPL_STATIC_LINK_NOINSTANCE( PrinterUpdate
, UpdateTimerHdl
, void*, EMPTYARG
)
1331 if( nActiveJobs
< 1 )
1334 delete pPrinterUpdateTimer
;
1335 pPrinterUpdateTimer
= NULL
;
1338 pPrinterUpdateTimer
->Start();
1343 void PrinterUpdate::update(SalGenericInstance
&rInstance
)
1345 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1348 if( ! rInstance
.isPrinterInit() )
1350 // #i45389# start background printer detection
1351 psp::PrinterInfoManager::get();
1355 if( nActiveJobs
< 1 )
1357 else if( ! pPrinterUpdateTimer
)
1359 pPrinterUpdateTimer
= new Timer();
1360 pPrinterUpdateTimer
->SetTimeout( 500 );
1361 pPrinterUpdateTimer
->SetTimeoutHdl( STATIC_LINK( NULL
, PrinterUpdate
, UpdateTimerHdl
) );
1362 pPrinterUpdateTimer
->Start();
1366 void SalGenericInstance::updatePrinterUpdate()
1368 PrinterUpdate::update(*this);
1371 void SalGenericInstance::jobStartedPrinterUpdate()
1373 PrinterUpdate::jobStarted();
1376 void PrinterUpdate::jobEnded()
1379 if( nActiveJobs
< 1 )
1381 if( pPrinterUpdateTimer
)
1383 pPrinterUpdateTimer
->Stop();
1384 delete pPrinterUpdateTimer
;
1385 pPrinterUpdateTimer
= NULL
;
1391 void SalGenericInstance::jobEndedPrinterUpdate()
1393 PrinterUpdate::jobEnded();
1396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */