sync master with lastest vba changes
[ooovba.git] / psprint / source / printer / printerinfomanager.cxx
blob735132573bf1df08db4c3a8ec376015ae6174cb0
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: printerinfomanager.cxx,v $
10 * $Revision: 1.50 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_psprint.hxx"
34 #include <unistd.h>
35 #include <sys/wait.h>
36 #include <signal.h>
38 #include <tools/urlobj.hxx>
39 #include <tools/stream.hxx>
40 #include <tools/debug.hxx>
41 #include <tools/config.hxx>
42 #include <cupsmgr.hxx>
43 #include <psprint/fontmanager.hxx>
44 #include <psprint/strhelper.hxx>
45 #include <rtl/strbuf.hxx>
47 #include <osl/thread.hxx>
48 #include <osl/mutex.hxx>
49 #include <osl/process.h>
51 #ifdef MACOSX
52 #include <sys/stat.h>
53 #endif
55 // filename of configuration files
56 #define PRINT_FILENAME "psprint.conf"
57 // the group of the global defaults
58 #define GLOBAL_DEFAULTS_GROUP "__Global_Printer_Defaults__"
60 #include <hash_set>
62 using namespace psp;
63 using namespace rtl;
64 using namespace osl;
66 namespace psp
68 class SystemQueueInfo : public Thread
70 mutable Mutex m_aMutex;
71 bool m_bChanged;
72 std::list< PrinterInfoManager::SystemPrintQueue >
73 m_aQueues;
74 OUString m_aCommand;
76 virtual void run();
78 public:
79 SystemQueueInfo();
80 ~SystemQueueInfo();
82 bool hasChanged() const;
83 OUString getCommand() const;
85 // sets changed status to false; therefore not const
86 void getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues );
88 } // namespace
91 * class PrinterInfoManager
94 // -----------------------------------------------------------------
96 PrinterInfoManager& PrinterInfoManager::get()
98 static PrinterInfoManager* pManager = NULL;
100 if( ! pManager )
102 pManager = CUPSManager::tryLoadCUPS();
103 if( ! pManager )
104 pManager = new PrinterInfoManager();
106 if( pManager )
107 pManager->initialize();
108 #if OSL_DEBUG_LEVEL > 1
109 fprintf( stderr, "PrinterInfoManager::get create Manager of type %d\n", pManager->getType() );
110 #endif
113 return *pManager;
116 // -----------------------------------------------------------------
118 PrinterInfoManager::PrinterInfoManager( Type eType ) :
119 m_pQueueInfo( NULL ),
120 m_eType( eType ),
121 m_bUseIncludeFeature( false ),
122 m_aSystemDefaultPaper( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ),
123 m_bDisableCUPS( false )
125 if( eType == Default )
126 m_pQueueInfo = new SystemQueueInfo();
127 initSystemDefaultPaper();
130 // -----------------------------------------------------------------
132 PrinterInfoManager::~PrinterInfoManager()
134 delete m_pQueueInfo;
137 // -----------------------------------------------------------------
139 bool PrinterInfoManager::isCUPSDisabled() const
141 return m_bDisableCUPS;
144 // -----------------------------------------------------------------
146 void PrinterInfoManager::setCUPSDisabled( bool bDisable )
148 m_bDisableCUPS = bDisable;
149 writePrinterConfig();
150 // actually we know the printers changed
151 // however this triggers reinitialization the right way
152 checkPrintersChanged( true );
155 // -----------------------------------------------------------------
157 void PrinterInfoManager::initSystemDefaultPaper()
159 bool bSuccess = false;
161 // try libpaper
163 // #i78617# workaround missing paperconf command
164 FILE* pPipe = popen( "sh -c paperconf 2>/dev/null", "r" );
165 if( pPipe )
167 char pBuffer[ 1024 ];
168 *pBuffer = 0;
169 fgets( pBuffer, sizeof(pBuffer)-1, pPipe );
170 pclose( pPipe );
172 ByteString aPaper( pBuffer );
173 aPaper = WhitespaceToSpace( aPaper );
174 if( aPaper.Len() )
176 m_aSystemDefaultPaper = OUString( OStringToOUString( aPaper, osl_getThreadTextEncoding() ) );
177 bSuccess = true;
178 #if OSL_DEBUG_LEVEL > 1
179 fprintf( stderr, "paper from paperconf = %s\n", aPaper.GetBuffer() );
180 #endif
182 if( bSuccess )
183 return;
186 // default value is Letter for US (en_US), Cannada (en_CA, fr_CA); else A4
187 // en will be interpreted as en_US
189 // note: at this point m_aSystemDefaultPaper is set to "A4" from the constructor
191 // check for LC_PAPER
192 const char* pPaperLang = getenv( "LC_PAPER" );
193 if( pPaperLang && *pPaperLang )
195 OString aLang( pPaperLang );
196 if( aLang.getLength() > 5 )
197 aLang = aLang.copy( 0, 5 );
198 if( aLang.getLength() == 5 )
200 if( aLang.equalsIgnoreAsciiCase( "en_us" )
201 || aLang.equalsIgnoreAsciiCase( "en_ca" )
202 || aLang.equalsIgnoreAsciiCase( "fr_ca" )
204 m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) );
206 else if( aLang.getLength() == 2 && aLang.equalsIgnoreAsciiCase( "en" ) )
207 m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) );
208 return;
211 // use process locale to determine paper
212 rtl_Locale* pLoc = NULL;
213 osl_getProcessLocale( &pLoc );
214 if( pLoc )
216 if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Language->buffer, pLoc->Language->length, "en") )
218 if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "us")
219 || 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "ca")
220 || pLoc->Country->length == 0
222 m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) );
224 else if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Language->buffer, pLoc->Language->length, "fr") )
226 if( 0 == rtl_ustr_ascii_compareIgnoreAsciiCase_WithLength( pLoc->Country->buffer, pLoc->Country->length, "ca") )
227 m_aSystemDefaultPaper = OUString( RTL_CONSTASCII_USTRINGPARAM( "Letter" ) );
232 // -----------------------------------------------------------------
234 bool PrinterInfoManager::checkPrintersChanged( bool bWait )
236 // check if files were created, deleted or modified since initialize()
237 ::std::list< WatchFile >::const_iterator it;
238 bool bChanged = false;
239 for( it = m_aWatchFiles.begin(); it != m_aWatchFiles.end() && ! bChanged; ++it )
241 DirectoryItem aItem;
242 if( DirectoryItem::get( it->m_aFilePath, aItem ) )
244 if( it->m_aModified.Seconds != 0 )
245 bChanged = true; // file probably has vanished
247 else
249 FileStatus aStatus( FileStatusMask_ModifyTime );
250 if( aItem.getFileStatus( aStatus ) )
251 bChanged = true; // unlikely but not impossible
252 else
254 TimeValue aModified = aStatus.getModifyTime();
255 if( aModified.Seconds != it->m_aModified.Seconds )
256 bChanged = true;
261 if( bWait && m_pQueueInfo )
263 #if OSL_DEBUG_LEVEL > 1
264 fprintf( stderr, "syncing printer discovery thread\n" );
265 #endif
266 m_pQueueInfo->join();
267 #if OSL_DEBUG_LEVEL > 1
268 fprintf( stderr, "done: syncing printer discovery thread\n" );
269 #endif
272 if( ! bChanged && m_pQueueInfo )
273 bChanged = m_pQueueInfo->hasChanged();
274 if( bChanged )
276 initialize();
279 return bChanged;
282 // -----------------------------------------------------------------
284 void PrinterInfoManager::initialize()
286 m_bUseIncludeFeature = false;
287 rtl_TextEncoding aEncoding = gsl_getSystemTextEncoding();
288 m_aPrinters.clear();
289 m_aWatchFiles.clear();
290 OUString aDefaultPrinter;
292 // first initialize the global defaults
293 // have to iterate over all possible files
294 // there should be only one global setup section in all
295 // available config files
296 m_aGlobalDefaults = PrinterInfo();
298 // need a parser for the PPDContext. generic printer should do.
299 m_aGlobalDefaults.m_pParser = PPDParser::getParser( String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) );
300 m_aGlobalDefaults.m_aContext.setParser( m_aGlobalDefaults.m_pParser );
301 m_aGlobalDefaults.m_bPerformFontSubstitution = true;
302 m_bDisableCUPS = false;
304 if( ! m_aGlobalDefaults.m_pParser )
306 #if OSL_DEBUG_LEVEL > 1
307 fprintf( stderr, "Error: no default PPD file SGENPRT available, shutting down psprint...\n" );
308 #endif
309 return;
312 std::list< OUString > aDirList;
313 psp::getPrinterPathList( aDirList, NULL );
314 std::list< OUString >::const_iterator print_dir_it;
315 for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it )
317 INetURLObject aFile( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
318 aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) );
319 Config aConfig( aFile.PathToFileName() );
320 if( aConfig.HasGroup( GLOBAL_DEFAULTS_GROUP ) )
322 #if OSL_DEBUG_LEVEL > 1
323 fprintf( stderr, "found global defaults in %s\n", OUStringToOString( aFile.PathToFileName(), RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
324 #endif
325 aConfig.SetGroup( GLOBAL_DEFAULTS_GROUP );
327 ByteString aValue( aConfig.ReadKey( "Copies" ) );
328 if( aValue.Len() )
329 m_aGlobalDefaults.m_nCopies = aValue.ToInt32();
331 aValue = aConfig.ReadKey( "Orientation" );
332 if( aValue.Len() )
333 m_aGlobalDefaults.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait;
335 aValue = aConfig.ReadKey( "MarginAdjust" );
336 if( aValue.Len() )
338 m_aGlobalDefaults.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32();
339 m_aGlobalDefaults.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32();
340 m_aGlobalDefaults.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32();
341 m_aGlobalDefaults.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32();
344 aValue = aConfig.ReadKey( "ColorDepth", "24" );
345 if( aValue.Len() )
346 m_aGlobalDefaults.m_nColorDepth = aValue.ToInt32();
348 aValue = aConfig.ReadKey( "ColorDevice" );
349 if( aValue.Len() )
350 m_aGlobalDefaults.m_nColorDevice = aValue.ToInt32();
352 aValue = aConfig.ReadKey( "PSLevel" );
353 if( aValue.Len() )
354 m_aGlobalDefaults.m_nPSLevel = aValue.ToInt32();
356 aValue = aConfig.ReadKey( "PerformFontSubstitution" );
357 if( aValue.Len() )
359 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
360 m_aGlobalDefaults.m_bPerformFontSubstitution = true;
361 else
362 m_aGlobalDefaults.m_bPerformFontSubstitution = false;
365 aValue = aConfig.ReadKey( "DisableCUPS" );
366 if( aValue.Len() )
368 if( aValue.Equals( "1" ) || aValue.EqualsIgnoreCaseAscii( "true" ) )
369 m_bDisableCUPS = true;
370 else
371 m_bDisableCUPS = false;
374 // get the PPDContext of global JobData
375 for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ )
377 ByteString aKey( aConfig.GetKeyName( nKey ) );
378 if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL )
380 aValue = aConfig.ReadKey( aKey );
381 const PPDKey* pKey = m_aGlobalDefaults.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) );
382 if( pKey )
384 m_aGlobalDefaults.m_aContext.
385 setValue( pKey,
386 aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ),
387 TRUE );
390 else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL )
392 aValue = aConfig.ReadKey( aKey );
393 m_aGlobalDefaults.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 );
396 #if OSL_DEBUG_LEVEL > 1
397 fprintf( stderr, "global settings: fontsubst = %s, %d substitutes\n", m_aGlobalDefaults.m_bPerformFontSubstitution ? "true" : "false", m_aGlobalDefaults.m_aFontSubstitutes.size() );
398 #endif
401 setDefaultPaper( m_aGlobalDefaults.m_aContext );
402 fillFontSubstitutions( m_aGlobalDefaults );
404 // now collect all available printers
405 for( print_dir_it = aDirList.begin(); print_dir_it != aDirList.end(); ++print_dir_it )
407 INetURLObject aDir( *print_dir_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
408 INetURLObject aFile( aDir );
409 aFile.Append( String( RTL_CONSTASCII_USTRINGPARAM( PRINT_FILENAME ) ) );
411 // check directory validity
412 OUString aUniPath;
413 FileBase::getFileURLFromSystemPath( aDir.PathToFileName(), aUniPath );
414 Directory aDirectory( aUniPath );
415 if( aDirectory.open() )
416 continue;
417 aDirectory.close();
420 FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aUniPath );
421 FileStatus aStatus( FileStatusMask_ModifyTime );
422 DirectoryItem aItem;
424 // setup WatchFile list
425 WatchFile aWatchFile;
426 aWatchFile.m_aFilePath = aUniPath;
427 if( ! DirectoryItem::get( aUniPath, aItem ) &&
428 ! aItem.getFileStatus( aStatus ) )
430 aWatchFile.m_aModified = aStatus.getModifyTime();
432 else
434 aWatchFile.m_aModified.Seconds = 0;
435 aWatchFile.m_aModified.Nanosec = 0;
437 m_aWatchFiles.push_back( aWatchFile );
439 Config aConfig( aFile.PathToFileName() );
440 for( int nGroup = 0; nGroup < aConfig.GetGroupCount(); nGroup++ )
442 aConfig.SetGroup( aConfig.GetGroupName( nGroup ) );
443 ByteString aValue = aConfig.ReadKey( "Printer" );
444 if( aValue.Len() )
446 OUString aPrinterName;
448 int nNamePos = aValue.Search( '/' );
449 // check for valid value of "Printer"
450 if( nNamePos == STRING_NOTFOUND )
451 continue;
453 Printer aPrinter;
454 // initialize to global defaults
455 aPrinter.m_aInfo = m_aGlobalDefaults;
456 // global settings do not default the printer substitution
457 // list ! the substitution list in there is only used for
458 // newly created printers
459 aPrinter.m_aInfo.m_aFontSubstitutes.clear();
460 aPrinter.m_aInfo.m_aFontSubstitutions.clear();
462 aPrinterName = String( aValue.Copy( nNamePos+1 ), RTL_TEXTENCODING_UTF8 );
463 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
464 aPrinter.m_aInfo.m_aDriverName = String( aValue.Copy( 0, nNamePos ), RTL_TEXTENCODING_UTF8 );
466 // set parser, merge settings
467 // don't do this for CUPS printers as this is done
468 // by the CUPS system itself
469 if( aPrinter.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 )
471 aPrinter.m_aInfo.m_pParser = PPDParser::getParser( aPrinter.m_aInfo.m_aDriverName );
472 aPrinter.m_aInfo.m_aContext.setParser( aPrinter.m_aInfo.m_pParser );
473 // note: setParser also purges the context
475 // ignore this printer if its driver is not found
476 if( ! aPrinter.m_aInfo.m_pParser )
477 continue;
479 // merge the ppd context keys if the printer has the same keys and values
480 // this is a bit tricky, since it involves mixing two PPDs
481 // without constraints which might end up badly
482 // this feature should be use with caution
483 // it is mainly to select default paper sizes for new printers
484 for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ )
486 const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified );
487 const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey );
488 const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL;
489 if( pDefKey && pPrinterKey )
490 // at least the options exist in both PPDs
492 if( pDefValue )
494 const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption );
495 if( pPrinterValue )
496 // the printer has a corresponding option for the key
497 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue );
499 else
500 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL );
504 aValue = aConfig.ReadKey( "Command" );
505 // no printer without a command
506 if( ! aValue.Len() )
508 /* TODO:
509 * porters: please append your platform to the Solaris
510 * case if your platform has SystemV printing per default.
512 #if defined SOLARIS || defined(IRIX)
513 aValue = "lp";
514 #else
515 aValue = "lpr";
516 #endif
518 aPrinter.m_aInfo.m_aCommand = String( aValue, RTL_TEXTENCODING_UTF8 );
521 aValue = aConfig.ReadKey( "QuickCommand" );
522 aPrinter.m_aInfo.m_aQuickCommand = String( aValue, RTL_TEXTENCODING_UTF8 );
524 aValue = aConfig.ReadKey( "Features" );
525 aPrinter.m_aInfo.m_aFeatures = String( aValue, RTL_TEXTENCODING_UTF8 );
527 // override the settings in m_aGlobalDefaults if keys exist
528 aValue = aConfig.ReadKey( "DefaultPrinter" );
529 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
530 aDefaultPrinter = aPrinterName;
532 aValue = aConfig.ReadKey( "Location" );
533 aPrinter.m_aInfo.m_aLocation = String( aValue, RTL_TEXTENCODING_UTF8 );
535 aValue = aConfig.ReadKey( "Comment" );
536 aPrinter.m_aInfo.m_aComment = String( aValue, RTL_TEXTENCODING_UTF8 );
538 aValue = aConfig.ReadKey( "Copies" );
539 if( aValue.Len() )
540 aPrinter.m_aInfo.m_nCopies = aValue.ToInt32();
542 aValue = aConfig.ReadKey( "Orientation" );
543 if( aValue.Len() )
544 aPrinter.m_aInfo.m_eOrientation = aValue.EqualsIgnoreCaseAscii( "Landscape" ) ? orientation::Landscape : orientation::Portrait;
546 aValue = aConfig.ReadKey( "MarginAdjust" );
547 if( aValue.Len() )
549 aPrinter.m_aInfo.m_nLeftMarginAdjust = aValue.GetToken( 0, ',' ).ToInt32();
550 aPrinter.m_aInfo.m_nRightMarginAdjust = aValue.GetToken( 1, ',' ).ToInt32();
551 aPrinter.m_aInfo.m_nTopMarginAdjust = aValue.GetToken( 2, ',' ).ToInt32();
552 aPrinter.m_aInfo.m_nBottomMarginAdjust = aValue.GetToken( 3, ',' ).ToInt32();
555 aValue = aConfig.ReadKey( "ColorDepth" );
556 if( aValue.Len() )
557 aPrinter.m_aInfo.m_nColorDepth = aValue.ToInt32();
559 aValue = aConfig.ReadKey( "ColorDevice" );
560 if( aValue.Len() )
561 aPrinter.m_aInfo.m_nColorDevice = aValue.ToInt32();
563 aValue = aConfig.ReadKey( "PSLevel" );
564 if( aValue.Len() )
565 aPrinter.m_aInfo.m_nPSLevel = aValue.ToInt32();
567 aValue = aConfig.ReadKey( "PerformFontSubstitution" );
568 if( ! aValue.Equals( "0" ) && ! aValue.EqualsIgnoreCaseAscii( "false" ) )
569 aPrinter.m_aInfo.m_bPerformFontSubstitution = true;
570 else
571 aPrinter.m_aInfo.m_bPerformFontSubstitution = false;
573 // now iterate over all keys to extract multi key information:
574 // 1. PPDContext information
575 // 2. Font substitution table
576 for( int nKey = 0; nKey < aConfig.GetKeyCount(); nKey++ )
578 ByteString aKey( aConfig.GetKeyName( nKey ) );
579 if( aKey.CompareTo( "PPD_", 4 ) == COMPARE_EQUAL && aPrinter.m_aInfo.m_pParser )
581 aValue = aConfig.ReadKey( aKey );
582 const PPDKey* pKey = aPrinter.m_aInfo.m_pParser->getKey( String( aKey.Copy( 4 ), RTL_TEXTENCODING_ISO_8859_1 ) );
583 if( pKey )
585 aPrinter.m_aInfo.m_aContext.
586 setValue( pKey,
587 aValue.Equals( "*nil" ) ? NULL : pKey->getValue( String( aValue, RTL_TEXTENCODING_ISO_8859_1 ) ),
588 TRUE );
591 else if( aKey.Len() > 10 && aKey.CompareTo("SubstFont_", 10 ) == COMPARE_EQUAL )
593 aValue = aConfig.ReadKey( aKey );
594 aPrinter.m_aInfo.m_aFontSubstitutes[ OStringToOUString( aKey.Copy( 10 ), RTL_TEXTENCODING_ISO_8859_1 ) ] = OStringToOUString( aValue, RTL_TEXTENCODING_ISO_8859_1 );
598 setDefaultPaper( aPrinter.m_aInfo.m_aContext );
599 fillFontSubstitutions( aPrinter.m_aInfo );
601 // finally insert printer
602 FileBase::getFileURLFromSystemPath( aFile.PathToFileName(), aPrinter.m_aFile );
603 aPrinter.m_bModified = false;
604 aPrinter.m_aGroup = aConfig.GetGroupName( nGroup );
605 std::hash_map< OUString, Printer, OUStringHash >::const_iterator find_it =
606 m_aPrinters.find( aPrinterName );
607 if( find_it != m_aPrinters.end() )
609 aPrinter.m_aAlternateFiles = find_it->second.m_aAlternateFiles;
610 aPrinter.m_aAlternateFiles.push_front( find_it->second.m_aFile );
612 m_aPrinters[ aPrinterName ] = aPrinter;
617 // set default printer
618 if( m_aPrinters.size() )
620 if( m_aPrinters.find( aDefaultPrinter ) == m_aPrinters.end() )
621 aDefaultPrinter = m_aPrinters.begin()->first;
623 else
624 aDefaultPrinter = OUString();
625 m_aDefaultPrinter = aDefaultPrinter;
627 if( m_eType != Default )
628 return;
630 // add a default printer for every available print queue
631 // merge paper and font substitution from default printer,
632 // all else from global defaults
633 PrinterInfo aMergeInfo( m_aGlobalDefaults );
634 aMergeInfo.m_aDriverName = String( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) );
635 aMergeInfo.m_aFeatures = String( RTL_CONSTASCII_USTRINGPARAM( "autoqueue" ) );
637 if( m_aDefaultPrinter.getLength() )
639 PrinterInfo aDefaultInfo( getPrinterInfo( m_aDefaultPrinter ) );
640 aMergeInfo.m_bPerformFontSubstitution = aDefaultInfo.m_bPerformFontSubstitution;
641 fillFontSubstitutions( aMergeInfo );
643 const PPDKey* pDefKey = aDefaultInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
644 const PPDKey* pMergeKey = aMergeInfo.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
645 const PPDValue* pDefValue = aDefaultInfo.m_aContext.getValue( pDefKey );
646 const PPDValue* pMergeValue = pMergeKey ? pMergeKey->getValue( pDefValue->m_aOption ) : NULL;
647 if( pMergeKey && pMergeValue )
648 aMergeInfo.m_aContext.setValue( pMergeKey, pMergeValue );
651 getSystemPrintQueues();
652 for( ::std::list< SystemPrintQueue >::iterator it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it )
654 String aPrinterName( RTL_CONSTASCII_USTRINGPARAM( "<" ) );
655 aPrinterName += String( it->m_aQueue );
656 aPrinterName.Append( '>' );
658 if( m_aPrinters.find( aPrinterName ) != m_aPrinters.end() )
659 // probably user made this one permanent in padmin
660 continue;
662 String aCmd( m_aSystemPrintCommand );
663 aCmd.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) ), it->m_aQueue );
665 Printer aPrinter;
667 // initialize to merged defaults
668 aPrinter.m_aInfo = aMergeInfo;
669 aPrinter.m_aInfo.m_aPrinterName = aPrinterName;
670 aPrinter.m_aInfo.m_aCommand = aCmd;
671 aPrinter.m_aInfo.m_aComment = it->m_aComment;
672 aPrinter.m_aInfo.m_aLocation = it->m_aLocation;
673 aPrinter.m_bModified = false;
674 aPrinter.m_aGroup = ByteString( aPrinterName, aEncoding ); //provide group name in case user makes this one permanent in padmin
676 m_aPrinters[ aPrinterName ] = aPrinter;
680 // -----------------------------------------------------------------
682 void PrinterInfoManager::listPrinters( ::std::list< OUString >& rList ) const
684 ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it;
685 rList.clear();
686 for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it )
687 rList.push_back( it->first );
690 // -----------------------------------------------------------------
692 const PrinterInfo& PrinterInfoManager::getPrinterInfo( const OUString& rPrinter ) const
694 static PrinterInfo aEmptyInfo;
695 ::std::hash_map< OUString, Printer, OUStringHash >::const_iterator it = m_aPrinters.find( rPrinter );
697 DBG_ASSERT( it != m_aPrinters.end(), "Do not ask for info about nonexistant printers" );
699 return it != m_aPrinters.end() ? it->second.m_aInfo : aEmptyInfo;
702 // -----------------------------------------------------------------
704 void PrinterInfoManager::changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo )
706 ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinter );
708 DBG_ASSERT( it != m_aPrinters.end(), "Do not change nonexistant printers" );
710 if( it != m_aPrinters.end() )
712 it->second.m_aInfo = rNewInfo;
713 // recalculate font substitutions
714 fillFontSubstitutions( it->second.m_aInfo );
715 it->second.m_bModified = true;
716 writePrinterConfig();
720 // -----------------------------------------------------------------
722 // need to check writeability / creatability of config files
723 static bool checkWriteability( const OUString& rUniPath )
725 bool bRet = false;
726 OUString aSysPath;
727 FileBase::getSystemPathFromFileURL( rUniPath, aSysPath );
728 SvFileStream aStream( aSysPath, STREAM_READ | STREAM_WRITE );
729 if( aStream.IsOpen() && aStream.IsWritable() )
730 bRet = true;
731 return bRet;
734 bool PrinterInfoManager::writePrinterConfig()
736 // find at least one writeable config
737 ::std::hash_map< OUString, Config*, OUStringHash > files;
738 ::std::hash_map< OUString, int, OUStringHash > rofiles;
739 ::std::hash_map< OUString, Config*, OUStringHash >::iterator file_it;
741 for( ::std::list< WatchFile >::const_iterator wit = m_aWatchFiles.begin(); wit != m_aWatchFiles.end(); ++wit )
743 if( checkWriteability( wit->m_aFilePath ) )
745 files[ wit->m_aFilePath ] = new Config( wit->m_aFilePath );
746 break;
750 if( files.empty() )
751 return false;
753 Config* pGlobal = files.begin()->second;
754 pGlobal->SetGroup( GLOBAL_DEFAULTS_GROUP );
755 pGlobal->WriteKey( "DisableCUPS", m_bDisableCUPS ? "true" : "false" );
757 ::std::hash_map< OUString, Printer, OUStringHash >::iterator it;
758 for( it = m_aPrinters.begin(); it != m_aPrinters.end(); ++it )
760 if( ! it->second.m_bModified )
761 // printer was not changed, do nothing
762 continue;
764 // don't save autoqueue printers
765 sal_Int32 nIndex = 0;
766 bool bAutoQueue = false;
767 while( nIndex != -1 && ! bAutoQueue )
769 OUString aToken( it->second.m_aInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
770 if( aToken.getLength() && aToken.compareToAscii( "autoqueue" ) == 0 )
771 bAutoQueue = true;
773 if( bAutoQueue )
774 continue;
776 if( it->second.m_aFile.getLength() )
778 // check if file is writable
779 if( files.find( it->second.m_aFile ) == files.end() )
781 bool bInsertToNewFile = false;
782 // maybe it is simply not inserted yet
783 if( rofiles.find( it->second.m_aFile ) == rofiles.end() )
785 if( checkWriteability( it->second.m_aFile ) )
786 files[ it->second.m_aFile ] = new Config( it->second.m_aFile );
787 else
788 bInsertToNewFile = true;
790 else
791 bInsertToNewFile = true;
792 // original file is read only, insert printer in a new writeable file
793 if( bInsertToNewFile )
795 rofiles[ it->second.m_aFile ] = 1;
796 // update alternate file list
797 // the remove operation ensures uniqueness of each alternate
798 it->second.m_aAlternateFiles.remove( it->second.m_aFile );
799 it->second.m_aAlternateFiles.remove( files.begin()->first );
800 it->second.m_aAlternateFiles.push_front( it->second.m_aFile );
801 // update file
802 it->second.m_aFile = files.begin()->first;
806 else // a new printer, write it to the first file available
807 it->second.m_aFile = files.begin()->first;
809 if( ! it->second.m_aGroup.getLength() ) // probably a new printer
810 it->second.m_aGroup = OString( it->first.getStr(), it->first.getLength(), RTL_TEXTENCODING_UTF8 );
812 if( files.find( it->second.m_aFile ) != files.end() )
814 Config* pConfig = files[ it->second.m_aFile ];
815 pConfig->DeleteGroup( it->second.m_aGroup ); // else some old keys may remain
816 pConfig->SetGroup( it->second.m_aGroup );
818 ByteString aValue( String( it->second.m_aInfo.m_aDriverName ), RTL_TEXTENCODING_UTF8 );
819 aValue += '/';
820 aValue += ByteString( String( it->first ), RTL_TEXTENCODING_UTF8 );
821 pConfig->WriteKey( "Printer", aValue );
822 pConfig->WriteKey( "DefaultPrinter", it->first == m_aDefaultPrinter ? "1" : "0" );
823 pConfig->WriteKey( "Location", ByteString( String( it->second.m_aInfo.m_aLocation ), RTL_TEXTENCODING_UTF8 ) );
824 pConfig->WriteKey( "Comment", ByteString( String( it->second.m_aInfo.m_aComment ), RTL_TEXTENCODING_UTF8 ) );
825 pConfig->WriteKey( "Command", ByteString( String( it->second.m_aInfo.m_aCommand ), RTL_TEXTENCODING_UTF8 ) );
826 pConfig->WriteKey( "QuickCommand", ByteString( String( it->second.m_aInfo.m_aQuickCommand ), RTL_TEXTENCODING_UTF8 ) );
827 pConfig->WriteKey( "Features", ByteString( String( it->second.m_aInfo.m_aFeatures ), RTL_TEXTENCODING_UTF8 ) );
828 pConfig->WriteKey( "Copies", ByteString::CreateFromInt32( it->second.m_aInfo.m_nCopies ) );
829 pConfig->WriteKey( "Orientation", it->second.m_aInfo.m_eOrientation == orientation::Landscape ? "Landscape" : "Portrait" );
830 pConfig->WriteKey( "PSLevel", ByteString::CreateFromInt32( it->second.m_aInfo.m_nPSLevel ) );
831 pConfig->WriteKey( "ColorDevice", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDevice ) );
832 pConfig->WriteKey( "ColorDepth", ByteString::CreateFromInt32( it->second.m_aInfo.m_nColorDepth ) );
833 aValue = ByteString::CreateFromInt32( it->second.m_aInfo.m_nLeftMarginAdjust );
834 aValue += ',';
835 aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nRightMarginAdjust );
836 aValue += ',';
837 aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nTopMarginAdjust );
838 aValue += ',';
839 aValue += ByteString::CreateFromInt32( it->second.m_aInfo.m_nBottomMarginAdjust );
840 pConfig->WriteKey( "MarginAdjust", aValue );
842 if( it->second.m_aInfo.m_aDriverName.compareToAscii( "CUPS:", 5 ) != 0 )
844 // write PPDContext (not for CUPS)
845 for( int i = 0; i < it->second.m_aInfo.m_aContext.countValuesModified(); i++ )
847 const PPDKey* pKey = it->second.m_aInfo.m_aContext.getModifiedKey( i );
848 ByteString aKey( "PPD_" );
849 aKey += ByteString( pKey->getKey(), RTL_TEXTENCODING_ISO_8859_1 );
851 const PPDValue* pValue = it->second.m_aInfo.m_aContext.getValue( pKey );
852 aValue = pValue ? ByteString( pValue->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ) : ByteString( "*nil" );
853 pConfig->WriteKey( aKey, aValue );
857 // write font substitution table
858 pConfig->WriteKey( "PerformFontSubstitution", it->second.m_aInfo.m_bPerformFontSubstitution ? "true" : "false" );
859 for( ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst = it->second.m_aInfo.m_aFontSubstitutes.begin();
860 subst != it->second.m_aInfo.m_aFontSubstitutes.end(); ++subst )
862 ByteString aKey( "SubstFont_" );
863 aKey.Append( OUStringToOString( subst->first, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
864 pConfig->WriteKey( aKey, OUStringToOString( subst->second, RTL_TEXTENCODING_ISO_8859_1 ) );
869 // get rid of Config objects. this also writes any changes
870 for( file_it = files.begin(); file_it != files.end(); ++file_it )
871 delete file_it->second;
873 return true;
876 // -----------------------------------------------------------------
878 bool PrinterInfoManager::addPrinter( const OUString& rPrinterName, const OUString& rDriverName )
880 bool bSuccess = false;
882 const PPDParser* pParser = NULL;
883 if( m_aPrinters.find( rPrinterName ) == m_aPrinters.end() && ( pParser = PPDParser::getParser( rDriverName ) ) )
885 Printer aPrinter;
886 aPrinter.m_bModified = true;
887 aPrinter.m_aInfo = m_aGlobalDefaults;
888 aPrinter.m_aInfo.m_aDriverName = rDriverName;
889 aPrinter.m_aInfo.m_pParser = pParser;
890 aPrinter.m_aInfo.m_aContext.setParser( pParser );
891 aPrinter.m_aInfo.m_aPrinterName = rPrinterName;
893 fillFontSubstitutions( aPrinter.m_aInfo );
894 // merge PPD values with global defaults
895 for( int nPPDValueModified = 0; nPPDValueModified < m_aGlobalDefaults.m_aContext.countValuesModified(); nPPDValueModified++ )
897 const PPDKey* pDefKey = m_aGlobalDefaults.m_aContext.getModifiedKey( nPPDValueModified );
898 const PPDValue* pDefValue = m_aGlobalDefaults.m_aContext.getValue( pDefKey );
899 const PPDKey* pPrinterKey = pDefKey ? aPrinter.m_aInfo.m_pParser->getKey( pDefKey->getKey() ) : NULL;
900 if( pDefKey && pPrinterKey )
901 // at least the options exist in both PPDs
903 if( pDefValue )
905 const PPDValue* pPrinterValue = pPrinterKey->getValue( pDefValue->m_aOption );
906 if( pPrinterValue )
907 // the printer has a corresponding option for the key
908 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, pPrinterValue );
910 else
911 aPrinter.m_aInfo.m_aContext.setValue( pPrinterKey, NULL );
915 m_aPrinters[ rPrinterName ] = aPrinter;
916 bSuccess = true;
917 #if OSL_DEBUG_LEVEL > 1
918 fprintf( stderr, "new printer %s, level = %d, colordevice = %d, depth = %d\n",
919 OUStringToOString( rPrinterName, osl_getThreadTextEncoding() ).getStr(),
920 m_aPrinters[rPrinterName].m_aInfo.m_nPSLevel,
921 m_aPrinters[rPrinterName].m_aInfo.m_nColorDevice,
922 m_aPrinters[rPrinterName].m_aInfo.m_nColorDepth );
923 #endif
924 // comment: logically one should writePrinterConfig() here
925 // but immediately after addPrinter() a changePrinterInfo()
926 // will follow (see padmin code), which writes it again,
927 // so we can currently save some performance here
929 return bSuccess;
932 // -----------------------------------------------------------------
934 bool PrinterInfoManager::removePrinter( const OUString& rPrinterName, bool bCheckOnly )
936 bool bSuccess = true;
938 ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName );
939 if( it != m_aPrinters.end() )
941 if( it->second.m_aFile.getLength() )
943 // this printer already exists in a config file
946 // check writeability of config file(s)
947 if( ! checkWriteability( it->second.m_aFile ) )
948 bSuccess = false;
949 else
951 for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin();
952 file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it )
954 if( ! checkWriteability( *file_it ) )
955 bSuccess = false;
958 if( bSuccess && ! bCheckOnly )
961 Config aConfig( it->second.m_aFile );
962 aConfig.DeleteGroup( it->second.m_aGroup );
963 aConfig.Flush();
964 for( std::list< OUString >::const_iterator file_it = it->second.m_aAlternateFiles.begin();
965 file_it != it->second.m_aAlternateFiles.end() && bSuccess; ++file_it )
967 Config aAltConfig( *file_it );
968 aAltConfig.DeleteGroup( it->second.m_aGroup );
969 aAltConfig.Flush();
973 if( bSuccess && ! bCheckOnly )
975 m_aPrinters.erase( it );
976 // need this here because someone may call
977 // checkPrintersChanged after the removal
978 // but then other added printers were not flushed
979 // to disk, so they are discarded
980 writePrinterConfig();
983 return bSuccess;
986 // -----------------------------------------------------------------
988 bool PrinterInfoManager::setDefaultPrinter( const OUString& rPrinterName )
990 bool bSuccess = false;
992 ::std::hash_map< OUString, Printer, OUStringHash >::iterator it = m_aPrinters.find( rPrinterName );
993 if( it != m_aPrinters.end() )
995 bSuccess = true;
996 it->second.m_bModified = true;
997 if( ( it = m_aPrinters.find( m_aDefaultPrinter ) ) != m_aPrinters.end() )
998 it->second.m_bModified = true;
999 m_aDefaultPrinter = rPrinterName;
1000 writePrinterConfig();
1002 return bSuccess;
1005 // -----------------------------------------------------------------
1006 bool PrinterInfoManager::addOrRemovePossible() const
1008 return true;
1011 // -----------------------------------------------------------------
1013 void PrinterInfoManager::fillFontSubstitutions( PrinterInfo& rInfo ) const
1015 PrintFontManager& rFontManager( PrintFontManager::get() );
1016 rInfo.m_aFontSubstitutions.clear();
1018 if( ! rInfo.m_bPerformFontSubstitution ||
1019 ! rInfo.m_aFontSubstitutes.size() )
1020 return;
1022 ::std::list< FastPrintFontInfo > aFonts;
1023 ::std::hash_map< OUString, ::std::list< FastPrintFontInfo >, OUStringHash > aPrinterFonts;
1024 rFontManager.getFontListWithFastInfo( aFonts, rInfo.m_pParser );
1026 // get builtin fonts
1027 ::std::list< FastPrintFontInfo >::const_iterator it;
1028 for( it = aFonts.begin(); it != aFonts.end(); ++it )
1029 if( it->m_eType == fonttype::Builtin )
1030 aPrinterFonts[ it->m_aFamilyName.toAsciiLowerCase() ].push_back( *it );
1032 // map lower case, so build a local copy of the font substitutions
1033 ::std::hash_map< OUString, OUString, OUStringHash > aSubstitutions;
1034 ::std::hash_map< OUString, OUString, OUStringHash >::const_iterator subst;
1035 for( subst = rInfo.m_aFontSubstitutes.begin(); subst != rInfo.m_aFontSubstitutes.end(); ++subst )
1037 OUString aFamily( subst->first.toAsciiLowerCase() );
1038 // first look if there is a builtin of this family
1039 // in this case override the substitution table
1040 if( aPrinterFonts.find( aFamily ) != aPrinterFonts.end() )
1041 aSubstitutions[ aFamily ] = aFamily;
1042 else
1043 aSubstitutions[ aFamily ] = subst->second.toAsciiLowerCase();
1047 // now find substitutions
1048 for( it = aFonts.begin(); it != aFonts.end(); ++it )
1050 if( it->m_eType != fonttype::Builtin )
1052 OUString aFamily( it->m_aFamilyName.toAsciiLowerCase() );
1053 subst = aSubstitutions.find( aFamily );
1054 if( subst != aSubstitutions.end() )
1056 // search a substitution
1057 const ::std::list< FastPrintFontInfo >& rBuiltins( aPrinterFonts[ aSubstitutions[ aFamily ] ] );
1058 ::std::list< FastPrintFontInfo >::const_iterator builtin;
1059 int nLastMatch = -10000;
1060 fontID nSubstitute = -1;
1061 for( builtin = rBuiltins.begin(); builtin != rBuiltins.end(); ++builtin )
1063 int nMatch = 0;
1064 int nDiff;
1065 if( builtin->m_eItalic == it->m_eItalic )
1066 nMatch += 8000;
1068 nDiff = builtin->m_eWeight - it->m_eWeight;
1069 nDiff = nDiff < 0 ? -nDiff : nDiff;
1070 nMatch += 4000 - 1000*nDiff;
1072 nDiff = builtin->m_eWidth - it->m_eWidth;
1073 nDiff = nDiff < 0 ? -nDiff : nDiff;
1074 nMatch += 2000 - 500*nDiff;
1076 if( nMatch > nLastMatch )
1078 nLastMatch = nMatch;
1079 nSubstitute = builtin->m_nID;
1082 if( nSubstitute != -1 )
1084 rInfo.m_aFontSubstitutions[ it->m_nID ] = nSubstitute;
1085 #if OSL_DEBUG_LEVEL > 2
1086 FastPrintFontInfo aInfo;
1087 rFontManager.getFontFastInfo( nSubstitute, aInfo );
1088 fprintf( stderr,
1089 "substitute %s %s %d %d\n"
1090 " -> %s %s %d %d\n",
1091 OUStringToOString( it->m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1092 it->m_eItalic == italic::Upright ? "r" : it->m_eItalic == italic::Oblique ? "o" : it->m_eItalic == italic::Italic ? "i" : "u",
1093 it->m_eWeight,
1094 it->m_eWidth,
1096 OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr(),
1097 aInfo.m_eItalic == italic::Upright ? "r" : aInfo.m_eItalic == italic::Oblique ? "o" : aInfo.m_eItalic == italic::Italic ? "i" : "u",
1098 aInfo.m_eWeight,
1099 aInfo.m_eWidth
1101 #endif
1108 // -----------------------------------------------------------------
1110 void PrinterInfoManager::getSystemPrintCommands( std::list< OUString >& rCommands )
1112 if( m_pQueueInfo && m_pQueueInfo->hasChanged() )
1114 m_aSystemPrintCommand = m_pQueueInfo->getCommand();
1115 m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues );
1116 delete m_pQueueInfo, m_pQueueInfo = NULL;
1119 std::list< SystemPrintQueue >::const_iterator it;
1120 rCommands.clear();
1121 String aPrinterConst( RTL_CONSTASCII_USTRINGPARAM( "(PRINTER)" ) );
1122 for( it = m_aSystemPrintQueues.begin(); it != m_aSystemPrintQueues.end(); ++it )
1124 String aCmd( m_aSystemPrintCommand );
1125 aCmd.SearchAndReplace( aPrinterConst, it->m_aQueue );
1126 rCommands.push_back( aCmd );
1130 const std::list< PrinterInfoManager::SystemPrintQueue >& PrinterInfoManager::getSystemPrintQueues()
1132 if( m_pQueueInfo && m_pQueueInfo->hasChanged() )
1134 m_aSystemPrintCommand = m_pQueueInfo->getCommand();
1135 m_pQueueInfo->getSystemQueues( m_aSystemPrintQueues );
1136 delete m_pQueueInfo, m_pQueueInfo = NULL;
1139 return m_aSystemPrintQueues;
1142 bool PrinterInfoManager::checkFeatureToken( const rtl::OUString& rPrinterName, const char* pToken ) const
1144 const PrinterInfo& rPrinterInfo( getPrinterInfo( rPrinterName ) );
1145 sal_Int32 nIndex = 0;
1146 while( nIndex != -1 )
1148 OUString aOuterToken = rPrinterInfo.m_aFeatures.getToken( 0, ',', nIndex );
1149 sal_Int32 nInnerIndex = 0;
1150 OUString aInnerToken = aOuterToken.getToken( 0, '=', nInnerIndex );
1151 if( aInnerToken.equalsIgnoreAsciiCaseAscii( pToken ) )
1152 return true;
1154 return false;
1157 FILE* PrinterInfoManager::startSpool( const OUString& rPrintername, bool bQuickCommand )
1159 const PrinterInfo& rPrinterInfo = getPrinterInfo (rPrintername);
1160 const rtl::OUString& rCommand = (bQuickCommand && rPrinterInfo.m_aQuickCommand.getLength() ) ?
1161 rPrinterInfo.m_aQuickCommand : rPrinterInfo.m_aCommand;
1162 rtl::OString aShellCommand = OUStringToOString (rCommand, RTL_TEXTENCODING_ISO_8859_1);
1163 aShellCommand += rtl::OString( " 2>/dev/null" );
1165 return popen (aShellCommand.getStr(), "w");
1168 int PrinterInfoManager::endSpool( const OUString& /*rPrintername*/, const OUString& /*rJobTitle*/, FILE* pFile, const JobData& /*rDocumentJobData*/ )
1170 return (0 == pclose( pFile ));
1173 void PrinterInfoManager::setupJobContextData( JobData& rData )
1175 std::hash_map< OUString, Printer, OUStringHash >::iterator it =
1176 m_aPrinters.find( rData.m_aPrinterName );
1177 if( it != m_aPrinters.end() )
1179 rData.m_pParser = it->second.m_aInfo.m_pParser;
1180 rData.m_aContext = it->second.m_aInfo.m_aContext;
1184 void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const
1186 if( ! rContext.getParser() )
1187 return;
1189 const PPDKey* pPageSizeKey = rContext.getParser()->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
1190 if( ! pPageSizeKey )
1191 return;
1193 int nModified = rContext.countValuesModified();
1194 while( nModified-- &&
1195 rContext.getModifiedKey( nModified ) != pPageSizeKey )
1198 if( nModified >= 0 ) // paper was set already, do not modify
1200 #if OSL_DEBUG_LEVEL > 1
1201 fprintf( stderr, "not setting default paper, already set %s\n",
1202 OUStringToOString( rContext.getValue( pPageSizeKey )->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1203 #endif
1204 return;
1207 // paper not set, fill in default value
1208 const PPDValue* pPaperVal = NULL;
1209 int nValues = pPageSizeKey->countValues();
1210 for( int i = 0; i < nValues && ! pPaperVal; i++ )
1212 const PPDValue* pVal = pPageSizeKey->getValue( i );
1213 if( pVal->m_aOption.EqualsIgnoreCaseAscii( m_aSystemDefaultPaper.getStr() ) )
1214 pPaperVal = pVal;
1216 if( pPaperVal )
1218 #if OSL_DEBUG_LEVEL > 1
1219 fprintf( stderr, "setting default paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1220 #endif
1221 rContext.setValue( pPageSizeKey, pPaperVal );
1222 #if OSL_DEBUG_LEVEL > 1
1223 pPaperVal = rContext.getValue( pPageSizeKey );
1224 fprintf( stderr, "-> got paper %s\n", OUStringToOString( pPaperVal->m_aOption, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
1225 #endif
1229 // -----------------------------------------------------------------
1231 SystemQueueInfo::SystemQueueInfo() :
1232 m_bChanged( false )
1234 create();
1237 SystemQueueInfo::~SystemQueueInfo()
1239 terminate();
1242 bool SystemQueueInfo::hasChanged() const
1244 MutexGuard aGuard( m_aMutex );
1245 bool bChanged = m_bChanged;
1246 return bChanged;
1249 void SystemQueueInfo::getSystemQueues( std::list< PrinterInfoManager::SystemPrintQueue >& rQueues )
1251 MutexGuard aGuard( m_aMutex );
1252 rQueues = m_aQueues;
1253 m_bChanged = false;
1256 OUString SystemQueueInfo::getCommand() const
1258 MutexGuard aGuard( m_aMutex );
1259 OUString aRet = m_aCommand;
1260 return aRet;
1263 struct SystemCommandParameters;
1264 typedef void(* tokenHandler)(const std::list< rtl::OString >&,
1265 std::list< PrinterInfoManager::SystemPrintQueue >&,
1266 const SystemCommandParameters*);
1268 struct SystemCommandParameters
1270 const char* pQueueCommand;
1271 const char* pPrintCommand;
1272 const char* pForeToken;
1273 const char* pAftToken;
1274 unsigned int nForeTokenCount;
1275 tokenHandler pHandler;
1278 #if ! (defined(LINUX) || defined(NETBSD) || defined(FREEBSD) || defined(MACOSX))
1279 static void lpgetSysQueueTokenHandler(
1280 const std::list< rtl::OString >& i_rLines,
1281 std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
1282 const SystemCommandParameters* )
1284 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1285 std::hash_set< OUString, OUStringHash > aUniqueSet;
1286 std::hash_set< OUString, OUStringHash > aOnlySet;
1287 aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_all" ) ) );
1288 aUniqueSet.insert( OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
1290 // the eventual "all" attribute of the "_all" queue tells us, which
1291 // printers are to be used for this user at all
1293 // find _all: line
1294 rtl::OString aAllLine( "_all:" );
1295 rtl::OString aAllAttr( "all=" );
1296 for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1297 it != i_rLines.end(); ++it )
1299 if( it->indexOf( aAllLine, 0 ) == 0 )
1301 // now find the "all" attribute
1302 ++it;
1303 while( it != i_rLines.end() )
1305 rtl::OString aClean( WhitespaceToSpace( *it ) );
1306 if( aClean.indexOf( aAllAttr, 0 ) == 0 )
1308 // insert the comma separated entries into the set of printers to use
1309 sal_Int32 nPos = aAllAttr.getLength();
1310 while( nPos != -1 )
1312 OString aTok( aClean.getToken( 0, ',', nPos ) );
1313 if( aTok.getLength() > 0 )
1314 aOnlySet.insert( rtl::OStringToOUString( aTok, aEncoding ) );
1316 break;
1319 break;
1323 bool bInsertAttribute = false;
1324 rtl::OString aDescrStr( "description=" );
1325 rtl::OString aLocStr( "location=" );
1326 for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1327 it != i_rLines.end(); ++it )
1329 sal_Int32 nPos = 0;
1330 // find the begin of a new printer section
1331 nPos = it->indexOf( ':', 0 );
1332 if( nPos != -1 )
1334 OUString aSysQueue( rtl::OStringToOUString( it->copy( 0, nPos ), aEncoding ) );
1335 // do not insert duplicates (e.g. lpstat tends to produce such lines)
1336 // in case there was a "_all" section, insert only those printer explicitly
1337 // set in the "all" attribute
1338 if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() &&
1339 ( aOnlySet.empty() || aOnlySet.find( aSysQueue ) != aOnlySet.end() )
1342 o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() );
1343 o_rQueues.back().m_aQueue = aSysQueue;
1344 o_rQueues.back().m_aLocation = aSysQueue;
1345 aUniqueSet.insert( aSysQueue );
1346 bInsertAttribute = true;
1348 else
1349 bInsertAttribute = false;
1350 continue;
1352 if( bInsertAttribute && ! o_rQueues.empty() )
1354 // look for "description" attribute, insert as comment
1355 nPos = it->indexOf( aDescrStr, 0 );
1356 if( nPos != -1 )
1358 ByteString aComment( WhitespaceToSpace( it->copy(nPos+12) ) );
1359 if( aComment.Len() > 0 )
1360 o_rQueues.back().m_aComment = String( aComment, aEncoding );
1361 continue;
1363 // look for "location" attribute, inser as location
1364 nPos = it->indexOf( aLocStr, 0 );
1365 if( nPos != -1 )
1367 ByteString aLoc( WhitespaceToSpace( it->copy(nPos+9) ) );
1368 if( aLoc.Len() > 0 )
1369 o_rQueues.back().m_aLocation = String( aLoc, aEncoding );
1370 continue;
1375 #endif
1376 static void standardSysQueueTokenHandler(
1377 const std::list< rtl::OString >& i_rLines,
1378 std::list< PrinterInfoManager::SystemPrintQueue >& o_rQueues,
1379 const SystemCommandParameters* i_pParms)
1381 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
1382 std::hash_set< OUString, OUStringHash > aUniqueSet;
1383 rtl::OString aForeToken( i_pParms->pForeToken );
1384 rtl::OString aAftToken( i_pParms->pAftToken );
1385 /* Normal Unix print queue discovery, also used for Darwin 5 LPR printing
1387 for( std::list< rtl::OString >::const_iterator it = i_rLines.begin();
1388 it != i_rLines.end(); ++it )
1390 sal_Int32 nPos = 0;
1392 // search for a line describing a printer:
1393 // find if there are enough tokens before the name
1394 for( unsigned int i = 0; i < i_pParms->nForeTokenCount && nPos != -1; i++ )
1396 nPos = it->indexOf( aForeToken, nPos );
1397 if( nPos != -1 && it->getLength() >= nPos+aForeToken.getLength() )
1398 nPos += aForeToken.getLength();
1400 if( nPos != -1 )
1402 // find if there is the token after the queue
1403 sal_Int32 nAftPos = it->indexOf( aAftToken, nPos );
1404 if( nAftPos != -1 )
1406 // get the queue name between fore and aft tokens
1407 OUString aSysQueue( rtl::OStringToOUString( it->copy( nPos, nAftPos - nPos ), aEncoding ) );
1408 // do not insert duplicates (e.g. lpstat tends to produce such lines)
1409 if( aUniqueSet.find( aSysQueue ) == aUniqueSet.end() )
1411 o_rQueues.push_back( PrinterInfoManager::SystemPrintQueue() );
1412 o_rQueues.back().m_aQueue = aSysQueue;
1413 o_rQueues.back().m_aLocation = aSysQueue;
1414 aUniqueSet.insert( aSysQueue );
1421 static const struct SystemCommandParameters aParms[] =
1423 #if defined(LINUX) || defined(NETBSD) || defined(FREEBSD) || defined(MACOSX)
1424 { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1425 { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1426 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler }
1427 #else
1428 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpget list", "lp -d \"(PRINTER)\"", "", ":", 0, lpgetSysQueueTokenHandler },
1429 { "LANG=C;LC_ALL=C;export LANG LC_ALL;lpstat -s", "lp -d \"(PRINTER)\"", "system for ", ": ", 1, standardSysQueueTokenHandler },
1430 { "/usr/sbin/lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler },
1431 { "lpc status", "lpr -P \"(PRINTER)\"", "", ":", 0, standardSysQueueTokenHandler }
1432 #endif
1435 void SystemQueueInfo::run()
1437 char pBuffer[1024];
1438 FILE *pPipe;
1439 std::list< rtl::OString > aLines;
1441 /* Discover which command we can use to get a list of all printer queues */
1442 for( unsigned int i = 0; i < sizeof(aParms)/sizeof(aParms[0]); i++ )
1444 aLines.clear();
1445 rtl::OStringBuffer aCmdLine( 128 );
1446 aCmdLine.append( aParms[i].pQueueCommand );
1447 #if OSL_DEBUG_LEVEL > 1
1448 fprintf( stderr, "trying print queue command \"%s\" ... ", aParms[i].pQueueCommand );
1449 #endif
1450 aCmdLine.append( " 2>/dev/null" );
1451 if( (pPipe = popen( aCmdLine.getStr(), "r" )) )
1453 while( fgets( pBuffer, 1024, pPipe ) )
1454 aLines.push_back( rtl::OString( pBuffer ) );
1455 if( ! pclose( pPipe ) )
1457 std::list< PrinterInfoManager::SystemPrintQueue > aSysPrintQueues;
1458 aParms[i].pHandler( aLines, aSysPrintQueues, &(aParms[i]) );
1459 MutexGuard aGuard( m_aMutex );
1460 m_bChanged = true;
1461 m_aQueues = aSysPrintQueues;
1462 m_aCommand = rtl::OUString::createFromAscii( aParms[i].pPrintCommand );
1463 #if OSL_DEBUG_LEVEL > 1
1464 fprintf( stderr, "success\n" );
1465 #endif
1466 break;
1469 #if OSL_DEBUG_LEVEL > 1
1470 fprintf( stderr, "failed\n" );
1471 #endif