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