xref: /aoo41x/main/vcl/unx/generic/gdi/salprnpsp.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 /**
32   this file implements the sal printer interface ( SalPrinter, SalInfoPrinter
33   and some printer relevant methods of SalInstance and SalGraphicsData )
34 
35   as aunderlying library the printer features of psprint are used.
36 
37   The query methods of a SalInfoPrinter are implemented by querying psprint
38 
39   The job methods of a SalPrinter are implemented by calling psprint
40   printer job functions.
41  */
42 
43 #include <unistd.h>
44 #include <sys/wait.h>
45 #include <sys/stat.h>
46 
47 #include "rtl/ustring.hxx"
48 
49 #include "osl/module.h"
50 
51 #include "vcl/svapp.hxx"
52 #include "vcl/print.hxx"
53 #include "vcl/pdfwriter.hxx"
54 #include "vcl/printerinfomanager.hxx"
55 
56 #include <unx/salunx.h>
57 #include "unx/saldisp.hxx"
58 #include "unx/salinst.h"
59 #include "unx/salprn.h"
60 #include "unx/salframe.h"
61 #include "unx/pspgraphics.h"
62 #include "unx/saldata.hxx"
63 
64 #include "jobset.h"
65 #include "print.h"
66 #include "salptype.hxx"
67 
68 using namespace psp;
69 using namespace rtl;
70 using namespace com::sun::star;
71 
72 /*
73  *	static helpers
74  */
75 
76 static oslModule driverLib					= NULL;
77 extern "C"
78 {
79 typedef int(*setupFunction)(PrinterInfo&);
80 static setupFunction pSetupFunction         = NULL;
81 typedef int(*faxFunction)(String&);
82 static faxFunction pFaxNrFunction           = NULL;
83 }
84 
85 static String getPdfDir( const PrinterInfo& rInfo )
86 {
87 	String aDir;
88     sal_Int32 nIndex = 0;
89     while( nIndex != -1 )
90 	{
91 		OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
92 		if( ! aToken.compareToAscii( "pdf=", 4 ) )
93 		{
94             sal_Int32 nPos = 0;
95 			aDir = aToken.getToken( 1, '=', nPos );
96 			if( ! aDir.Len() )
97 				aDir = String( ByteString( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
98 			break;
99 		}
100 	}
101 	return aDir;
102 }
103 
104 static void getPaLib()
105 {
106 	if( ! driverLib )
107 	{
108         OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( _XSALSET_LIBNAME ) );
109         driverLib	= osl_loadModuleRelative( (oslGenericFunction)getPaLib, aLibName.pData, SAL_LOADMODULE_DEFAULT );
110         if ( !driverLib )
111         {
112             return;
113         }
114 
115         pSetupFunction	= (setupFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_SetupPrinterDriver" );
116         if ( !pSetupFunction )
117             fprintf( stderr, "could not resolve Sal_SetupPrinterDriver\n" );
118 
119         pFaxNrFunction = (faxFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_queryFaxNumber" );
120         if ( !pFaxNrFunction )
121             fprintf( stderr, "could not resolve Sal_queryFaxNumber\n" );
122 	}
123 }
124 
125 inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); }
126 
127 inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); }
128 
129 static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
130 {
131 	pJobSetup->meOrientation	= (Orientation)(rData.m_eOrientation == orientation::Landscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT);
132 
133 	// copy page size
134 	String aPaper;
135 	int width, height;
136 
137 	rData.m_aContext.getPageSize( aPaper, width, height );
138 	pJobSetup->mePaperFormat	= PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 ));
139 
140 	pJobSetup->mnPaperWidth		= 0;
141 	pJobSetup->mnPaperHeight	= 0;
142 	if( pJobSetup->mePaperFormat == PAPER_USER )
143 	{
144 		// transform to 100dth mm
145 		width				= PtTo10Mu( width );
146 		height				= PtTo10Mu( height );
147 
148         if( rData.m_eOrientation == psp::orientation::Portrait )
149         {
150             pJobSetup->mnPaperWidth	= width;
151             pJobSetup->mnPaperHeight= height;
152         }
153         else
154         {
155             pJobSetup->mnPaperWidth	= height;
156             pJobSetup->mnPaperHeight= width;
157         }
158 	}
159 
160 	// copy input slot
161 	const PPDKey* pKey = NULL;
162 	const PPDValue* pValue = NULL;
163 
164     pJobSetup->mnPaperBin = 0;
165     if( rData.m_pParser )
166 	    pKey					= rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
167     if( pKey )
168         pValue					= rData.m_aContext.getValue( pKey );
169     if( pKey && pValue )
170     {
171         for( pJobSetup->mnPaperBin = 0;
172              pValue != pKey->getValue( pJobSetup->mnPaperBin ) &&
173                  pJobSetup->mnPaperBin < pKey->countValues();
174              pJobSetup->mnPaperBin++ )
175             ;
176         if( pJobSetup->mnPaperBin >= pKey->countValues() )
177             pJobSetup->mnPaperBin = 0;
178     }
179 
180     // copy duplex
181     pKey = NULL;
182     pValue = NULL;
183 
184     pJobSetup->meDuplexMode = DUPLEX_UNKNOWN;
185     if( rData.m_pParser )
186         pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
187     if( pKey )
188         pValue = rData.m_aContext.getValue( pKey );
189     if( pKey && pValue )
190     {
191         if( pValue->m_aOption.EqualsIgnoreCaseAscii( "None" ) ||
192             pValue->m_aOption.EqualsIgnoreCaseAscii( "Simplex", 0, 7 )
193            )
194         {
195             pJobSetup->meDuplexMode = DUPLEX_OFF;
196         }
197         else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) )
198         {
199             pJobSetup->meDuplexMode = DUPLEX_LONGEDGE;
200         }
201         else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexTumble" ) )
202         {
203             pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE;
204         }
205     }
206 
207 	// copy the whole context
208 	if( pJobSetup->mpDriverData )
209 		rtl_freeMemory( pJobSetup->mpDriverData );
210 
211 	int nBytes;
212 	void* pBuffer = NULL;
213 	if( rData.getStreamBuffer( pBuffer, nBytes ) )
214 	{
215 		pJobSetup->mnDriverDataLen = nBytes;
216 		pJobSetup->mpDriverData = (sal_uInt8*)pBuffer;
217 	}
218 	else
219 	{
220 		pJobSetup->mnDriverDataLen = 0;
221 		pJobSetup->mpDriverData = NULL;
222 	}
223 }
224 
225 static bool passFileToCommandLine( const String& rFilename, const String& rCommandLine, bool bRemoveFile = true )
226 {
227 	bool bSuccess = false;
228 
229 	rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
230 	ByteString aCmdLine( rCommandLine, aEncoding );
231 	ByteString aFilename( rFilename, aEncoding );
232 
233 	bool bPipe = aCmdLine.Search( "(TMP)" ) != STRING_NOTFOUND ? false : true;
234 
235 	// setup command line for exec
236 	if( ! bPipe )
237 		while( aCmdLine.SearchAndReplace( "(TMP)", aFilename ) != STRING_NOTFOUND )
238 			;
239 
240 #if OSL_DEBUG_LEVEL > 1
241 	fprintf( stderr, "%s commandline: \"%s\"\n",
242 			 bPipe ? "piping to" : "executing",
243 			 aCmdLine.GetBuffer() );
244     struct stat aStat;
245     if( stat( aFilename.GetBuffer(), &aStat ) )
246         fprintf( stderr, "stat( %s ) failed\n", aFilename.GetBuffer() );
247     fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.GetBuffer(), (long)aStat.st_mode );
248 #endif
249 	const char* argv[4];
250 	if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) )
251 		argv[ 0 ] = "/bin/sh";
252 	argv[ 1 ] = "-c";
253 	argv[ 2 ] = aCmdLine.GetBuffer();
254 	argv[ 3 ] = 0;
255 
256 	bool bHavePipes = false;
257 	int pid, fd[2];
258 
259 	if( bPipe )
260 		bHavePipes = pipe( fd ) ? false : true;
261 	if( ( pid = fork() ) > 0 )
262 	{
263 		if( bPipe && bHavePipes )
264 		{
265 			close( fd[0] );
266 			char aBuffer[ 2048 ];
267 			FILE* fp = fopen( aFilename.GetBuffer(), "r" );
268 			while( fp && ! feof( fp ) )
269 			{
270 				int nBytes = fread( aBuffer, 1, sizeof( aBuffer ), fp );
271 				if( nBytes )
272 					write( fd[ 1 ], aBuffer, nBytes );
273 			}
274 			fclose( fp );
275 			close( fd[ 1 ] );
276 		}
277 		int status = 0;
278 		waitpid( pid, &status, 0 );
279 		if( ! status )
280 			bSuccess = true;
281 	}
282 	else if( ! pid )
283 	{
284 		if( bPipe && bHavePipes )
285 		{
286 			close( fd[1] );
287 			if( fd[0] != STDIN_FILENO ) // not probable, but who knows :)
288 				dup2( fd[0], STDIN_FILENO );
289 		}
290 		execv( argv[0], const_cast<char**>(argv) );
291 		fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.GetBuffer() );
292 		_exit( 1 );
293 	}
294 	else
295 		fprintf( stderr, "failed to fork\n" );
296 
297 	// clean up the mess
298     if( bRemoveFile )
299         unlink( aFilename.GetBuffer() );
300 
301 	return bSuccess;
302 }
303 
304 static bool sendAFax( const String& rFaxNumber, const String& rFileName, const String& rCommand )
305 {
306     std::list< OUString > aFaxNumbers;
307 
308 	if( ! rFaxNumber.Len() )
309 	{
310 		getPaLib();
311 		if( pFaxNrFunction )
312 		{
313 			String aNewNr;
314 			if( pFaxNrFunction( aNewNr ) )
315 				aFaxNumbers.push_back( OUString( aNewNr ) );
316 		}
317 	}
318     else
319     {
320         sal_Int32 nIndex = 0;
321         OUString aFaxes( rFaxNumber );
322         OUString aBeginToken( RTL_CONSTASCII_USTRINGPARAM("<Fax#>") );
323         OUString aEndToken( RTL_CONSTASCII_USTRINGPARAM("</Fax#>") );
324         while( nIndex != -1 )
325         {
326             nIndex = aFaxes.indexOf( aBeginToken, nIndex );
327             if( nIndex != -1 )
328             {
329                 sal_Int32 nBegin = nIndex + aBeginToken.getLength();
330                 nIndex = aFaxes.indexOf( aEndToken, nIndex );
331                 if( nIndex != -1 )
332                 {
333                     aFaxNumbers.push_back( aFaxes.copy( nBegin, nIndex-nBegin ) );
334                     nIndex += aEndToken.getLength();
335                 }
336             }
337         }
338     }
339 
340     bool bSuccess = true;
341     if( aFaxNumbers.begin() != aFaxNumbers.end() )
342 	{
343         while( aFaxNumbers.begin() != aFaxNumbers.end() && bSuccess )
344         {
345             String aCmdLine( rCommand );
346             String aFaxNumber( aFaxNumbers.front() );
347             aFaxNumbers.pop_front();
348             while( aCmdLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PHONE)" ) ), aFaxNumber ) != STRING_NOTFOUND )
349                 ;
350 #if OSL_DEBUG_LEVEL > 1
351             fprintf( stderr, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber, osl_getThreadTextEncoding() ).getStr() );
352 #endif
353             bSuccess = passFileToCommandLine( rFileName, aCmdLine, false );
354         }
355 	}
356     else
357         bSuccess = false;
358 
359     // clean up temp file
360     unlink( ByteString( rFileName, osl_getThreadTextEncoding() ).GetBuffer() );
361 
362     return bSuccess;
363 }
364 
365 static bool createPdf( const String& rToFile, const String& rFromFile, const String& rCommandLine )
366 {
367 	String aCommandLine( rCommandLine );
368 	while( aCommandLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(OUTFILE)" ) ), rToFile ) != STRING_NOTFOUND )
369 		;
370 	return passFileToCommandLine( rFromFile, aCommandLine );
371 }
372 
373 /*
374  *	SalInstance
375  */
376 
377 // -----------------------------------------------------------------------
378 
379 SalInfoPrinter* X11SalInstance::CreateInfoPrinter( SalPrinterQueueInfo*	pQueueInfo,
380                                                    ImplJobSetup*			pJobSetup )
381 {
382     mbPrinterInit = true;
383 	// create and initialize SalInfoPrinter
384 	PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter;
385 
386 	if( pJobSetup )
387 	{
388 		PrinterInfoManager& rManager( PrinterInfoManager::get() );
389 		PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
390 		pPrinter->m_aJobData = aInfo;
391 		pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData );
392 
393 		if( pJobSetup->mpDriverData )
394 			JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
395 
396 		pJobSetup->mnSystem			= JOBSETUP_SYSTEM_UNIX;
397 		pJobSetup->maPrinterName	= pQueueInfo->maPrinterName;
398 		pJobSetup->maDriver			= aInfo.m_aDriverName;
399 		copyJobDataToJobSetup( pJobSetup, aInfo );
400 
401         // set/clear backwards compatibility flag
402         bool bStrictSO52Compatibility = false;
403         std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
404             pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
405 
406         if( compat_it != pJobSetup->maValueMap.end() )
407         {
408             if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
409                 bStrictSO52Compatibility = true;
410         }
411         pPrinter->m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
412 	}
413 
414 
415 	return pPrinter;
416 }
417 
418 // -----------------------------------------------------------------------
419 
420 void X11SalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
421 {
422 	delete pPrinter;
423 }
424 
425 // -----------------------------------------------------------------------
426 
427 SalPrinter* X11SalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
428 {
429     mbPrinterInit = true;
430 	// create and initialize SalPrinter
431 	PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
432 	pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
433 
434 	return pPrinter;
435 }
436 
437 // -----------------------------------------------------------------------
438 
439 void X11SalInstance::DestroyPrinter( SalPrinter* pPrinter )
440 {
441 	delete pPrinter;
442 }
443 
444 // -----------------------------------------------------------------------
445 
446 void X11SalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
447 {
448     mbPrinterInit = true;
449 	PrinterInfoManager& rManager( PrinterInfoManager::get() );
450     static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
451     if( ! pNoSyncDetection || ! *pNoSyncDetection )
452     {
453         // #i62663# synchronize possible asynchronouse printer detection now
454         rManager.checkPrintersChanged( true );
455     }
456 	::std::list< OUString > aPrinters;
457 	rManager.listPrinters( aPrinters );
458 
459 	for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it )
460 	{
461 		const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) );
462 		// Neuen Eintrag anlegen
463 		SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
464 		pInfo->maPrinterName	= *it;
465 		pInfo->maDriver			= rInfo.m_aDriverName;
466 		pInfo->maLocation		= rInfo.m_aLocation;
467 		pInfo->maComment      	= rInfo.m_aComment;
468 		pInfo->mpSysData		= NULL;
469 
470         sal_Int32 nIndex = 0;
471         while( nIndex != -1 )
472 		{
473 			String aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
474 			if( aToken.CompareToAscii( "pdf=", 4 ) == COMPARE_EQUAL )
475 			{
476 				pInfo->maLocation = getPdfDir( rInfo );
477 				break;
478 			}
479 		}
480 
481 		pList->Add( pInfo );
482 	}
483 }
484 
485 // -----------------------------------------------------------------------
486 
487 void X11SalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
488 {
489 	delete pInfo;
490 }
491 
492 // -----------------------------------------------------------------------
493 
494 void X11SalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
495 {
496     mbPrinterInit = true;
497 }
498 
499 // -----------------------------------------------------------------------
500 
501 String X11SalInstance::GetDefaultPrinter()
502 {
503     mbPrinterInit = true;
504 	PrinterInfoManager& rManager( PrinterInfoManager::get() );
505 	return rManager.getDefaultPrinter();
506 }
507 
508 // =======================================================================
509 
510 PspSalInfoPrinter::PspSalInfoPrinter()
511 {
512 	m_pGraphics = NULL;
513     m_bPapersInit = false;
514 }
515 
516 // -----------------------------------------------------------------------
517 
518 PspSalInfoPrinter::~PspSalInfoPrinter()
519 {
520 	if( m_pGraphics )
521 	{
522 		delete m_pGraphics;
523 		m_pGraphics = NULL;
524 	}
525 }
526 
527 // -----------------------------------------------------------------------
528 
529 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
530 {
531     m_aPaperFormats.clear();
532     m_bPapersInit = true;
533 
534     if( m_aJobData.m_pParser )
535     {
536         const PPDKey* pKey = m_aJobData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
537         if( pKey )
538         {
539             int nValues = pKey->countValues();
540             for( int i = 0; i < nValues; i++ )
541             {
542                 const PPDValue* pValue = pKey->getValue( i );
543                 int nWidth = 0, nHeight = 0;
544                 m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
545                 PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
546                 m_aPaperFormats.push_back( aInfo );
547             }
548         }
549     }
550 }
551 
552 // -----------------------------------------------------------------------
553 
554 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
555 {
556     return 900;
557 }
558 
559 // -----------------------------------------------------------------------
560 
561 SalGraphics* PspSalInfoPrinter::GetGraphics()
562 {
563 	// return a valid pointer only once
564 	// the reasoning behind this is that we could have different
565 	// SalGraphics that can run in multiple threads
566 	// (future plans)
567 	SalGraphics* pRet = NULL;
568 	if( ! m_pGraphics )
569 	{
570 		m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, NULL, false, this );
571         m_pGraphics->SetLayout( 0 );
572 		pRet = m_pGraphics;
573 	}
574 	return pRet;
575 }
576 
577 // -----------------------------------------------------------------------
578 
579 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
580 {
581 	if( pGraphics == m_pGraphics )
582 	{
583 		delete pGraphics;
584 		m_pGraphics = NULL;
585 	}
586 	return;
587 }
588 
589 // -----------------------------------------------------------------------
590 
591 sal_Bool PspSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup )
592 {
593 	if( ! pFrame || ! pJobSetup )
594 		return sal_False;
595 
596 	getPaLib();
597 
598 	if( ! pSetupFunction )
599 		return sal_False;
600 
601 	PrinterInfoManager& rManager = PrinterInfoManager::get();
602 
603 	PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) );
604 	if ( pJobSetup->mpDriverData )
605     {
606 		SetData( ~0, pJobSetup );
607 		JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
608     }
609 
610 	if( pSetupFunction( aInfo ) )
611 	{
612 		rtl_freeMemory( pJobSetup->mpDriverData );
613 		pJobSetup->mpDriverData = NULL;
614 
615 		int nBytes;
616 		void* pBuffer = NULL;
617 		aInfo.getStreamBuffer( pBuffer, nBytes );
618 		pJobSetup->mnDriverDataLen	= nBytes;
619 		pJobSetup->mpDriverData		= (sal_uInt8*)pBuffer;
620 
621 		// copy everything to job setup
622 		copyJobDataToJobSetup( pJobSetup, aInfo );
623 		JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
624         return sal_True;
625 	}
626 	return sal_False;
627 }
628 
629 // -----------------------------------------------------------------------
630 
631 // This function gets the driver data and puts it into pJobSetup
632 // If pJobSetup->mpDriverData is NOT NULL, then the independend
633 // data should be merged into the driver data
634 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
635 // should be merged into the independent data
636 sal_Bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
637 {
638     // set/clear backwards compatibility flag
639     bool bStrictSO52Compatibility = false;
640     std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
641         pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
642 
643     if( compat_it != pJobSetup->maValueMap.end() )
644     {
645         if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
646             bStrictSO52Compatibility = true;
647     }
648     m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
649 
650 	if( pJobSetup->mpDriverData )
651 		return SetData( ~0, pJobSetup );
652 
653 	copyJobDataToJobSetup( pJobSetup, m_aJobData );
654 
655     return sal_True;
656 }
657 
658 // -----------------------------------------------------------------------
659 
660 // This function merges the independ driver data
661 // and sets the new independ data in pJobSetup
662 // Only the data must be changed, where the bit
663 // in nGetDataFlags is set
664 sal_Bool PspSalInfoPrinter::SetData(
665 	sal_uLong nSetDataFlags,
666 	ImplJobSetup* pJobSetup )
667 {
668 	JobData aData;
669 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
670 
671 	if( aData.m_pParser )
672 	{
673 		const PPDKey* pKey;
674 		const PPDValue* pValue;
675 
676 		// merge papersize if necessary
677 		if( nSetDataFlags & SAL_JOBSET_PAPERSIZE )
678 		{
679             int nWidth, nHeight;
680             if( pJobSetup->meOrientation == ORIENTATION_PORTRAIT )
681             {
682                 nWidth	= pJobSetup->mnPaperWidth;
683                 nHeight	= pJobSetup->mnPaperHeight;
684             }
685             else
686             {
687                 nWidth	= pJobSetup->mnPaperHeight;
688                 nHeight	= pJobSetup->mnPaperWidth;
689             }
690 			String aPaper;
691 
692             if( pJobSetup->mePaperFormat == PAPER_USER )
693                 aPaper = aData.m_pParser->matchPaper(
694                     TenMuToPt( pJobSetup->mnPaperWidth ),
695                     TenMuToPt( pJobSetup->mnPaperHeight ) );
696             else
697 				aPaper = rtl::OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1);
698 
699 			pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
700 			pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL;
701 
702             // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
703             // try to find the correct paper anyway using the size
704             if( pKey && ! pValue && pJobSetup->mePaperFormat != PAPER_USER )
705             {
706                 PaperInfo aInfo( pJobSetup->mePaperFormat );
707                 aPaper = aData.m_pParser->matchPaper(
708                     TenMuToPt( aInfo.getWidth() ),
709                     TenMuToPt( aInfo.getHeight() ) );
710                 pValue = pKey->getValueCaseInsensitive( aPaper );
711             }
712 
713 			if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) )
714 				return sal_False;
715 		}
716 
717 		// merge paperbin if necessary
718 		if( nSetDataFlags & SAL_JOBSET_PAPERBIN )
719 		{
720 			pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
721 			if( pKey )
722 			{
723 				int nPaperBin = pJobSetup->mnPaperBin;
724 				if( nPaperBin >= pKey->countValues() )
725 					pValue = pKey->getDefaultValue();
726 				else
727                     pValue = pKey->getValue( pJobSetup->mnPaperBin );
728 
729                 // may fail due to constraints;
730                 // real paper bin is copied back to jobsetup in that case
731 				aData.m_aContext.setValue( pKey, pValue );
732 			}
733 			// if printer has no InputSlot key simply ignore this setting
734 			// (e.g. SGENPRT has no InputSlot)
735 		}
736 
737 		// merge orientation if necessary
738 		if( nSetDataFlags & SAL_JOBSET_ORIENTATION )
739 			aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait;
740 
741         // merge duplex if necessary
742         if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE )
743         {
744             pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
745             if( pKey )
746             {
747                 pValue = NULL;
748                 switch( pJobSetup->meDuplexMode )
749                 {
750                 case DUPLEX_OFF:
751                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
752                     if( pValue == NULL )
753                         pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "SimplexNoTumble" ) ) );
754                     break;
755                 case DUPLEX_SHORTEDGE:
756                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexTumble" ) ) );
757                     break;
758                 case DUPLEX_LONGEDGE:
759                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexNoTumble" ) ) );
760                     break;
761                 case DUPLEX_UNKNOWN:
762                 default:
763                     pValue = 0;
764                     break;
765                 }
766                 if( ! pValue )
767                     pValue = pKey->getDefaultValue();
768                 aData.m_aContext.setValue( pKey, pValue );
769             }
770         }
771 
772 		m_aJobData = aData;
773 		copyJobDataToJobSetup( pJobSetup, aData );
774 		return sal_True;
775 	}
776 
777 	return sal_False;
778 }
779 
780 // -----------------------------------------------------------------------
781 
782 void PspSalInfoPrinter::GetPageInfo(
783 	const ImplJobSetup* pJobSetup,
784 	long& rOutWidth, long& rOutHeight,
785 	long& rPageOffX, long& rPageOffY,
786 	long& rPageWidth, long& rPageHeight )
787 {
788 	if( ! pJobSetup )
789 		return;
790 
791 	JobData aData;
792 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
793 
794 	// get the selected page size
795 	if( aData.m_pParser )
796 	{
797 
798 		String aPaper;
799 		int width, height;
800 		int left = 0, top = 0, right = 0, bottom = 0;
801 		int nDPI = aData.m_aContext.getRenderResolution();
802 
803 
804         if( aData.m_eOrientation == psp::orientation::Portrait )
805         {
806             aData.m_aContext.getPageSize( aPaper, width, height );
807             aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
808         }
809         else
810         {
811             aData.m_aContext.getPageSize( aPaper, height, width );
812             aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
813         }
814 
815 		rPageWidth	= width * nDPI / 72;
816 		rPageHeight	= height * nDPI / 72;
817 		rPageOffX	= left * nDPI / 72;
818 		rPageOffY	= top * nDPI / 72;
819 		rOutWidth	= ( width  - left - right ) * nDPI / 72;
820 		rOutHeight	= ( height - top  - bottom ) * nDPI / 72;
821 	}
822 }
823 
824 // -----------------------------------------------------------------------
825 
826 sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
827 {
828 	if( ! pJobSetup )
829 		return 0;
830 
831 	JobData aData;
832 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
833 
834 	const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
835     return pKey ? pKey->countValues() : 0;
836 }
837 
838 // -----------------------------------------------------------------------
839 
840 String PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin )
841 {
842 	JobData aData;
843 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
844 
845 	String aRet;
846 	if( aData.m_pParser )
847 	{
848 		const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
849 		if( ! pKey || nPaperBin >= (sal_uLong)pKey->countValues() )
850 			aRet = aData.m_pParser->getDefaultInputSlot();
851 		else
852         {
853             const PPDValue* pValue = pKey->getValue( nPaperBin );
854             if( pValue )
855                 aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
856         }
857 	}
858 
859 	return aRet;
860 }
861 
862 // -----------------------------------------------------------------------
863 
864 sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType )
865 {
866 	switch( nType )
867 	{
868 		case PRINTER_CAPABILITIES_SUPPORTDIALOG:
869 			return 1;
870 		case PRINTER_CAPABILITIES_COPIES:
871 			return 0xffff;
872 		case PRINTER_CAPABILITIES_COLLATECOPIES:
873         {
874             // see if the PPD contains a value to set Collate to True
875             JobData aData;
876             JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
877 
878             const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ) : NULL;
879             const PPDValue* pVal = pKey ? pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ) : NULL;
880 
881             // PPDs don't mention the number of possible collated copies.
882             // so let's guess as many as we want ?
883 			return pVal ? 0xffff : 0;
884         }
885 		case PRINTER_CAPABILITIES_SETORIENTATION:
886 			return 1;
887 		case PRINTER_CAPABILITIES_SETDUPLEX:
888 			return 1;
889 		case PRINTER_CAPABILITIES_SETPAPERBIN:
890 			return 1;
891 		case PRINTER_CAPABILITIES_SETPAPERSIZE:
892 			return 1;
893 		case PRINTER_CAPABILITIES_SETPAPER:
894 			return 0;
895 		case PRINTER_CAPABILITIES_FAX:
896             return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "fax" ) ? 1 : 0;
897 		case PRINTER_CAPABILITIES_PDF:
898             if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "pdf" ) )
899                 return 1;
900             else
901             {
902                 // see if the PPD contains a value to set Collate to True
903                 JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
904                 if( pJobSetup->mpDriverData )
905                     JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
906                 return aData.m_nPDFDevice > 0 ? 1 : 0;
907             }
908 		case PRINTER_CAPABILITIES_EXTERNALDIALOG:
909             return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "external_dialog" ) ? 1 : 0;
910         case PRINTER_CAPABILITIES_USEPULLMODEL:
911         {
912             // see if the PPD contains a value to set Collate to True
913             JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
914             if( pJobSetup->mpDriverData )
915                 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
916             return aData.m_nPDFDevice > 0 ? 1 : 0;
917         }
918 		default: break;
919 	};
920 	return 0;
921 }
922 
923 // =======================================================================
924 
925 /*
926  *	SalPrinter
927  */
928 
929  PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
930  : m_bFax( false ),
931    m_bPdf( false ),
932    m_bSwallowFaxNo( false ),
933    m_bIsPDFWriterJob( false ),
934    m_pGraphics( NULL ),
935    m_nCopies( 1 ),
936    m_bCollate( false ),
937    m_pInfoPrinter( pInfoPrinter )
938 {
939 }
940 
941 // -----------------------------------------------------------------------
942 
943 PspSalPrinter::~PspSalPrinter()
944 {
945 }
946 
947 // -----------------------------------------------------------------------
948 
949 static String getTmpName()
950 {
951     rtl::OUString aTmp, aSys;
952     osl_createTempFile( NULL, NULL, &aTmp.pData );
953     osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData );
954 
955     return aSys;
956 }
957 
958 sal_Bool PspSalPrinter::StartJob(
959 	const XubString* pFileName,
960 	const XubString& rJobName,
961 	const XubString& rAppName,
962 	sal_uLong nCopies,
963     bool bCollate,
964     bool bDirect,
965 	ImplJobSetup* pJobSetup )
966 {
967     vcl_sal::PrinterUpdate::jobStarted();
968 
969 	m_bFax		= false;
970 	m_bPdf		= false;
971 	m_aFileName	= pFileName ? *pFileName : String();
972 	m_aTmpFile	= String();
973     m_nCopies	= nCopies;
974     m_bCollate  = bCollate;
975 
976 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
977     if( m_nCopies > 1 )
978     {
979         // in case user did not do anything (m_nCopies=1)
980         // take the default from jobsetup
981         m_aJobData.m_nCopies = m_nCopies;
982         m_aJobData.setCollate( bCollate );
983     }
984 
985 	// check wether this printer is configured as fax
986     int nMode = 0;
987 	const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
988     sal_Int32 nIndex = 0;
989     while( nIndex != -1 )
990 	{
991 		OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
992 		if( ! aToken.compareToAscii( "fax", 3 ) )
993 		{
994 			m_bFax = true;
995 			m_aTmpFile = getTmpName();
996             nMode = S_IRUSR | S_IWUSR;
997 
998 			::std::hash_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it;
999 			it = pJobSetup->maValueMap.find( ::rtl::OUString::createFromAscii( "FAX#" ) );
1000 			if( it != pJobSetup->maValueMap.end() )
1001 				m_aFaxNr = it->second;
1002 
1003             sal_Int32 nPos = 0;
1004 			m_bSwallowFaxNo = ! aToken.getToken( 1, '=', nPos ).compareToAscii( "swallow", 7 ) ? true : false;
1005 
1006 			break;
1007 		}
1008 		if( ! aToken.compareToAscii( "pdf=", 4 ) )
1009 		{
1010 			m_bPdf = true;
1011 			m_aTmpFile = getTmpName();
1012             nMode = S_IRUSR | S_IWUSR;
1013 
1014 			if( ! m_aFileName.Len() )
1015 			{
1016 				m_aFileName = getPdfDir( rInfo );
1017 				m_aFileName.Append( '/' );
1018 				m_aFileName.Append( rJobName );
1019 				m_aFileName.AppendAscii( ".pdf" );
1020 			}
1021 			break;
1022 		}
1023 	}
1024 	m_aPrinterGfx.Init( m_aJobData );
1025 
1026     // set/clear backwards compatibility flag
1027     bool bStrictSO52Compatibility = false;
1028     std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
1029         pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
1030 
1031     if( compat_it != pJobSetup->maValueMap.end() )
1032     {
1033         if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
1034             bStrictSO52Compatibility = true;
1035     }
1036     m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
1037 
1038 	return m_aPrintJob.StartJob( m_aTmpFile.Len() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect ) ? sal_True : sal_False;
1039 }
1040 
1041 // -----------------------------------------------------------------------
1042 
1043 sal_Bool PspSalPrinter::EndJob()
1044 {
1045     sal_Bool bSuccess = sal_False;
1046     if( m_bIsPDFWriterJob )
1047         bSuccess = sal_True;
1048     else
1049     {
1050         bSuccess = m_aPrintJob.EndJob();
1051 
1052         if( bSuccess )
1053         {
1054             // check for fax
1055             if( m_bFax )
1056             {
1057 
1058                 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
1059                 // sendAFax removes the file after use
1060                 bSuccess = sendAFax( m_aFaxNr, m_aTmpFile, rInfo.m_aCommand );
1061             }
1062             else if( m_bPdf )
1063             {
1064                 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
1065                 bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand );
1066             }
1067         }
1068     }
1069     vcl_sal::PrinterUpdate::jobEnded();
1070 	return bSuccess;
1071 }
1072 
1073 // -----------------------------------------------------------------------
1074 
1075 sal_Bool PspSalPrinter::AbortJob()
1076 {
1077     sal_Bool bAbort = m_aPrintJob.AbortJob() ? sal_True : sal_False;
1078     vcl_sal::PrinterUpdate::jobEnded();
1079 	return bAbort;
1080 }
1081 
1082 // -----------------------------------------------------------------------
1083 
1084 SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, sal_Bool )
1085 {
1086 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
1087 	m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, m_bFax ? &m_aFaxNr : NULL, m_bSwallowFaxNo, m_pInfoPrinter  );
1088     m_pGraphics->SetLayout( 0 );
1089     if( m_nCopies > 1 )
1090     {
1091         // in case user did not do anything (m_nCopies=1)
1092         // take the default from jobsetup
1093         m_aJobData.m_nCopies = m_nCopies;
1094         m_aJobData.setCollate( m_nCopies > 1 && m_bCollate );
1095     }
1096 
1097 	m_aPrintJob.StartPage( m_aJobData );
1098 	m_aPrinterGfx.Init( m_aPrintJob );
1099 
1100 	return m_pGraphics;
1101 }
1102 
1103 // -----------------------------------------------------------------------
1104 
1105 sal_Bool PspSalPrinter::EndPage()
1106 {
1107 	sal_Bool bResult = m_aPrintJob.EndPage();
1108 	m_aPrinterGfx.Clear();
1109 	return bResult ? sal_True : sal_False;
1110 }
1111 
1112 // -----------------------------------------------------------------------
1113 
1114 sal_uLong PspSalPrinter::GetErrorCode()
1115 {
1116 	return 0;
1117 }
1118 
1119 // -----------------------------------------------------------------------
1120 
1121 struct PDFNewJobParameters
1122 {
1123     Size        maPageSize;
1124     sal_uInt16      mnPaperBin;
1125 
1126     PDFNewJobParameters( const Size& i_rSize = Size(),
1127                          sal_uInt16 i_nPaperBin = 0xffff )
1128     : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
1129 
1130     bool operator!=(const PDFNewJobParameters& rComp ) const
1131     {
1132         Size aCompLSSize( rComp.maPageSize.Height(), rComp.maPageSize.Width() );
1133         return
1134             (maPageSize != rComp.maPageSize && maPageSize != aCompLSSize)
1135         ||  mnPaperBin != rComp.mnPaperBin
1136         ;
1137     }
1138 
1139     bool operator==(const PDFNewJobParameters& rComp) const
1140     {
1141         return ! this->operator!=(rComp);
1142     }
1143 };
1144 
1145 struct PDFPrintFile
1146 {
1147     rtl::OUString       maTmpURL;
1148     PDFNewJobParameters maParameters;
1149 
1150     PDFPrintFile( const rtl::OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters )
1151     : maTmpURL( i_rURL )
1152     , maParameters( i_rNewParameters ) {}
1153 };
1154 
1155 sal_Bool PspSalPrinter::StartJob( const String* i_pFileName, const String& i_rJobName, const String& i_rAppName,
1156                               ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
1157 {
1158     OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName ? rtl::OUStringToOString( *i_pFileName, RTL_TEXTENCODING_UTF8 ).getStr() : "<nil>" );
1159     // mark for endjob
1160     m_bIsPDFWriterJob = true;
1161     // reset IsLastPage
1162     i_rController.setLastPage( sal_False );
1163 
1164     // update job data
1165     if( i_pSetupData )
1166         JobData::constructFromStreamBuffer( i_pSetupData->mpDriverData, i_pSetupData->mnDriverDataLen, m_aJobData );
1167 
1168     OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 );
1169     m_aJobData.m_nPDFDevice = 1;
1170 
1171     // possibly create one job for collated output
1172     sal_Bool bSinglePrintJobs = sal_False;
1173     beans::PropertyValue* pSingleValue = i_rController.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) );
1174     if( pSingleValue )
1175     {
1176         pSingleValue->Value >>= bSinglePrintJobs;
1177     }
1178 
1179     int nCopies = i_rController.getPrinter()->GetCopyCount();
1180     bool bCollate = i_rController.getPrinter()->IsCollateCopy();
1181 
1182     // notify start of real print job
1183     i_rController.jobStarted();
1184 
1185     // setup PDFWriter context
1186     vcl::PDFWriter::PDFWriterContext aContext;
1187     aContext.Version            = vcl::PDFWriter::PDF_1_4;
1188     aContext.Tagged             = false;
1189     aContext.EmbedStandardFonts = true;
1190     aContext.DocumentLocale     = Application::GetSettings().GetLocale();
1191     aContext.ColorMode          = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
1192     ? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor;
1193 
1194     // prepare doc info
1195     aContext.DocumentInfo.Title              = i_rJobName;
1196     aContext.DocumentInfo.Creator            = i_rAppName;
1197     aContext.DocumentInfo.Producer           = i_rAppName;
1198 
1199     // define how we handle metafiles in PDFWriter
1200     vcl::PDFWriter::PlayMetafileContext aMtfContext;
1201     aMtfContext.m_bOnlyLosslessCompression = true;
1202 
1203     boost::shared_ptr<vcl::PDFWriter> pWriter;
1204     std::vector< PDFPrintFile > aPDFFiles;
1205     boost::shared_ptr<Printer> pPrinter( i_rController.getPrinter() );
1206     int nAllPages = i_rController.getFilteredPageCount();
1207     i_rController.createProgressDialog();
1208     bool bAborted = false;
1209     PDFNewJobParameters aLastParm;
1210 
1211     aContext.DPIx = pPrinter->ImplGetDPIX();
1212     aContext.DPIy = pPrinter->ImplGetDPIY();
1213     for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ )
1214     {
1215         if( nPage == nAllPages-1 )
1216             i_rController.setLastPage( sal_True );
1217 
1218         // get the page's metafile
1219         GDIMetaFile aPageFile;
1220         vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
1221         if( i_rController.isProgressCanceled() )
1222         {
1223             bAborted = true;
1224             if( nPage != nAllPages-1 )
1225             {
1226                 i_rController.createProgressDialog();
1227                 i_rController.setLastPage( sal_True );
1228                 i_rController.getFilteredPageFile( nPage, aPageFile );
1229             }
1230         }
1231         else
1232         {
1233             pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
1234             pPrinter->SetPaperSizeUser( aPageSize.aSize, true );
1235             PDFNewJobParameters aNewParm( pPrinter->GetPaperSize(), pPrinter->GetPaperBin() );
1236 
1237             // create PDF writer on demand
1238             // either on first page
1239             // or on paper format change - cups does not support multiple paper formats per job (yet?)
1240             // so we need to start a new job to get a new paper format from the printer
1241             // orientation switches (that is switch of height and width) is handled transparently by CUPS
1242             if( ! pWriter ||
1243                 (aNewParm != aLastParm && ! i_pFileName ) )
1244             {
1245                 if( pWriter )
1246                 {
1247                     pWriter->Emit();
1248                 }
1249                 // produce PDF file
1250                 OUString aPDFUrl;
1251                 if( i_pFileName )
1252                     aPDFUrl = *i_pFileName;
1253                 else
1254                     osl_createTempFile( NULL, NULL, &aPDFUrl.pData );
1255                 // normalize to file URL
1256                 if( aPDFUrl.compareToAscii( "file:", 5 ) != 0 )
1257                 {
1258                     // this is not a file URL, but it should
1259                     // form it into a osl friendly file URL
1260                     rtl::OUString aTmp;
1261                     osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
1262                     aPDFUrl = aTmp;
1263                 }
1264                 // save current file and paper format
1265                 aLastParm = aNewParm;
1266                 aPDFFiles.push_back( PDFPrintFile( aPDFUrl, aNewParm ) );
1267                 // update context
1268                 aContext.URL = aPDFUrl;
1269 
1270                 // create and initialize PDFWriter
1271                 #if defined __SUNPRO_CC
1272                 #pragma disable_warn
1273                 #endif
1274                 pWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) );
1275                 #if defined __SUNPRO_CC
1276                 #pragma enable_warn
1277                 #endif
1278             }
1279 
1280             pWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
1281                               TenMuToPt( aNewParm.maPageSize.Height() ),
1282                               vcl::PDFWriter::Portrait );
1283 
1284             pWriter->PlayMetafile( aPageFile, aMtfContext, NULL );
1285         }
1286     }
1287 
1288     // emit the last file
1289     if( pWriter )
1290         pWriter->Emit();
1291 
1292     // handle collate, copy count and multiple jobs correctly
1293     int nOuterJobs = 1;
1294     if( bSinglePrintJobs )
1295     {
1296         nOuterJobs = nCopies;
1297         m_aJobData.m_nCopies = 1;
1298     }
1299     else
1300     {
1301         if( bCollate )
1302         {
1303             if( aPDFFiles.size() == 1 && pPrinter->HasSupport( SUPPORT_COLLATECOPY ) )
1304             {
1305                 m_aJobData.setCollate( true );
1306                 m_aJobData.m_nCopies = nCopies;
1307             }
1308             else
1309             {
1310                 nOuterJobs = nCopies;
1311                 m_aJobData.m_nCopies = 1;
1312             }
1313         }
1314         else
1315         {
1316             m_aJobData.setCollate( false );
1317             m_aJobData.m_nCopies = nCopies;
1318         }
1319     }
1320 
1321     // spool files
1322     if( ! i_pFileName && ! bAborted )
1323     {
1324         bool bFirstJob = true;
1325         for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ )
1326         {
1327             for( size_t i = 0; i < aPDFFiles.size(); i++ )
1328             {
1329                 oslFileHandle pFile = NULL;
1330                 osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
1331                 if( pFile )
1332                 {
1333                     osl_setFilePos( pFile, osl_Pos_Absolut, 0 );
1334                     std::vector< char > buffer( 0x10000, 0 );
1335                     // update job data with current page size
1336                     Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
1337                     m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
1338                     // update job data with current paperbin
1339                     m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
1340 
1341                     // spool current file
1342                     FILE* fp = PrinterInfoManager::get().startSpool( pPrinter->GetName(), i_rController.isDirectPrint() );
1343                     if( fp )
1344                     {
1345                         sal_uInt64 nBytesRead = 0;
1346                         do
1347                         {
1348                             osl_readFile( pFile, &buffer[0], buffer.size(), &nBytesRead );
1349                             if( nBytesRead > 0 )
1350                                 fwrite( &buffer[0], 1, nBytesRead, fp );
1351                         } while( nBytesRead == buffer.size() );
1352                         rtl::OUStringBuffer aBuf( i_rJobName.Len() + 8 );
1353                         aBuf.append( i_rJobName );
1354                         if( i > 0 || nCurJob > 0 )
1355                         {
1356                             aBuf.append( sal_Unicode(' ') );
1357                             aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
1358                         }
1359                         PrinterInfoManager::get().endSpool( pPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob );
1360                         bFirstJob = false;
1361                     }
1362                 }
1363                 osl_closeFile( pFile );
1364             }
1365         }
1366     }
1367 
1368     // job has been spooled
1369     i_rController.setJobState( bAborted ? view::PrintableState_JOB_ABORTED : view::PrintableState_JOB_SPOOLED );
1370 
1371     // clean up the temporary PDF files
1372     if( ! i_pFileName || bAborted )
1373     {
1374         for( size_t i = 0; i < aPDFFiles.size(); i++ )
1375         {
1376             osl_removeFile( aPDFFiles[i].maTmpURL.pData );
1377             OSL_TRACE( "removed print PDF file %s\n", rtl::OUStringToOString( aPDFFiles[i].maTmpURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1378         }
1379     }
1380 
1381     return sal_True;
1382 }
1383 
1384 
1385 
1386 /*
1387  *  vcl::PrinterUpdate
1388  */
1389 
1390 Timer* vcl_sal::PrinterUpdate::pPrinterUpdateTimer = NULL;
1391 int vcl_sal::PrinterUpdate::nActiveJobs = 0;
1392 
1393 void vcl_sal::PrinterUpdate::doUpdate()
1394 {
1395     ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
1396     if( rManager.checkPrintersChanged( false ) )
1397     {
1398         SalDisplay* pDisp = GetX11SalData()->GetDisplay();
1399         const std::list< SalFrame* >& rList = pDisp->getFrames();
1400         for( std::list< SalFrame* >::const_iterator it = rList.begin();
1401              it != rList.end(); ++it )
1402             pDisp->SendInternalEvent( *it, NULL, SALEVENT_PRINTERCHANGED );
1403     }
1404 }
1405 
1406 // -----------------------------------------------------------------------
1407 
1408 IMPL_STATIC_LINK_NOINSTANCE( vcl_sal::PrinterUpdate, UpdateTimerHdl, void*, EMPTYARG )
1409 {
1410     if( nActiveJobs < 1 )
1411     {
1412         doUpdate();
1413         delete pPrinterUpdateTimer;
1414         pPrinterUpdateTimer = NULL;
1415     }
1416     else
1417         pPrinterUpdateTimer->Start();
1418 
1419     return 0;
1420 }
1421 
1422 // -----------------------------------------------------------------------
1423 
1424 void vcl_sal::PrinterUpdate::update()
1425 {
1426     if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1427         return;
1428 
1429     if( ! static_cast< X11SalInstance* >(GetSalData()->m_pInstance)->isPrinterInit() )
1430     {
1431         // #i45389# start background printer detection
1432         psp::PrinterInfoManager::get();
1433         return;
1434     }
1435 
1436     if( nActiveJobs < 1 )
1437         doUpdate();
1438     else if( ! pPrinterUpdateTimer )
1439     {
1440         pPrinterUpdateTimer = new Timer();
1441         pPrinterUpdateTimer->SetTimeout( 500 );
1442         pPrinterUpdateTimer->SetTimeoutHdl( STATIC_LINK( NULL, vcl_sal::PrinterUpdate, UpdateTimerHdl ) );
1443         pPrinterUpdateTimer->Start();
1444     }
1445 }
1446 
1447 // -----------------------------------------------------------------------
1448 
1449 void vcl_sal::PrinterUpdate::jobEnded()
1450 {
1451     nActiveJobs--;
1452     if( nActiveJobs < 1 )
1453     {
1454         if( pPrinterUpdateTimer )
1455         {
1456             pPrinterUpdateTimer->Stop();
1457             delete pPrinterUpdateTimer;
1458             pPrinterUpdateTimer = NULL;
1459             doUpdate();
1460         }
1461     }
1462 }
1463