xref: /aoo41x/main/vcl/unx/headless/svpprn.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 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 
35 #include "vcl/svapp.hxx"
36 #include "vcl/timer.hxx"
37 #include "vcl/printerinfomanager.hxx"
38 
39 #include "jobset.h"
40 #include "print.h"
41 #include "salptype.hxx"
42 
43 #include "svpprn.hxx"
44 #include "svppspgraphics.hxx"
45 #include "svpinst.hxx"
46 
47 using namespace psp;
48 using namespace rtl;
49 
50 /*
51  *	static helpers
52  */
53 
54 static String getPdfDir( const PrinterInfo& rInfo )
55 {
56 	String aDir;
57     sal_Int32 nIndex = 0;
58     while( nIndex != -1 )
59 	{
60 		OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
61 		if( ! aToken.compareToAscii( "pdf=", 4 ) )
62 		{
63             sal_Int32 nPos = 0;
64 			aDir = aToken.getToken( 1, '=', nPos );
65 			if( ! aDir.Len() )
66 				aDir = String( ByteString( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
67 			break;
68 		}
69 	}
70 	return aDir;
71 }
72 
73 inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); }
74 
75 inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); }
76 
77 static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
78 {
79 	pJobSetup->meOrientation	= (Orientation)(rData.m_eOrientation == orientation::Landscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT);
80 
81 	// copy page size
82 	String aPaper;
83 	int width, height;
84 
85 	rData.m_aContext.getPageSize( aPaper, width, height );
86 	pJobSetup->mePaperFormat	= PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 ));
87 	pJobSetup->mnPaperWidth		= 0;
88 	pJobSetup->mnPaperHeight	= 0;
89 	if( pJobSetup->mePaperFormat == PAPER_USER )
90 	{
91 		// transform to 100dth mm
92 		width				= PtTo10Mu( width );
93 		height				= PtTo10Mu( height );
94 
95         if( rData.m_eOrientation == psp::orientation::Portrait )
96         {
97             pJobSetup->mnPaperWidth	= width;
98             pJobSetup->mnPaperHeight= height;
99         }
100         else
101         {
102             pJobSetup->mnPaperWidth	= height;
103             pJobSetup->mnPaperHeight= width;
104         }
105 	}
106 
107 	// copy input slot
108 	const PPDKey* pKey = NULL;
109 	const PPDValue* pValue = NULL;
110 
111     pJobSetup->mnPaperBin = 0xffff;
112     if( rData.m_pParser )
113 	    pKey					= rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
114     if( pKey )
115         pValue					= rData.m_aContext.getValue( pKey );
116     if( pKey && pValue )
117     {
118         for( pJobSetup->mnPaperBin = 0;
119              pValue != pKey->getValue( pJobSetup->mnPaperBin ) &&
120                  pJobSetup->mnPaperBin < pKey->countValues();
121              pJobSetup->mnPaperBin++ )
122             ;
123         if( pJobSetup->mnPaperBin >= pKey->countValues() || pValue == pKey->getDefaultValue() )
124             pJobSetup->mnPaperBin = 0xffff;
125     }
126 
127     // copy duplex
128     pKey = NULL;
129     pValue = NULL;
130 
131     pJobSetup->meDuplexMode = DUPLEX_UNKNOWN;
132     if( rData.m_pParser )
133         pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
134     if( pKey )
135         pValue = rData.m_aContext.getValue( pKey );
136     if( pKey && pValue )
137     {
138         if( pValue->m_aOption.EqualsIgnoreCaseAscii( "None" ) ||
139             pValue->m_aOption.EqualsIgnoreCaseAscii( "Simplex", 0, 7 )
140            )
141         {
142             pJobSetup->meDuplexMode = DUPLEX_OFF;
143         }
144         else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) )
145         {
146             pJobSetup->meDuplexMode = DUPLEX_LONGEDGE;
147         }
148         else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexTumble" ) )
149         {
150             pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE;
151         }
152     }
153 
154 	// copy the whole context
155 	if( pJobSetup->mpDriverData )
156 		rtl_freeMemory( pJobSetup->mpDriverData );
157 
158 	int nBytes;
159 	void* pBuffer = NULL;
160 	if( rData.getStreamBuffer( pBuffer, nBytes ) )
161 	{
162 		pJobSetup->mnDriverDataLen = nBytes;
163 		pJobSetup->mpDriverData = (sal_uInt8*)pBuffer;
164 	}
165 	else
166 	{
167 		pJobSetup->mnDriverDataLen = 0;
168 		pJobSetup->mpDriverData = NULL;
169 	}
170 }
171 
172 static bool passFileToCommandLine( const String& rFilename, const String& rCommandLine, bool bRemoveFile = true )
173 {
174 	bool bSuccess = false;
175 
176 	rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
177 	ByteString aCmdLine( rCommandLine, aEncoding );
178 	ByteString aFilename( rFilename, aEncoding );
179 
180 	bool bPipe = aCmdLine.Search( "(TMP)" ) != STRING_NOTFOUND ? false : true;
181 
182 	// setup command line for exec
183 	if( ! bPipe )
184 		while( aCmdLine.SearchAndReplace( "(TMP)", aFilename ) != STRING_NOTFOUND )
185 			;
186 
187 #if OSL_DEBUG_LEVEL > 1
188 	fprintf( stderr, "%s commandline: \"%s\"\n",
189 			 bPipe ? "piping to" : "executing",
190 			 aCmdLine.GetBuffer() );
191     struct stat aStat;
192     if( stat( aFilename.GetBuffer(), &aStat ) )
193         fprintf( stderr, "stat( %s ) failed\n", aFilename.GetBuffer() );
194     fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.GetBuffer(), (long)aStat.st_mode );
195 #endif
196 	const char* argv[4];
197 	if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) )
198 		argv[ 0 ] = "/bin/sh";
199 	argv[ 1 ] = "-c";
200 	argv[ 2 ] = aCmdLine.GetBuffer();
201 	argv[ 3 ] = 0;
202 
203 	bool bHavePipes = false;
204 	int pid, fd[2];
205 
206 	if( bPipe )
207 		bHavePipes = pipe( fd ) ? false : true;
208 	if( ( pid = fork() ) > 0 )
209 	{
210 		if( bPipe && bHavePipes )
211 		{
212 			close( fd[0] );
213 			char aBuffer[ 2048 ];
214 			FILE* fp = fopen( aFilename.GetBuffer(), "r" );
215 			while( fp && ! feof( fp ) )
216 			{
217 				int nBytes = fread( aBuffer, 1, sizeof( aBuffer ), fp );
218 				if( nBytes )
219 					write( fd[ 1 ], aBuffer, nBytes );
220 			}
221 			fclose( fp );
222 			close( fd[ 1 ] );
223 		}
224 		int status = 0;
225 		waitpid( pid, &status, 0 );
226 		if( ! status )
227 			bSuccess = true;
228 	}
229 	else if( ! pid )
230 	{
231 		if( bPipe && bHavePipes )
232 		{
233 			close( fd[1] );
234 			if( fd[0] != STDIN_FILENO ) // not probable, but who knows :)
235 				dup2( fd[0], STDIN_FILENO );
236 		}
237 		execv( argv[0], const_cast<char**>(argv) );
238 		fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.GetBuffer() );
239 		_exit( 1 );
240 	}
241 	else
242 		fprintf( stderr, "failed to fork\n" );
243 
244 	// clean up the mess
245     if( bRemoveFile )
246         unlink( aFilename.GetBuffer() );
247 
248 	return bSuccess;
249 }
250 
251 static bool sendAFax( const String& rFaxNumber, const String& rFileName, const String& rCommand )
252 {
253     std::list< OUString > aFaxNumbers;
254 
255 	if( ! rFaxNumber.Len() )
256         return false;
257 
258     sal_Int32 nIndex = 0;
259     OUString aFaxes( rFaxNumber );
260     OUString aBeginToken( RTL_CONSTASCII_USTRINGPARAM("<Fax#>") );
261     OUString aEndToken( RTL_CONSTASCII_USTRINGPARAM("</Fax#>") );
262     while( nIndex != -1 )
263     {
264         nIndex = aFaxes.indexOf( aBeginToken, nIndex );
265         if( nIndex != -1 )
266         {
267             sal_Int32 nBegin = nIndex + aBeginToken.getLength();
268             nIndex = aFaxes.indexOf( aEndToken, nIndex );
269             if( nIndex != -1 )
270             {
271                 aFaxNumbers.push_back( aFaxes.copy( nBegin, nIndex-nBegin ) );
272                 nIndex += aEndToken.getLength();
273             }
274         }
275     }
276 
277     bool bSuccess = true;
278     if( aFaxNumbers.begin() != aFaxNumbers.end() )
279 	{
280         while( aFaxNumbers.begin() != aFaxNumbers.end() && bSuccess )
281         {
282             String aCmdLine( rCommand );
283             String aFaxNumber( aFaxNumbers.front() );
284             aFaxNumbers.pop_front();
285             while( aCmdLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PHONE)" ) ), aFaxNumber ) != STRING_NOTFOUND )
286                 ;
287 #if OSL_DEBUG_LEVEL > 1
288             fprintf( stderr, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber, osl_getThreadTextEncoding() ).getStr() );
289 #endif
290             bSuccess = passFileToCommandLine( rFileName, aCmdLine, false );
291         }
292 	}
293     else
294         bSuccess = false;
295 
296     // clean up temp file
297     unlink( ByteString( rFileName, osl_getThreadTextEncoding() ).GetBuffer() );
298 
299     return bSuccess;
300 }
301 
302 static bool createPdf( const String& rToFile, const String& rFromFile, const String& rCommandLine )
303 {
304 	String aCommandLine( rCommandLine );
305 	while( aCommandLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(OUTFILE)" ) ), rToFile ) != STRING_NOTFOUND )
306 		;
307 	return passFileToCommandLine( rFromFile, aCommandLine );
308 }
309 
310 /*
311  *	SalInstance
312  */
313 
314 // -----------------------------------------------------------------------
315 
316 SalInfoPrinter* SvpSalInstance::CreateInfoPrinter( SalPrinterQueueInfo*	pQueueInfo,
317                                                    ImplJobSetup*			pJobSetup )
318 {
319 	// create and initialize SalInfoPrinter
320 	PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter;
321 
322 	if( pJobSetup )
323 	{
324 		PrinterInfoManager& rManager( PrinterInfoManager::get() );
325 		PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
326 		pPrinter->m_aJobData = aInfo;
327 		pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData );
328 
329 		if( pJobSetup->mpDriverData )
330 			JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
331 
332 		pJobSetup->mnSystem			= JOBSETUP_SYSTEM_UNIX;
333 		pJobSetup->maPrinterName	= pQueueInfo->maPrinterName;
334 		pJobSetup->maDriver			= aInfo.m_aDriverName;
335 		copyJobDataToJobSetup( pJobSetup, aInfo );
336 
337         // set/clear backwards compatibility flag
338         bool bStrictSO52Compatibility = false;
339         std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
340             pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
341         if( compat_it != pJobSetup->maValueMap.end() )
342         {
343             if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
344                 bStrictSO52Compatibility = true;
345         }
346         pPrinter->m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
347 	}
348 
349 
350 	return pPrinter;
351 }
352 
353 // -----------------------------------------------------------------------
354 
355 void SvpSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
356 {
357 	delete pPrinter;
358 }
359 
360 // -----------------------------------------------------------------------
361 
362 SalPrinter* SvpSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
363 {
364 	// create and initialize SalPrinter
365 	PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
366 	pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
367 
368 	return pPrinter;
369 }
370 
371 // -----------------------------------------------------------------------
372 
373 void SvpSalInstance::DestroyPrinter( SalPrinter* pPrinter )
374 {
375 	delete pPrinter;
376 }
377 
378 // -----------------------------------------------------------------------
379 
380 void SvpSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
381 {
382 	PrinterInfoManager& rManager( PrinterInfoManager::get() );
383     static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
384     if( ! pNoSyncDetection || ! *pNoSyncDetection )
385     {
386         // #i62663# synchronize possible asynchronouse printer detection now
387         rManager.checkPrintersChanged( true );
388     }
389 	::std::list< OUString > aPrinters;
390 	rManager.listPrinters( aPrinters );
391 
392 	for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it )
393 	{
394 		const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) );
395 		// Neuen Eintrag anlegen
396 		SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
397 		pInfo->maPrinterName	= *it;
398 		pInfo->maDriver			= rInfo.m_aDriverName;
399 		pInfo->maLocation		= rInfo.m_aLocation;
400 		pInfo->maComment      	= rInfo.m_aComment;
401 		pInfo->mpSysData		= NULL;
402 
403         sal_Int32 nIndex = 0;
404         while( nIndex != -1 )
405 		{
406 			String aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
407 			if( aToken.CompareToAscii( "pdf=", 4 ) == COMPARE_EQUAL )
408 			{
409 				pInfo->maLocation = getPdfDir( rInfo );
410 				break;
411 			}
412 		}
413 
414 		pList->Add( pInfo );
415 	}
416 }
417 
418 // -----------------------------------------------------------------------
419 
420 void SvpSalInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
421 {
422 	delete pInfo;
423 }
424 
425 // -----------------------------------------------------------------------
426 
427 void SvpSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
428 {
429 }
430 
431 // -----------------------------------------------------------------------
432 
433 String SvpSalInstance::GetDefaultPrinter()
434 {
435 	PrinterInfoManager& rManager( PrinterInfoManager::get() );
436 	return rManager.getDefaultPrinter();
437 }
438 
439 // =======================================================================
440 
441 PspSalInfoPrinter::PspSalInfoPrinter()
442 {
443 	m_pGraphics = NULL;
444     m_bPapersInit = false;
445 }
446 
447 // -----------------------------------------------------------------------
448 
449 PspSalInfoPrinter::~PspSalInfoPrinter()
450 {
451 	if( m_pGraphics )
452 	{
453 		delete m_pGraphics;
454 		m_pGraphics = NULL;
455 	}
456 }
457 
458 // -----------------------------------------------------------------------
459 
460 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
461 {
462     m_aPaperFormats.clear();
463     m_bPapersInit = true;
464 
465     if( m_aJobData.m_pParser )
466     {
467         const PPDKey* pKey = m_aJobData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
468         if( pKey )
469         {
470             int nValues = pKey->countValues();
471             for( int i = 0; i < nValues; i++ )
472             {
473                 const PPDValue* pValue = pKey->getValue( i );
474                 int nWidth = 0, nHeight = 0;
475                 m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
476                 PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
477                 m_aPaperFormats.push_back( aInfo );
478             }
479         }
480     }
481 }
482 
483 // -----------------------------------------------------------------------
484 
485 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
486 {
487     return 900;
488 }
489 
490 // -----------------------------------------------------------------------
491 
492 SalGraphics* PspSalInfoPrinter::GetGraphics()
493 {
494 	// return a valid pointer only once
495 	// the reasoning behind this is that we could have different
496 	// SalGraphics that can run in multiple threads
497 	// (future plans)
498 	SalGraphics* pRet = NULL;
499 	if( ! m_pGraphics )
500 	{
501 		m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, NULL, false, this );
502         m_pGraphics->SetLayout( 0 );
503 		pRet = m_pGraphics;
504 	}
505 	return pRet;
506 }
507 
508 // -----------------------------------------------------------------------
509 
510 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
511 {
512 	if( pGraphics == m_pGraphics )
513 	{
514 		delete pGraphics;
515 		m_pGraphics = NULL;
516 	}
517 	return;
518 }
519 
520 // -----------------------------------------------------------------------
521 
522 sal_Bool PspSalInfoPrinter::Setup( SalFrame*, ImplJobSetup* )
523 {
524 	return sal_False;
525 }
526 
527 // -----------------------------------------------------------------------
528 
529 // This function gets the driver data and puts it into pJobSetup
530 // If pJobSetup->mpDriverData is NOT NULL, then the independend
531 // data should be merged into the driver data
532 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
533 // should be merged into the independent data
534 sal_Bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
535 {
536 	if( pJobSetup->mpDriverData )
537 		return SetData( ~0, pJobSetup );
538 
539 	copyJobDataToJobSetup( pJobSetup, m_aJobData );
540 
541     // set/clear backwards compatibility flag
542     bool bStrictSO52Compatibility = false;
543     std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
544         pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
545     if( compat_it != pJobSetup->maValueMap.end() )
546     {
547         if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
548             bStrictSO52Compatibility = true;
549     }
550     m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
551 
552 	return sal_True;
553 }
554 
555 // -----------------------------------------------------------------------
556 
557 // This function merges the independ driver data
558 // and sets the new independ data in pJobSetup
559 // Only the data must be changed, where the bit
560 // in nGetDataFlags is set
561 sal_Bool PspSalInfoPrinter::SetData(
562 	sal_uLong nSetDataFlags,
563 	ImplJobSetup* pJobSetup )
564 {
565 	JobData aData;
566 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
567 
568 	if( aData.m_pParser )
569 	{
570 		const PPDKey* pKey;
571 		const PPDValue* pValue;
572 
573 		// merge papersize if necessary
574 		if( nSetDataFlags & SAL_JOBSET_PAPERSIZE )
575 		{
576             int nWidth, nHeight;
577             if( pJobSetup->meOrientation == ORIENTATION_PORTRAIT )
578             {
579                 nWidth	= pJobSetup->mnPaperWidth;
580                 nHeight	= pJobSetup->mnPaperHeight;
581             }
582             else
583             {
584                 nWidth	= pJobSetup->mnPaperHeight;
585                 nHeight	= pJobSetup->mnPaperWidth;
586             }
587 			String aPaper;
588 
589             if( pJobSetup->mePaperFormat == PAPER_USER )
590                 aPaper = aData.m_pParser->matchPaper(
591                     TenMuToPt( pJobSetup->mnPaperWidth ),
592                     TenMuToPt( pJobSetup->mnPaperHeight ) );
593             else
594 				aPaper = rtl::OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1);
595 
596 			pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
597 			pValue = pKey ? pKey->getValue( aPaper ) : NULL;
598 			if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) )
599 				return sal_False;
600 		}
601 
602 		// merge paperbin if necessary
603 		if( nSetDataFlags & SAL_JOBSET_PAPERBIN )
604 		{
605 			pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
606 			if( pKey )
607 			{
608 				int nPaperBin = pJobSetup->mnPaperBin;
609 				if( nPaperBin == 0xffff )
610 					pValue = pKey->getDefaultValue();
611 				else
612                     pValue = pKey->getValue( pJobSetup->mnPaperBin );
613 
614                 // may fail due to constraints;
615                 // real paper bin is copied back to jobsetup in that case
616 				aData.m_aContext.setValue( pKey, pValue );
617 			}
618 			// if printer has no InputSlot key simply ignore this setting
619 			// (e.g. SGENPRT has no InputSlot)
620 		}
621 
622 		// merge orientation if necessary
623 		if( nSetDataFlags & SAL_JOBSET_ORIENTATION )
624 			aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait;
625 
626         // merge duplex if necessary
627         if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE )
628         {
629             pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
630             if( pKey )
631             {
632                 pValue = NULL;
633                 switch( pJobSetup->meDuplexMode )
634                 {
635                 case DUPLEX_OFF:
636                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
637                     if( pValue == NULL )
638                         pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "SimplexNoTumble" ) ) );
639                     break;
640                 case DUPLEX_SHORTEDGE:
641                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexTumble" ) ) );
642                     break;
643                 case DUPLEX_LONGEDGE:
644                     pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexNoTumble" ) ) );
645                     break;
646                 case DUPLEX_UNKNOWN:
647                 default:
648                     pValue = 0;
649                     break;
650                 }
651                 if( ! pValue )
652                     pValue = pKey->getDefaultValue();
653                 aData.m_aContext.setValue( pKey, pValue );
654             }
655         }
656 
657 		m_aJobData = aData;
658 		copyJobDataToJobSetup( pJobSetup, aData );
659 		return sal_True;
660 	}
661 
662 	return sal_False;
663 }
664 
665 // -----------------------------------------------------------------------
666 
667 void PspSalInfoPrinter::GetPageInfo(
668 	const ImplJobSetup* pJobSetup,
669 	long& rOutWidth, long& rOutHeight,
670 	long& rPageOffX, long& rPageOffY,
671 	long& rPageWidth, long& rPageHeight )
672 {
673 	if( ! pJobSetup )
674 		return;
675 
676 	JobData aData;
677 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
678 
679 	// get the selected page size
680 	if( aData.m_pParser )
681 	{
682 
683 		String aPaper;
684 		int width, height;
685 		int left = 0, top = 0, right = 0, bottom = 0;
686 		int nDPI = aData.m_aContext.getRenderResolution();
687 
688 
689         if( aData.m_eOrientation == psp::orientation::Portrait )
690         {
691             aData.m_aContext.getPageSize( aPaper, width, height );
692             aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
693         }
694         else
695         {
696             aData.m_aContext.getPageSize( aPaper, height, width );
697             aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
698         }
699 
700 		rPageWidth	= width * nDPI / 72;
701 		rPageHeight	= height * nDPI / 72;
702 		rPageOffX	= left * nDPI / 72;
703 		rPageOffY	= top * nDPI / 72;
704 		rOutWidth	= ( width  - left - right ) * nDPI / 72;
705 		rOutHeight	= ( height - top  - bottom ) * nDPI / 72;
706 	}
707 }
708 
709 // -----------------------------------------------------------------------
710 
711 sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
712 {
713 	if( ! pJobSetup )
714 		return 0;
715 
716 	JobData aData;
717 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
718 
719 	const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
720     return pKey ? pKey->countValues() : 0;
721 }
722 
723 // -----------------------------------------------------------------------
724 
725 String PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin )
726 {
727 	JobData aData;
728 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
729 
730 	String aRet;
731 	if( aData.m_pParser )
732 	{
733 		const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
734 		if( nPaperBin == 0xffff || ! pKey )
735 			aRet = aData.m_pParser->getDefaultInputSlot();
736 		else
737         {
738             const PPDValue* pValue = pKey->getValue( nPaperBin );
739             if( pValue )
740                 aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
741         }
742 	}
743 
744 	return aRet;
745 }
746 
747 // -----------------------------------------------------------------------
748 
749 sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType )
750 {
751 	switch( nType )
752 	{
753 		case PRINTER_CAPABILITIES_SUPPORTDIALOG:
754 			return 1;
755 		case PRINTER_CAPABILITIES_COPIES:
756 			return 0xffff;
757 		case PRINTER_CAPABILITIES_COLLATECOPIES:
758         {
759             // see if the PPD contains a value to set Collate to True
760             JobData aData;
761             JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
762 
763             const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ) : NULL;
764             const PPDValue* pVal = pKey ? pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ) : NULL;
765 
766             // PPDs don't mention the number of possible collated copies.
767             // so let's guess as many as we want ?
768 			return pVal ? 0xffff : 0;
769         }
770 		case PRINTER_CAPABILITIES_SETORIENTATION:
771 			return 1;
772 		case PRINTER_CAPABILITIES_SETDUPLEX:
773 			return 1;
774 		case PRINTER_CAPABILITIES_SETPAPERBIN:
775 			return 1;
776 		case PRINTER_CAPABILITIES_SETPAPERSIZE:
777 			return 1;
778 		case PRINTER_CAPABILITIES_SETPAPER:
779 			return 0;
780 		case PRINTER_CAPABILITIES_FAX:
781 		{
782 			PrinterInfoManager& rManager = PrinterInfoManager::get();
783 			PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) );
784 			String aFeatures( aInfo.m_aFeatures );
785 			int nTokenCount = aFeatures.GetTokenCount( ',' );
786 			for( int i = 0; i < nTokenCount; i++ )
787 			{
788 				if( aFeatures.GetToken( i ).CompareToAscii( "fax", 3 ) == COMPARE_EQUAL )
789 					return 1;
790 			}
791 			return 0;
792 		}
793 		case PRINTER_CAPABILITIES_PDF:
794 		{
795 			PrinterInfoManager& rManager = PrinterInfoManager::get();
796 			PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) );
797 			String aFeatures( aInfo.m_aFeatures );
798 			int nTokenCount = aFeatures.GetTokenCount( ',' );
799 			for( int i = 0; i < nTokenCount; i++ )
800 			{
801 				if( aFeatures.GetToken( i ).CompareToAscii( "pdf=", 4 ) == COMPARE_EQUAL )
802 					return 1;
803 			}
804 			return 0;
805 		}
806 		default: break;
807 	};
808 	return 0;
809 }
810 
811 // =======================================================================
812 
813 /*
814  *	SalPrinter
815  */
816 
817 PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
818  : m_bFax( false ),
819    m_bPdf( false ),
820    m_bSwallowFaxNo( false ),
821    m_pGraphics( NULL ),
822    m_nCopies( 1 ),
823    m_bCollate( false ),
824    m_pInfoPrinter( pInfoPrinter )
825 {
826 }
827 
828 // -----------------------------------------------------------------------
829 
830 PspSalPrinter::~PspSalPrinter()
831 {
832 }
833 
834 // -----------------------------------------------------------------------
835 
836 static String getTmpName()
837 {
838     rtl::OUString aTmp, aSys;
839     osl_createTempFile( NULL, NULL, &aTmp.pData );
840     osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData );
841 
842     return aSys;
843 }
844 
845 sal_Bool PspSalPrinter::StartJob(
846 	const XubString* pFileName,
847 	const XubString& rJobName,
848 	const XubString& rAppName,
849 	sal_uLong nCopies,
850     bool bCollate,
851     bool /*bDirect*/,
852 	ImplJobSetup* pJobSetup )
853 {
854     vcl_sal::PrinterUpdate::jobStarted();
855 
856 	m_bFax		= false;
857 	m_bPdf		= false;
858 	m_aFileName	= pFileName ? *pFileName : String();
859 	m_aTmpFile	= String();
860     m_nCopies   = nCopies;
861     m_bCollate  = bCollate;
862 
863 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
864     if( m_nCopies > 1 )
865     {
866         // in case user did not do anything (m_nCopies=1)
867         // take the default from jobsetup
868         m_aJobData.m_nCopies = m_nCopies;
869         m_aJobData.setCollate( bCollate );
870     }
871 
872 	// check wether this printer is configured as fax
873     int nMode = 0;
874 	const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
875     sal_Int32 nIndex = 0;
876     while( nIndex != -1 )
877 	{
878 		OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
879 		if( ! aToken.compareToAscii( "fax", 3 ) )
880 		{
881 			m_bFax = true;
882 			m_aTmpFile = getTmpName();
883             nMode = S_IRUSR | S_IWUSR;
884 
885 			::std::hash_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it;
886 			it = pJobSetup->maValueMap.find( ::rtl::OUString::createFromAscii( "FAX#" ) );
887 			if( it != pJobSetup->maValueMap.end() )
888 				m_aFaxNr = it->second;
889 
890             sal_Int32 nPos = 0;
891 			m_bSwallowFaxNo = ! aToken.getToken( 1, '=', nPos ).compareToAscii( "swallow", 7 ) ? true : false;
892 
893 			break;
894 		}
895 		if( ! aToken.compareToAscii( "pdf=", 4 ) )
896 		{
897 			m_bPdf = true;
898 			m_aTmpFile = getTmpName();
899             nMode = S_IRUSR | S_IWUSR;
900 
901 			if( ! m_aFileName.Len() )
902 			{
903 				m_aFileName = getPdfDir( rInfo );
904 				m_aFileName.Append( '/' );
905 				m_aFileName.Append( rJobName );
906 				m_aFileName.AppendAscii( ".pdf" );
907 			}
908 			break;
909 		}
910 	}
911 	m_aPrinterGfx.Init( m_aJobData );
912 
913     // set/clear backwards compatibility flag
914     bool bStrictSO52Compatibility = false;
915     std::hash_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
916         pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
917     if( compat_it != pJobSetup->maValueMap.end() )
918     {
919         if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
920             bStrictSO52Compatibility = true;
921     }
922     m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
923 
924 	return m_aPrintJob.StartJob( m_aTmpFile.Len() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, false ) ? sal_True : sal_False;
925 }
926 
927 // -----------------------------------------------------------------------
928 
929 sal_Bool PspSalPrinter::EndJob()
930 {
931 	sal_Bool bSuccess = m_aPrintJob.EndJob();
932 
933 	if( bSuccess )
934 	{
935 		// check for fax
936 		if( m_bFax )
937 		{
938 
939 			const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
940 			// sendAFax removes the file after use
941 			bSuccess = sendAFax( m_aFaxNr, m_aTmpFile, rInfo.m_aCommand );
942 		}
943 		else if( m_bPdf )
944 		{
945 			const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
946 			bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand );
947 		}
948 	}
949     vcl_sal::PrinterUpdate::jobEnded();
950 	return bSuccess;
951 }
952 
953 // -----------------------------------------------------------------------
954 
955 sal_Bool PspSalPrinter::AbortJob()
956 {
957     sal_Bool bAbort = m_aPrintJob.AbortJob() ? sal_True : sal_False;
958     vcl_sal::PrinterUpdate::jobEnded();
959 	return bAbort;
960 }
961 
962 // -----------------------------------------------------------------------
963 
964 SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, sal_Bool )
965 {
966 	JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
967 	m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, m_bFax ? &m_aFaxNr : NULL, m_bSwallowFaxNo, m_pInfoPrinter  );
968     m_pGraphics->SetLayout( 0 );
969     if( m_nCopies > 1 )
970     {
971         // in case user did not do anything (m_nCopies=1)
972         // take the default from jobsetup
973         m_aJobData.m_nCopies = m_nCopies;
974         m_aJobData.setCollate( m_nCopies > 1 && m_bCollate );
975     }
976 
977 	m_aPrintJob.StartPage( m_aJobData );
978 	m_aPrinterGfx.Init( m_aPrintJob );
979 
980 	return m_pGraphics;
981 }
982 
983 // -----------------------------------------------------------------------
984 
985 sal_Bool PspSalPrinter::EndPage()
986 {
987 	sal_Bool bResult = m_aPrintJob.EndPage();
988 	m_aPrinterGfx.Clear();
989 	return bResult ? sal_True : sal_False;
990 }
991 
992 // -----------------------------------------------------------------------
993 
994 sal_uLong PspSalPrinter::GetErrorCode()
995 {
996 	return 0;
997 }
998 
999 /*
1000  *  vcl::PrinterUpdate
1001  */
1002 
1003 Timer* vcl_sal::PrinterUpdate::pPrinterUpdateTimer = NULL;
1004 int vcl_sal::PrinterUpdate::nActiveJobs = 0;
1005 
1006 void vcl_sal::PrinterUpdate::doUpdate()
1007 {
1008     ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
1009     if( rManager.checkPrintersChanged( false ) && SvpSalInstance::s_pDefaultInstance )
1010     {
1011         const std::list< SalFrame* >& rList = SvpSalInstance::s_pDefaultInstance->getFrames();
1012         for( std::list< SalFrame* >::const_iterator it = rList.begin();
1013              it != rList.end(); ++it )
1014              SvpSalInstance::s_pDefaultInstance->PostEvent( *it, NULL, SALEVENT_PRINTERCHANGED );
1015     }
1016 }
1017 
1018 // -----------------------------------------------------------------------
1019 
1020 IMPL_STATIC_LINK_NOINSTANCE( vcl_sal::PrinterUpdate, UpdateTimerHdl, void*, )
1021 {
1022     if( nActiveJobs < 1 )
1023     {
1024         doUpdate();
1025         delete pPrinterUpdateTimer;
1026         pPrinterUpdateTimer = NULL;
1027     }
1028     else
1029         pPrinterUpdateTimer->Start();
1030 
1031     return 0;
1032 }
1033 
1034 // -----------------------------------------------------------------------
1035 
1036 void vcl_sal::PrinterUpdate::update()
1037 {
1038     if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1039         return;
1040 
1041     static bool bOnce = false;
1042     if( ! bOnce )
1043     {
1044         bOnce = true;
1045         // start background printer detection
1046         psp::PrinterInfoManager::get();
1047         return;
1048     }
1049 
1050     if( nActiveJobs < 1 )
1051         doUpdate();
1052     else if( ! pPrinterUpdateTimer )
1053     {
1054         pPrinterUpdateTimer = new Timer();
1055         pPrinterUpdateTimer->SetTimeout( 500 );
1056         pPrinterUpdateTimer->SetTimeoutHdl( STATIC_LINK( NULL, vcl_sal::PrinterUpdate, UpdateTimerHdl ) );
1057         pPrinterUpdateTimer->Start();
1058     }
1059 }
1060 
1061 // -----------------------------------------------------------------------
1062 
1063 void vcl_sal::PrinterUpdate::jobEnded()
1064 {
1065     nActiveJobs--;
1066     if( nActiveJobs < 1 )
1067     {
1068         if( pPrinterUpdateTimer )
1069         {
1070             pPrinterUpdateTimer->Stop();
1071             delete pPrinterUpdateTimer;
1072             pPrinterUpdateTimer = NULL;
1073             doUpdate();
1074         }
1075     }
1076 }
1077