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