1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 
34 #include <hash_map>
35 
36 #include "vcl/ppdparser.hxx"
37 #include "vcl/strhelper.hxx"
38 #include "vcl/helper.hxx"
39 #include "vcl/svapp.hxx"
40 #include "cupsmgr.hxx"
41 #include "tools/debug.hxx"
42 #include "tools/urlobj.hxx"
43 #include "tools/stream.hxx"
44 #include "tools/zcodec.hxx"
45 #include "osl/mutex.hxx"
46 #include "osl/file.hxx"
47 #include "osl/process.h"
48 #include "osl/thread.h"
49 #include "rtl/strbuf.hxx"
50 #include "rtl/ustrbuf.hxx"
51 
52 #include "com/sun/star/lang/Locale.hpp"
53 
54 namespace psp
55 {
56     class PPDTranslator
57     {
58         struct LocaleEqual
59         {
60             bool operator()(const com::sun::star::lang::Locale& i_rLeft,
61                             const com::sun::star::lang::Locale& i_rRight) const
62             {
63                 return i_rLeft.Language.equals( i_rRight.Language ) &&
64                 i_rLeft.Country.equals( i_rRight.Country ) &&
65                 i_rLeft.Variant.equals( i_rRight.Variant );
66             }
67         };
68 
69         struct LocaleHash
70         {
71             size_t operator()(const com::sun::star::lang::Locale& rLocale) const
72             { return
73                   (size_t)rLocale.Language.hashCode()
74                 ^ (size_t)rLocale.Country.hashCode()
75                 ^ (size_t)rLocale.Variant.hashCode()
76                 ;
77             }
78         };
79 
80         typedef std::hash_map< com::sun::star::lang::Locale, rtl::OUString, LocaleHash, LocaleEqual > translation_map;
81         typedef std::hash_map< rtl::OUString, translation_map, rtl::OUStringHash > key_translation_map;
82 
83         key_translation_map     m_aTranslations;
84         public:
85         PPDTranslator() {}
86         ~PPDTranslator() {}
87 
88 
89         void insertValue(
90             const rtl::OUString& i_rKey,
91             const rtl::OUString& i_rOption,
92             const rtl::OUString& i_rValue,
93             const rtl::OUString& i_rTranslation,
94             const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale()
95             );
96 
97         void insertOption( const rtl::OUString& i_rKey,
98                            const rtl::OUString& i_rOption,
99                            const rtl::OUString& i_rTranslation,
100                            const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() )
101         {
102             insertValue( i_rKey, i_rOption, rtl::OUString(), i_rTranslation, i_rLocale );
103         }
104 
105         void insertKey( const rtl::OUString& i_rKey,
106                         const rtl::OUString& i_rTranslation,
107                         const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() )
108         {
109             insertValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rTranslation, i_rLocale );
110         }
111 
112         rtl::OUString translateValue(
113             const rtl::OUString& i_rKey,
114             const rtl::OUString& i_rOption,
115             const rtl::OUString& i_rValue,
116             const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale()
117             ) const;
118 
119         rtl::OUString translateOption( const rtl::OUString& i_rKey,
120                                        const rtl::OUString& i_rOption,
121                                        const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const
122         {
123             return translateValue( i_rKey, i_rOption, rtl::OUString(), i_rLocale );
124         }
125 
126         rtl::OUString translateKey( const rtl::OUString& i_rKey,
127                                     const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const
128         {
129             return translateValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rLocale );
130         }
131     };
132 
133     static com::sun::star::lang::Locale normalizeInputLocale(
134         const com::sun::star::lang::Locale& i_rLocale,
135         bool bInsertDefault = false
136         )
137     {
138         com::sun::star::lang::Locale aLoc( i_rLocale );
139         if( bInsertDefault && aLoc.Language.getLength() == 0 )
140         {
141             // empty locale requested, fill in application UI locale
142             aLoc = Application::GetSettings().GetUILocale();
143 
144             #if OSL_DEBUG_LEVEL > 1
145             static const char* pEnvLocale = getenv( "SAL_PPDPARSER_LOCALE" );
146             if( pEnvLocale && *pEnvLocale )
147             {
148                 rtl::OString aStr( pEnvLocale );
149                 sal_Int32 nLen = aStr.getLength();
150                 aLoc.Language = rtl::OStringToOUString( aStr.copy( 0, nLen > 2 ? 2 : nLen ), RTL_TEXTENCODING_MS_1252 );
151                 if( nLen >=5 && aStr.getStr()[2] == '_' )
152                     aLoc.Country = rtl::OStringToOUString( aStr.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252 );
153                 else
154                     aLoc.Country = rtl::OUString();
155                 aLoc.Variant = rtl::OUString();
156             }
157             #endif
158         }
159         aLoc.Language = aLoc.Language.toAsciiLowerCase();
160         aLoc.Country  = aLoc.Country.toAsciiUpperCase();
161         aLoc.Variant  = aLoc.Variant.toAsciiUpperCase();
162 
163         return aLoc;
164     }
165 
166     void PPDTranslator::insertValue(
167         const rtl::OUString& i_rKey,
168         const rtl::OUString& i_rOption,
169         const rtl::OUString& i_rValue,
170         const rtl::OUString& i_rTranslation,
171         const com::sun::star::lang::Locale& i_rLocale
172         )
173     {
174         rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 );
175         aKey.append( i_rKey );
176         if( i_rOption.getLength() || i_rValue.getLength() )
177         {
178             aKey.append( sal_Unicode( ':' ) );
179             aKey.append( i_rOption );
180         }
181         if( i_rValue.getLength() )
182         {
183             aKey.append( sal_Unicode( ':' ) );
184             aKey.append( i_rValue );
185         }
186         if( aKey.getLength() && i_rTranslation.getLength() )
187         {
188             rtl::OUString aK( aKey.makeStringAndClear() );
189             com::sun::star::lang::Locale aLoc;
190             aLoc.Language = i_rLocale.Language.toAsciiLowerCase();
191             aLoc.Country  = i_rLocale.Country.toAsciiUpperCase();
192             aLoc.Variant  = i_rLocale.Variant.toAsciiUpperCase();
193             m_aTranslations[ aK ][ aLoc ] = i_rTranslation;
194         }
195     }
196 
197     rtl::OUString PPDTranslator::translateValue(
198         const rtl::OUString& i_rKey,
199         const rtl::OUString& i_rOption,
200         const rtl::OUString& i_rValue,
201         const com::sun::star::lang::Locale& i_rLocale
202         ) const
203     {
204         rtl::OUString aResult;
205 
206         rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 );
207         aKey.append( i_rKey );
208         if( i_rOption.getLength() || i_rValue.getLength() )
209         {
210             aKey.append( sal_Unicode( ':' ) );
211             aKey.append( i_rOption );
212         }
213         if( i_rValue.getLength() )
214         {
215             aKey.append( sal_Unicode( ':' ) );
216             aKey.append( i_rValue );
217         }
218         if( aKey.getLength() )
219         {
220             rtl::OUString aK( aKey.makeStringAndClear() );
221             key_translation_map::const_iterator it = m_aTranslations.find( aK );
222             if( it != m_aTranslations.end() )
223             {
224                 const translation_map& rMap( it->second );
225 
226                 com::sun::star::lang::Locale aLoc( normalizeInputLocale( i_rLocale, true ) );
227                 for( int nTry = 0; nTry < 4; nTry++ )
228                 {
229                     translation_map::const_iterator tr = rMap.find( aLoc );
230                     if( tr != rMap.end() )
231                     {
232                         aResult = tr->second;
233                         break;
234                     }
235                     switch( nTry )
236                     {
237                     case 0: aLoc.Variant  = rtl::OUString();break;
238                     case 1: aLoc.Country  = rtl::OUString();break;
239                     case 2: aLoc.Language = rtl::OUString();break;
240                     }
241                 }
242             }
243         }
244         return aResult;
245     }
246 }
247 
248 using namespace psp;
249 using namespace rtl;
250 
251 #undef DBG_ASSERT
252 #if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1)
253 #define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() )
254 #define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); }
255 #else
256 #define DBG_ASSERT( x, y )
257 #endif
258 
259 std::list< PPDParser* > PPDParser::aAllParsers;
260 std::hash_map< OUString, OUString, OUStringHash >* PPDParser::pAllPPDFiles = NULL;
261 
262 class PPDDecompressStream
263 {
264     SvFileStream*       mpFileStream;
265     SvMemoryStream*     mpMemStream;
266     rtl::OUString       maFileName;
267 
268     // forbid copying
269     PPDDecompressStream( const PPDDecompressStream& );
270     PPDDecompressStream& operator=(const PPDDecompressStream& );
271 
272     public:
273     PPDDecompressStream( const rtl::OUString& rFile );
274     ~PPDDecompressStream();
275 
276     bool IsOpen() const;
277     bool IsEof() const;
278     void ReadLine( ByteString& o_rLine);
279     void Open( const rtl::OUString& i_rFile );
280     void Close();
281     const rtl::OUString& GetFileName() const { return maFileName; }
282 };
283 
284 PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) :
285     mpFileStream( NULL ),
286     mpMemStream( NULL )
287 {
288     Open( i_rFile );
289 }
290 
291 PPDDecompressStream::~PPDDecompressStream()
292 {
293     Close();
294 }
295 
296 void PPDDecompressStream::Open( const rtl::OUString& i_rFile )
297 {
298     Close();
299 
300     mpFileStream = new SvFileStream( i_rFile, STREAM_READ );
301     maFileName = mpFileStream->GetFileName();
302 
303     if( ! mpFileStream->IsOpen() )
304     {
305         Close();
306         return;
307     }
308 
309     ByteString aLine;
310     mpFileStream->ReadLine( aLine );
311     mpFileStream->Seek( 0 );
312 
313     // check for compress'ed or gzip'ed file
314     sal_uLong nCompressMethod = 0;
315     if( aLine.Len() > 1 && static_cast<unsigned char>(aLine.GetChar( 0 )) == 0x1f )
316     {
317         if( static_cast<unsigned char>(aLine.GetChar( 1 )) == 0x8b ) // check for gzip
318             nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB;
319     }
320 
321     if( nCompressMethod != 0 )
322     {
323         // so let's try to decompress the stream
324         mpMemStream = new SvMemoryStream( 4096, 4096 );
325         ZCodec aCodec;
326         aCodec.BeginCompression( nCompressMethod );
327         long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream );
328         aCodec.EndCompression();
329         if( nComp < 0 )
330         {
331             // decompression failed, must be an uncompressed stream after all
332             delete mpMemStream, mpMemStream = NULL;
333             mpFileStream->Seek( 0 );
334         }
335         else
336         {
337             // compression successfull, can get rid of file stream
338             delete mpFileStream, mpFileStream = NULL;
339             mpMemStream->Seek( 0 );
340         }
341     }
342 }
343 
344 void PPDDecompressStream::Close()
345 {
346     delete mpMemStream, mpMemStream = NULL;
347     delete mpFileStream, mpFileStream = NULL;
348 }
349 
350 bool PPDDecompressStream::IsOpen() const
351 {
352     return (mpMemStream || (mpFileStream && mpFileStream->IsOpen()));
353 }
354 
355 bool PPDDecompressStream::IsEof() const
356 {
357     return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) );
358 }
359 
360 void PPDDecompressStream::ReadLine( ByteString& o_rLine )
361 {
362     if( mpMemStream )
363         mpMemStream->ReadLine( o_rLine );
364     else if( mpFileStream )
365         mpFileStream->ReadLine( o_rLine );
366 }
367 
368 static osl::FileBase::RC resolveLink( const rtl::OUString& i_rURL, rtl::OUString& o_rResolvedURL, rtl::OUString& o_rBaseName, osl::FileStatus::Type& o_rType, int nLinkLevel = 10 )
369 {
370     osl::DirectoryItem aLinkItem;
371     osl::FileBase::RC aRet = osl::FileBase::E_None;
372 
373     if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None )
374     {
375         osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL );
376         if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None )
377         {
378             if( aStatus.getFileType() == osl::FileStatus::Link )
379             {
380                 if( nLinkLevel > 0 )
381                     aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 );
382                 else
383                     aRet = osl::FileBase::E_MULTIHOP;
384             }
385             else
386             {
387                 o_rResolvedURL = i_rURL;
388                 o_rBaseName = aStatus.getFileName();
389                 o_rType = aStatus.getFileType();
390             }
391         }
392     }
393     return aRet;
394 }
395 
396 void PPDParser::scanPPDDir( const String& rDir )
397 {
398     static struct suffix_t
399     {
400         const sal_Char* pSuffix;
401         const sal_Int32 nSuffixLen;
402     } const pSuffixes[] =
403     { { ".PS", 3 },  { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } };
404 
405     const int nSuffixes = sizeof(pSuffixes)/sizeof(pSuffixes[0]);
406 
407     osl::Directory aDir( rDir );
408     if ( aDir.open() == osl::FileBase::E_None )
409     {
410         osl::DirectoryItem aItem;
411 
412         INetURLObject aPPDDir(rDir);
413         while( aDir.getNextItem( aItem ) == osl::FileBase::E_None )
414         {
415             osl::FileStatus aStatus( FileStatusMask_FileName );
416             if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
417             {
418                 rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 );
419                 aURLBuf.append( rDir );
420                 aURLBuf.append( sal_Unicode( '/' ) );
421                 aURLBuf.append( aStatus.getFileName() );
422 
423                 rtl::OUString aFileURL, aFileName;
424                 osl::FileStatus::Type eType = osl::FileStatus::Unknown;
425 
426                 if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None )
427                 {
428                     if( eType == osl::FileStatus::Regular )
429                     {
430                         INetURLObject aPPDFile = aPPDDir;
431                         aPPDFile.Append( aFileName );
432 
433                         // match extension
434                         for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ )
435                         {
436                             if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen )
437                             {
438                                 if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) )
439                                 {
440                                     (*pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName();
441                                     break;
442                                 }
443                             }
444                         }
445                     }
446                     else if( eType == osl::FileStatus::Directory )
447                     {
448                         scanPPDDir( aFileURL );
449                     }
450                 }
451             }
452         }
453         aDir.close();
454     }
455 }
456 
457 void PPDParser::initPPDFiles()
458 {
459     if( pAllPPDFiles )
460         return;
461 
462     pAllPPDFiles = new std::hash_map< OUString, OUString, OUStringHash >();
463 
464     // check installation directories
465     std::list< OUString > aPathList;
466     psp::getPrinterPathList( aPathList, PRINTER_PPDDIR );
467     for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it )
468     {
469         INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
470         scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) );
471     }
472     if( pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() )
473     {
474         // last try: search in directory of executable (mainly for setup)
475         OUString aExe;
476         if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
477         {
478             INetURLObject aDir( aExe );
479             aDir.removeSegment();
480 #ifdef DEBUG
481             fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() );
482 #endif
483             scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) );
484 #ifdef DEBUG
485             fprintf( stderr, "SGENPRT %s\n", pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ? "not found" : "found" );
486 #endif
487         }
488     }
489 }
490 
491 void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers, bool bRefresh )
492 {
493     if( bRefresh )
494     {
495         delete pAllPPDFiles;
496         pAllPPDFiles = NULL;
497     }
498 
499     initPPDFiles();
500     o_rDrivers.clear();
501 
502     std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
503     for( it = pAllPPDFiles->begin(); it != pAllPPDFiles->end(); ++it )
504         o_rDrivers.push_back( it->first );
505 }
506 
507 String PPDParser::getPPDFile( const String& rFile )
508 {
509     INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
510     // someone might enter a full qualified name here
511     PPDDecompressStream aStream( aPPD.PathToFileName() );
512     if( ! aStream.IsOpen() )
513     {
514         std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
515 
516         bool bRetry = true;
517         do
518         {
519             initPPDFiles();
520             // some PPD files contain dots beside the extension, so try name first
521             // and cut of points after that
522             rtl::OUString aBase( rFile );
523             sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) );
524             if( nLastIndex >= 0 )
525                 aBase = aBase.copy( nLastIndex+1 );
526             do
527             {
528                 it = pAllPPDFiles->find( aBase );
529                 nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) );
530                 if( nLastIndex > 0 )
531                     aBase = aBase.copy( 0, nLastIndex );
532             } while( it == pAllPPDFiles->end() && nLastIndex > 0 );
533 
534             if( it == pAllPPDFiles->end() && bRetry )
535             {
536                 // a new file ? rehash
537                 delete pAllPPDFiles; pAllPPDFiles = NULL;
538                 bRetry = false;
539                 // note this is optimized for office start where
540                 // no new files occur and initPPDFiles is called only once
541             }
542         } while( ! pAllPPDFiles );
543 
544         if( it != pAllPPDFiles->end() )
545             aStream.Open( it->second );
546     }
547 
548     String aRet;
549     if( aStream.IsOpen() )
550     {
551         ByteString aLine;
552         aStream.ReadLine( aLine );
553         if( aLine.Search( "*PPD-Adobe" ) == 0 )
554             aRet = aStream.GetFileName();
555         else
556         {
557             // our *Include hack does usually not begin
558             // with *PPD-Adobe, so try some lines for *Include
559             int nLines = 10;
560             while( aLine.Search( "*Include" ) != 0 && --nLines )
561                 aStream.ReadLine( aLine );
562             if( nLines )
563                 aRet = aStream.GetFileName();
564         }
565     }
566 
567     return aRet;
568 }
569 
570 String PPDParser::getPPDPrinterName( const String& rFile )
571 {
572     String aPath = getPPDFile( rFile );
573     String aName;
574 
575     // read in the file
576     PPDDecompressStream aStream( aPath );
577     if( aStream.IsOpen() )
578     {
579         String aCurLine;
580         while( ! aStream.IsEof() && aStream.IsOpen() )
581         {
582             ByteString aByteLine;
583             aStream.ReadLine( aByteLine );
584             aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 );
585             if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
586             {
587                 aCurLine.Erase( 0, 9 );
588                 aCurLine.EraseLeadingChars( ' ' );
589                 aCurLine.EraseTrailingChars( ' ' );
590                 aCurLine.EraseLeadingChars( '\t' );
591                 aCurLine.EraseTrailingChars( '\t' );
592                 aCurLine.EraseTrailingChars( '\r' );
593                 aCurLine.EraseTrailingChars( '\n' );
594                 aCurLine.EraseLeadingChars( '"' );
595                 aCurLine.EraseTrailingChars( '"' );
596                 aStream.Close();
597                 aStream.Open( getPPDFile( aCurLine ) );
598                 continue;
599             }
600             if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL )
601             {
602                 aName = aCurLine.GetToken( 1, '"' );
603                 break;
604             }
605             else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL )
606                 aName = aCurLine.GetToken( 1, '"' );
607         }
608     }
609     return aName;
610 }
611 
612 const PPDParser* PPDParser::getParser( const String& rFile )
613 {
614     static ::osl::Mutex aMutex;
615     ::osl::Guard< ::osl::Mutex > aGuard( aMutex );
616 
617     String aFile = rFile;
618     if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
619         aFile = getPPDFile( rFile );
620     if( ! aFile.Len() )
621     {
622 #if OSL_DEBUG_LEVEL > 1
623         fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() );
624 #endif
625         return NULL;
626     }
627 
628     for( ::std::list< PPDParser* >::const_iterator it = aAllParsers.begin(); it != aAllParsers.end(); ++it )
629         if( (*it)->m_aFile == aFile )
630             return *it;
631 
632     PPDParser* pNewParser = NULL;
633     if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
634         pNewParser = new PPDParser( aFile );
635     else
636     {
637         PrinterInfoManager& rMgr = PrinterInfoManager::get();
638         if( rMgr.getType() == PrinterInfoManager::CUPS )
639         {
640             pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
641         }
642     }
643     if( pNewParser )
644     {
645         // this may actually be the SGENPRT parser,
646         // so ensure uniquness here
647         aAllParsers.remove( pNewParser );
648         // insert new parser to list
649         aAllParsers.push_front( pNewParser );
650     }
651     return pNewParser;
652 }
653 
654 void PPDParser::freeAll()
655 {
656     while( aAllParsers.begin() != aAllParsers.end() )
657     {
658         delete aAllParsers.front();
659         aAllParsers.pop_front();
660     }
661     delete pAllPPDFiles;
662     pAllPPDFiles = NULL;
663 }
664 
665 PPDParser::PPDParser( const String& rFile ) :
666         m_aFile( rFile ),
667         m_bType42Capable( false ),
668         m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ),
669         m_pDefaultImageableArea( NULL ),
670         m_pImageableAreas( NULL ),
671         m_pDefaultPaperDimension( NULL ),
672         m_pPaperDimensions( NULL ),
673         m_pDefaultInputSlot( NULL ),
674         m_pInputSlots( NULL ),
675         m_pDefaultResolution( NULL ),
676         m_pResolutions( NULL ),
677         m_pDefaultDuplexType( NULL ),
678         m_pDuplexTypes( NULL ),
679         m_pFontList( NULL ),
680         m_pTranslator( new PPDTranslator() )
681 {
682     // read in the file
683     std::list< ByteString > aLines;
684     PPDDecompressStream aStream( m_aFile );
685     bool bLanguageEncoding = false;
686     if( aStream.IsOpen() )
687     {
688         ByteString aCurLine;
689         while( ! aStream.IsEof() )
690         {
691             aStream.ReadLine( aCurLine );
692             if( aCurLine.GetChar( 0 ) == '*' )
693             {
694                 if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
695                 {
696                     aCurLine.Erase( 0, 9 );
697                     aCurLine.EraseLeadingChars( ' ' );
698                     aCurLine.EraseTrailingChars( ' ' );
699                     aCurLine.EraseLeadingChars( '\t' );
700                     aCurLine.EraseTrailingChars( '\t' );
701                     aCurLine.EraseTrailingChars( '\r' );
702                     aCurLine.EraseTrailingChars( '\n' );
703                     aCurLine.EraseLeadingChars( '"' );
704                     aCurLine.EraseTrailingChars( '"' );
705                     aStream.Close();
706                     aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) );
707                     continue;
708                 }
709                 else if( ! bLanguageEncoding &&
710                          aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL )
711                 {
712                     bLanguageEncoding = true; // generally only the first one counts
713                     ByteString aLower = aCurLine;
714                     aLower.ToLowerAscii();
715                     if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND ||
716                         aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND )
717                         m_aFileEncoding = RTL_TEXTENCODING_MS_1252;
718                     else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND )
719                         m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2;
720                     else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND )
721                         m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5;
722                     else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND )
723                         m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS;
724                     else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND )
725                         m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
726                     else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND )
727                         m_aFileEncoding = RTL_TEXTENCODING_UTF8;
728                 }
729             }
730             aLines.push_back( aCurLine );
731         }
732     }
733     aStream.Close();
734 
735     // now get the Values
736     parse( aLines );
737 #if OSL_DEBUG_LEVEL > 2
738     fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() );
739     for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
740     {
741         const PPDKey* pKey = it->second;
742         char* pSetupType = "<unknown>";
743         switch( pKey->m_eSetupType )
744         {
745             case PPDKey::ExitServer:        pSetupType = "ExitServer";break;
746             case PPDKey::Prolog:            pSetupType = "Prolog";break;
747             case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break;
748             case PPDKey::PageSetup:     pSetupType = "PageSetup";break;
749             case PPDKey::JCLSetup:          pSetupType = "JCLSetup";break;
750             case PPDKey::AnySetup:          pSetupType = "AnySetup";break;
751             default: break;
752         };
753         fprintf( stderr, "\t\"%s\" (\"%s\") (%d values) OrderDependency: %d %s\n",
754                  BSTRING( pKey->getKey() ).GetBuffer(),
755                  BSTRING( pKey->m_aUITranslation ).GetBuffer(),
756                  pKey->countValues(),
757                  pKey->m_nOrderDependency,
758                  pSetupType );
759         for( int j = 0; j < pKey->countValues(); j++ )
760         {
761             fprintf( stderr, "\t\t" );
762             const PPDValue* pValue = pKey->getValue( j );
763             if( pValue == pKey->m_pDefaultValue )
764                 fprintf( stderr, "(Default:) " );
765             char* pVType = "<unknown>";
766             switch( pValue->m_eType )
767             {
768                 case eInvocation:       pVType = "invocation";break;
769                 case eQuoted:           pVType = "quoted";break;
770                 case eString:           pVType = "string";break;
771                 case eSymbol:           pVType = "symbol";break;
772                 case eNo:               pVType = "no";break;
773                 default: break;
774             };
775             fprintf( stderr, "option: \"%s\" (\"%s\"), value: type %s \"%s\" (\"%s\")\n",
776                      BSTRING( pValue->m_aOption ).GetBuffer(),
777                      BSTRING( pValue->m_aOptionTranslation ).GetBuffer(),
778                      pVType,
779                      BSTRING( pValue->m_aValue ).GetBuffer(),
780                      BSTRING( pValue->m_aValueTranslation ).GetBuffer() );
781         }
782     }
783     fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() );
784     for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit )
785     {
786         fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n",
787                  BSTRING( cit->m_pKey1->getKey() ).GetBuffer(),
788                  cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "<nil>",
789                  BSTRING( cit->m_pKey2->getKey() ).GetBuffer(),
790                  cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "<nil>"
791                  );
792     }
793 #endif
794 
795     // fill in shortcuts
796     const PPDKey* pKey;
797 
798     m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) );
799     if( m_pImageableAreas )
800         m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
801     DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" );
802     DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" );
803 
804     m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) );
805     if( m_pPaperDimensions )
806         m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
807     DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" );
808     DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" );
809 
810     m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
811     if( m_pResolutions )
812         m_pDefaultResolution = m_pResolutions->getDefaultValue();
813     DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" );
814     DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" );
815 
816     m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
817     if( m_pInputSlots )
818         m_pDefaultInputSlot = m_pInputSlots->getDefaultValue();
819     DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" );
820     DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" );
821 
822     m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
823     if( m_pDuplexTypes )
824         m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue();
825 
826     m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) );
827     DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" );
828 
829     // fill in direct values
830     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) )
831         m_aPrinterName = pKey->getValue( 0 )->m_aValue;
832     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) )
833         m_aNickName = pKey->getValue( 0 )->m_aValue;
834     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) )
835         m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false;
836 
837     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) )
838         m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32();
839     if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) )
840         m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false;
841 }
842 
843 PPDParser::~PPDParser()
844 {
845     for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
846         delete it->second;
847     delete m_pTranslator;
848 }
849 
850 void PPDParser::insertKey( const String& rKey, PPDKey* pKey )
851 {
852     m_aKeys[ rKey ] = pKey;
853     m_aOrderedKeys.push_back( pKey );
854 }
855 
856 const PPDKey* PPDParser::getKey( int n ) const
857 {
858     return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL;
859 }
860 
861 const PPDKey* PPDParser::getKey( const String& rKey ) const
862 {
863     PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey );
864     return it != m_aKeys.end() ? it->second : NULL;
865 }
866 
867 bool PPDParser::hasKey( const PPDKey* pKey ) const
868 {
869     return
870         pKey ?
871         ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) :
872         false;
873 }
874 
875 static sal_uInt8 getNibble( sal_Char cChar )
876 {
877     sal_uInt8 nRet = 0;
878     if( cChar >= '0' && cChar <= '9' )
879         nRet = sal_uInt8( cChar - '0' );
880     else if( cChar >= 'A' && cChar <= 'F' )
881         nRet = 10 + sal_uInt8( cChar - 'A' );
882     else if( cChar >= 'a' && cChar <= 'f' )
883         nRet = 10 + sal_uInt8( cChar - 'a' );
884     return nRet;
885 }
886 
887 String PPDParser::handleTranslation( const ByteString& i_rString, bool bIsGlobalized )
888 {
889     int nOrigLen = i_rString.Len();
890     OStringBuffer aTrans( nOrigLen );
891     const sal_Char* pStr = i_rString.GetBuffer();
892     const sal_Char* pEnd = pStr + nOrigLen;
893     while( pStr < pEnd )
894     {
895         if( *pStr == '<' )
896         {
897             pStr++;
898             sal_Char cChar;
899             while( *pStr != '>' && pStr < pEnd-1 )
900             {
901                 cChar = getNibble( *pStr++ ) << 4;
902                 cChar |= getNibble( *pStr++ );
903                 aTrans.append( cChar );
904             }
905             pStr++;
906         }
907         else
908             aTrans.append( *pStr++ );
909     }
910     return OStringToOUString( aTrans.makeStringAndClear(), bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding );
911 }
912 
913 void PPDParser::parse( ::std::list< ByteString >& rLines )
914 {
915     std::list< ByteString >::iterator line = rLines.begin();
916     PPDParser::hash_type::const_iterator keyit;
917     while( line != rLines.end() )
918     {
919         ByteString aCurrentLine( *line );
920         ++line;
921         if( aCurrentLine.GetChar(0) != '*' )
922             continue;
923         if( aCurrentLine.GetChar(1) == '%' )
924             continue;
925 
926         ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) );
927         int nPos = aKey.Search( '/' );
928         if( nPos != STRING_NOTFOUND )
929             aKey.Erase( nPos );
930         aKey.Erase( 0, 1 ); // remove the '*'
931 
932         if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) )
933             continue;
934 
935         if( aKey.Equals( "OpenUI" ) )
936         {
937             parseOpenUI( aCurrentLine );
938             continue;
939         }
940         else if( aKey.Equals( "OrderDependency" ) )
941         {
942             parseOrderDependency( aCurrentLine );
943             continue;
944         }
945         else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) )
946             continue; // parsed in pass 2
947         else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled
948             continue;
949 
950         // default values are parsed in pass 2
951         if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL )
952             continue;
953 
954         bool bQuery     = false;
955         if( aKey.GetChar( 0 ) == '?' )
956         {
957             aKey.Erase( 0, 1 );
958             bQuery = true;
959         }
960 
961         String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
962         // handle CUPS extension for globalized PPDs
963         bool bIsGlobalizedLine = false;
964         com::sun::star::lang::Locale aTransLocale;
965         if( ( aUniKey.Len() > 3 && aUniKey.GetChar( 2 ) == '.' ) ||
966             ( aUniKey.Len() > 5 && aUniKey.GetChar( 2 ) == '_' && aUniKey.GetChar( 5 ) == '.' ) )
967         {
968             if( aUniKey.GetChar( 2 ) == '.' )
969             {
970                 aTransLocale.Language = aUniKey.Copy( 0, 2 );
971                 aUniKey = aUniKey.Copy( 3 );
972             }
973             else
974             {
975                 aTransLocale.Language = aUniKey.Copy( 0, 2 );
976                 aTransLocale.Country = aUniKey.Copy( 3, 2 );
977                 aUniKey = aUniKey.Copy( 6 );
978             }
979             bIsGlobalizedLine = true;
980         }
981 
982         String aOption;
983         nPos = aCurrentLine.Search( ':' );
984         if( nPos != STRING_NOTFOUND )
985         {
986             aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 );
987             aOption = GetCommandLineToken( 1, aOption );
988             int nTransPos = aOption.Search( '/' );
989             if( nTransPos != STRING_NOTFOUND )
990                 aOption.Erase( nTransPos );
991         }
992 
993         PPDValueType eType = eNo;
994         String aValue;
995         rtl::OUString aOptionTranslation;
996         rtl::OUString aValueTranslation;
997         if( nPos != STRING_NOTFOUND )
998         {
999             // found a colon, there may be an option
1000             ByteString aLine = aCurrentLine.Copy( 1, nPos-1 );
1001             aLine = WhitespaceToSpace( aLine );
1002             int nTransPos = aLine.Search( '/' );
1003             if( nTransPos != STRING_NOTFOUND )
1004                 aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine );
1005 
1006             // read in more lines if necessary for multiline values
1007             aLine = aCurrentLine.Copy( nPos+1 );
1008             if( aLine.Len() )
1009             {
1010                 while( ! ( aLine.GetTokenCount( '"' ) & 1 ) &&
1011                        line != rLines.end() )
1012                     // while there is an even number of tokens; that means
1013                     // an odd number of doubleqoutes
1014                 {
1015                     // copy the newlines also
1016                     aLine += '\n';
1017                     aLine += *line;
1018                     ++line;
1019                 }
1020             }
1021             aLine = WhitespaceToSpace( aLine );
1022 
1023             // #i100644# handle a missing value (actually a broken PPD)
1024             if( ! aLine.Len() )
1025             {
1026                 if( aOption.Len() &&
1027                     aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL )
1028                     eType = eInvocation;
1029                 else
1030                     eType = eQuoted;
1031             }
1032             // check for invocation or quoted value
1033             else if( aLine.GetChar(0) == '"' )
1034             {
1035                 aLine.Erase( 0, 1 );
1036                 nTransPos = aLine.Search( '"' );
1037                 aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
1038                 // after the second doublequote can follow a / and a translation
1039                 aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ), bIsGlobalizedLine );
1040                 // check for quoted value
1041                 if( aOption.Len() &&
1042                     aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL )
1043                     eType = eInvocation;
1044                 else
1045                     eType = eQuoted;
1046             }
1047             // check for symbol value
1048             else if( aLine.GetChar(0) == '^' )
1049             {
1050                 aLine.Erase( 0, 1 );
1051                 aValue = String( aLine, RTL_TEXTENCODING_MS_1252 );
1052                 eType = eSymbol;
1053             }
1054             else
1055             {
1056                 // must be a string value then
1057                 // strictly this is false because string values
1058                 // can contain any whitespace which is reduced
1059                 // to one space by now
1060                 // who cares ...
1061                 nTransPos = aLine.Search( '/' );
1062                 if( nTransPos == STRING_NOTFOUND )
1063                     nTransPos = aLine.Len();
1064                 aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
1065                 aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine );
1066                 eType = eString;
1067             }
1068         }
1069 
1070         // handle globalized PPD entries
1071         if( bIsGlobalizedLine )
1072         {
1073             // handle main key translations of form:
1074             // *ll_CC.Translation MainKeyword/translated text: ""
1075             if( aUniKey.EqualsAscii( "Translation" ) )
1076             {
1077                 m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale );
1078             }
1079             // handle options translations of for:
1080             // *ll_CC.MainKeyword OptionKeyword/translated text: ""
1081             else
1082             {
1083                 m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1084             }
1085             continue;
1086         }
1087 
1088         PPDKey* pKey = NULL;
1089         keyit = m_aKeys.find( aUniKey );
1090         if( keyit == m_aKeys.end() )
1091         {
1092             pKey = new PPDKey( aUniKey );
1093             insertKey( aUniKey, pKey );
1094         }
1095         else
1096             pKey = keyit->second;
1097 
1098         if( eType == eNo && bQuery )
1099             continue;
1100 
1101         PPDValue* pValue = pKey->insertValue( aOption );
1102         if( ! pValue )
1103             continue;
1104         pValue->m_eType = eType;
1105         pValue->m_aValue = aValue;
1106 
1107         if( aOptionTranslation.getLength() )
1108             m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1109         if( aValueTranslation.getLength() )
1110             m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale );
1111 
1112         // eventually update query and remove from option list
1113         if( bQuery && pKey->m_bQueryValue == sal_False )
1114         {
1115             pKey->m_aQueryValue = *pValue;
1116             pKey->m_bQueryValue = true;
1117             pKey->eraseValue( pValue->m_aOption );
1118         }
1119     }
1120 
1121     // second pass: fill in defaults
1122     for( line = rLines.begin(); line != rLines.end(); ++line )
1123     {
1124         ByteString aLine( *line );
1125         if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL )
1126         {
1127             String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 );
1128             sal_uInt16 nPos = aKey.Search( ':' );
1129             if( nPos != STRING_NOTFOUND )
1130             {
1131                 aKey.Erase( nPos );
1132                 String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 );
1133                 keyit = m_aKeys.find( aKey );
1134                 if( keyit != m_aKeys.end() )
1135                 {
1136                     PPDKey* pKey = keyit->second;
1137                     const PPDValue* pDefValue = pKey->getValue( aOption );
1138                     if( pKey->m_pDefaultValue == NULL )
1139                         pKey->m_pDefaultValue = pDefValue;
1140                 }
1141                 else
1142                 {
1143                     // some PPDs contain defaults for keys that
1144                     // do not exist otherwise
1145                     // (example: DefaultResolution)
1146                     // so invent that key here and have a default value
1147                     PPDKey* pKey = new PPDKey( aKey );
1148                     PPDValue* pNewValue = pKey->insertValue( aOption );
1149                     pNewValue->m_eType = eInvocation; // or what ?
1150                     insertKey( aKey, pKey );
1151                 }
1152             }
1153         }
1154         else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL  ||
1155                  aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL )
1156             parseConstraint( aLine );
1157 
1158     }
1159 }
1160 
1161 void PPDParser::parseOpenUI( const ByteString& rLine )
1162 {
1163     String aTranslation;
1164     ByteString aKey = rLine;
1165 
1166     int nPos = aKey.Search( ':' );
1167     if( nPos != STRING_NOTFOUND )
1168         aKey.Erase( nPos );
1169     nPos = aKey.Search( '/' );
1170     if( nPos != STRING_NOTFOUND )
1171     {
1172         aTranslation = handleTranslation( aKey.Copy( nPos + 1 ), false );
1173         aKey.Erase( nPos );
1174     }
1175     aKey = GetCommandLineToken( 1, aKey );
1176     aKey.Erase( 0, 1 );
1177 
1178     String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
1179     PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey );
1180     PPDKey* pKey;
1181     if( keyit == m_aKeys.end() )
1182     {
1183         pKey = new PPDKey( aUniKey );
1184         insertKey( aUniKey, pKey );
1185     }
1186     else
1187         pKey = keyit->second;
1188 
1189     pKey->m_bUIOption = true;
1190     m_pTranslator->insertKey( pKey->getKey(), aTranslation );
1191 
1192     ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) );
1193     if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL )
1194         pKey->m_eUIType = PPDKey::Boolean;
1195     else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL )
1196         pKey->m_eUIType = PPDKey::PickMany;
1197     else
1198         pKey->m_eUIType = PPDKey::PickOne;
1199 }
1200 
1201 void PPDParser::parseOrderDependency( const ByteString& rLine )
1202 {
1203     ByteString aLine( rLine );
1204     int nPos = aLine.Search( ':' );
1205     if( nPos != STRING_NOTFOUND )
1206         aLine.Erase( 0, nPos+1 );
1207 
1208     int nOrder = GetCommandLineToken( 0, aLine ).ToInt32();
1209     ByteString aSetup = GetCommandLineToken( 1, aLine );
1210     String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 );
1211     if( aKey.GetChar( 0 ) != '*' )
1212         return; // invalid order depency
1213     aKey.Erase( 0, 1 );
1214 
1215     PPDKey* pKey;
1216     PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey );
1217     if( keyit == m_aKeys.end() )
1218     {
1219         pKey = new PPDKey( aKey );
1220         insertKey( aKey, pKey );
1221     }
1222     else
1223         pKey = keyit->second;
1224 
1225     pKey->m_nOrderDependency = nOrder;
1226     if( aSetup.Equals( "ExitServer" ) )
1227         pKey->m_eSetupType = PPDKey::ExitServer;
1228     else if( aSetup.Equals( "Prolog" ) )
1229         pKey->m_eSetupType = PPDKey::Prolog;
1230     else if( aSetup.Equals( "DocumentSetup" ) )
1231         pKey->m_eSetupType = PPDKey::DocumentSetup;
1232     else if( aSetup.Equals( "PageSetup" ) )
1233         pKey->m_eSetupType = PPDKey::PageSetup;
1234     else if( aSetup.Equals( "JCLSetup" ) )
1235         pKey->m_eSetupType = PPDKey::JCLSetup;
1236     else
1237         pKey->m_eSetupType = PPDKey::AnySetup;
1238 }
1239 
1240 void PPDParser::parseConstraint( const ByteString& rLine )
1241 {
1242     bool bFailed = false;
1243 
1244     String aLine( rLine, RTL_TEXTENCODING_MS_1252 );
1245     aLine.Erase( 0, rLine.Search( ':' )+1 );
1246     PPDConstraint aConstraint;
1247     int nTokens = GetCommandLineTokenCount( aLine );
1248     for( int i = 0; i < nTokens; i++ )
1249     {
1250         String aToken = GetCommandLineToken( i, aLine );
1251         if( aToken.GetChar( 0 ) == '*' )
1252         {
1253             aToken.Erase( 0, 1 );
1254             if( aConstraint.m_pKey1 )
1255                 aConstraint.m_pKey2 = getKey( aToken );
1256             else
1257                 aConstraint.m_pKey1 = getKey( aToken );
1258         }
1259         else
1260         {
1261             if( aConstraint.m_pKey2 )
1262             {
1263                 if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1264                     bFailed = true;
1265             }
1266             else if( aConstraint.m_pKey1 )
1267             {
1268                 if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1269                     bFailed = true;
1270             }
1271             else
1272                 // constraint for nonexistent keys; this happens
1273                 // e.g. in HP4PLUS3 (#75636#)
1274                 bFailed = true;
1275         }
1276     }
1277     // there must be two keywords
1278     if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed )
1279     {
1280 #ifdef __DEBUG
1281         fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() );
1282 #endif
1283     }
1284     else
1285         m_aConstraints.push_back( aConstraint );
1286 }
1287 
1288 String PPDParser::getDefaultPaperDimension() const
1289 {
1290     if( m_pDefaultPaperDimension )
1291         return m_pDefaultPaperDimension->m_aOption;
1292 
1293     return String();
1294 }
1295 
1296 bool PPDParser::getMargins(
1297                            const String& rPaperName,
1298                            int& rLeft, int& rRight,
1299                            int& rUpper, int& rLower ) const
1300 {
1301     if( ! m_pImageableAreas || ! m_pPaperDimensions )
1302         return false;
1303 
1304     int nPDim=-1, nImArea=-1, i;
1305     for( i = 0; i < m_pImageableAreas->countValues(); i++ )
1306         if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption )
1307             nImArea = i;
1308     for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1309         if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1310             nPDim = i;
1311     if( nPDim == -1 || nImArea == -1 )
1312         return false;
1313 
1314     double ImLLx, ImLLy, ImURx, ImURy;
1315     double PDWidth, PDHeight;
1316     String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue;
1317     ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) );
1318     ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) );
1319     ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) );
1320     ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) );
1321 //  sscanf( m_pImageableAreas->getValue( nImArea )->m_aValue.GetStr(),
1322 //          "%lg%lg%lg%lg", &ImLLx, &ImLLy, &ImURx, &ImURy );
1323     aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1324     PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1325     PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1326 //  sscanf( m_pPaperDimensions->getValue( nPDim )->m_aValue.GetStr(),
1327 //          "%lg%lg", &PDWidth, &PDHeight );
1328     rLeft  = (int)(ImLLx + 0.5);
1329     rLower = (int)(ImLLy + 0.5);
1330     rUpper = (int)(PDHeight - ImURy + 0.5);
1331     rRight = (int)(PDWidth - ImURx + 0.5);
1332 
1333     return true;
1334 }
1335 
1336 bool PPDParser::getPaperDimension(
1337                                   const String& rPaperName,
1338                                   int& rWidth, int& rHeight ) const
1339 {
1340     if( ! m_pPaperDimensions )
1341         return false;
1342 
1343     int nPDim=-1;
1344     for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1345         if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1346             nPDim = i;
1347     if( nPDim == -1 )
1348         return false;
1349 
1350     double PDWidth, PDHeight;
1351     String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1352     PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1353     PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1354     rHeight = (int)(PDHeight + 0.5);
1355     rWidth  = (int)(PDWidth + 0.5);
1356 
1357     return true;
1358 }
1359 
1360 String PPDParser::matchPaper( int nWidth, int nHeight ) const
1361 {
1362     if( ! m_pPaperDimensions )
1363         return String();
1364 
1365     int nPDim = -1;
1366     double PDWidth, PDHeight;
1367     double fSort = 2e36, fNewSort;
1368 
1369     for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1370     {
1371         String aArea =  m_pPaperDimensions->getValue( i )->m_aValue;
1372         PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1373         PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1374         PDWidth     /= (double)nWidth;
1375         PDHeight    /= (double)nHeight;
1376         if( PDWidth >= 0.9      &&  PDWidth <= 1.1      &&
1377             PDHeight >= 0.9     &&  PDHeight <= 1.1         )
1378         {
1379             fNewSort =
1380                 (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight);
1381             if( fNewSort == 0.0 ) // perfect match
1382                 return m_pPaperDimensions->getValue( i )->m_aOption;
1383 
1384             if( fNewSort < fSort )
1385             {
1386                 fSort = fNewSort;
1387                 nPDim = i;
1388             }
1389         }
1390     }
1391 
1392     static bool bDontSwap = false;
1393     if( nPDim == -1 && ! bDontSwap )
1394     {
1395         // swap portrait/landscape and try again
1396         bDontSwap = true;
1397         String rRet = matchPaper( nHeight, nWidth );
1398         bDontSwap = false;
1399         return rRet;
1400     }
1401 
1402     return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : String();
1403 }
1404 
1405 String PPDParser::getDefaultInputSlot() const
1406 {
1407     if( m_pDefaultInputSlot )
1408         return m_pDefaultInputSlot->m_aValue;
1409     return String();
1410 }
1411 
1412 String PPDParser::getSlot( int nSlot ) const
1413 {
1414     if( ! m_pInputSlots )
1415         return String();
1416 
1417     if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1418         return m_pInputSlots->getValue( nSlot )->m_aOption;
1419     else if( m_pInputSlots->countValues() > 0 )
1420         return m_pInputSlots->getValue( (sal_uLong)0 )->m_aOption;
1421 
1422     return String();
1423 }
1424 
1425 String PPDParser::getSlotCommand( int nSlot ) const
1426 {
1427     if( ! m_pInputSlots )
1428         return String();
1429 
1430     if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1431         return m_pInputSlots->getValue( nSlot )->m_aValue;
1432     else if( m_pInputSlots->countValues() > 0 )
1433         return m_pInputSlots->getValue( (sal_uLong)0 )->m_aValue;
1434 
1435     return String();
1436 }
1437 
1438 String PPDParser::getSlotCommand( const String& rSlot ) const
1439 {
1440     if( ! m_pInputSlots )
1441         return String();
1442 
1443     for( int i=0; i < m_pInputSlots->countValues(); i++ )
1444     {
1445         const PPDValue* pValue = m_pInputSlots->getValue( i );
1446         if( pValue->m_aOption == rSlot )
1447             return pValue->m_aValue;
1448     }
1449     return String();
1450 }
1451 
1452 String PPDParser::getPaperDimension( int nPaperDimension ) const
1453 {
1454     if( ! m_pPaperDimensions )
1455         return String();
1456 
1457     if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1458         return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption;
1459     else if( m_pPaperDimensions->countValues() > 0 )
1460         return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aOption;
1461 
1462     return String();
1463 }
1464 
1465 String PPDParser::getPaperDimensionCommand( int nPaperDimension ) const
1466 {
1467     if( ! m_pPaperDimensions )
1468         return String();
1469 
1470     if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1471         return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue;
1472     else if( m_pPaperDimensions->countValues() > 0 )
1473         return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aValue;
1474 
1475     return String();
1476 }
1477 
1478 String PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const
1479 {
1480     if( ! m_pPaperDimensions )
1481         return String();
1482 
1483     for( int i=0; i < m_pPaperDimensions->countValues(); i++ )
1484     {
1485         const PPDValue* pValue = m_pPaperDimensions->getValue( i );
1486         if( pValue->m_aOption == rPaperDimension )
1487             return pValue->m_aValue;
1488     }
1489     return String();
1490 }
1491 
1492 void PPDParser::getResolutionFromString(
1493                                         const String& rString,
1494                                         int& rXRes, int& rYRes ) const
1495 {
1496     int nPos = 0, nDPIPos;
1497 
1498     rXRes = rYRes = 300;
1499 
1500     nDPIPos = rString.SearchAscii( "dpi" );
1501     if( nDPIPos != STRING_NOTFOUND )
1502     {
1503         if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND )
1504         {
1505             rXRes = rString.Copy( 0, nPos ).ToInt32();
1506             rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32();
1507         }
1508         else
1509             rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32();
1510     }
1511 }
1512 
1513 void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const
1514 {
1515     if( m_pDefaultResolution )
1516     {
1517         getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes );
1518         return;
1519     }
1520 
1521     rXRes = 300;
1522     rYRes = 300;
1523 }
1524 
1525 int PPDParser::getResolutions() const
1526 {
1527     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) &&
1528         m_pDefaultResolution )
1529         return 1;
1530     return m_pResolutions ? m_pResolutions->countValues() : 0;
1531 }
1532 
1533 void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const
1534 {
1535     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 )
1536     {
1537         getDefaultResolution( rXRes, rYRes );
1538         return;
1539     }
1540     if( ! m_pResolutions )
1541         return;
1542 
1543     getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption,
1544                              rXRes, rYRes );
1545 }
1546 
1547 String PPDParser::getResolutionCommand( int nXRes, int nYRes ) const
1548 {
1549     if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution )
1550         return m_pDefaultResolution->m_aValue;
1551 
1552     if( ! m_pResolutions )
1553         return String();
1554 
1555     int nX, nY;
1556     for( int i = 0; i < m_pResolutions->countValues(); i++ )
1557     {
1558         getResolutionFromString( m_pResolutions->getValue( i )->m_aOption,
1559                                  nX, nY );
1560         if( nX == nXRes && nY == nYRes )
1561             return m_pResolutions->getValue( i )->m_aValue;
1562     }
1563     return String();
1564 }
1565 
1566 String PPDParser::getDefaultDuplexType() const
1567 {
1568     if( m_pDefaultDuplexType )
1569         return m_pDefaultDuplexType->m_aValue;
1570     return String();
1571 }
1572 
1573 String PPDParser::getDuplex( int nDuplex ) const
1574 {
1575     if( ! m_pDuplexTypes )
1576         return String();
1577 
1578     if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1579         return m_pDuplexTypes->getValue( nDuplex )->m_aOption;
1580     else if( m_pDuplexTypes->countValues() > 0 )
1581         return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aOption;
1582 
1583     return String();
1584 }
1585 
1586 String PPDParser::getDuplexCommand( int nDuplex ) const
1587 {
1588     if( ! m_pDuplexTypes )
1589         return String();
1590 
1591     if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1592         return m_pDuplexTypes->getValue( nDuplex )->m_aValue;
1593     else if( m_pDuplexTypes->countValues() > 0 )
1594         return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aValue;
1595 
1596     return String();
1597 }
1598 
1599 String PPDParser::getDuplexCommand( const String& rDuplex ) const
1600 {
1601     if( ! m_pDuplexTypes )
1602         return String();
1603 
1604     for( int i=0; i < m_pDuplexTypes->countValues(); i++ )
1605     {
1606         const PPDValue* pValue = m_pDuplexTypes->getValue( i );
1607         if( pValue->m_aOption == rDuplex )
1608             return pValue->m_aValue;
1609     }
1610     return String();
1611 }
1612 
1613 void PPDParser::getFontAttributes(
1614                                   int nFont,
1615                                   String& rEncoding,
1616                                   String& rCharset ) const
1617 {
1618     if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() )
1619     {
1620         String aAttribs =
1621             WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue );
1622         rEncoding   = GetCommandLineToken( 0, aAttribs );
1623         rCharset    = GetCommandLineToken( 2, aAttribs );
1624     }
1625 }
1626 
1627 void PPDParser::getFontAttributes(
1628                                   const String& rFont,
1629                                   String& rEncoding,
1630                                   String& rCharset ) const
1631 {
1632     if( m_pFontList )
1633     {
1634         for( int i = 0; i < m_pFontList->countValues(); i++ )
1635             if( m_pFontList->getValue( i )->m_aOption == rFont )
1636                 getFontAttributes( i, rEncoding, rCharset );
1637     }
1638 }
1639 
1640 String PPDParser::getFont( int nFont ) const
1641 {
1642     if( ! m_pFontList )
1643         return String();
1644 
1645     if( nFont >=0 && nFont < m_pFontList->countValues() )
1646         return m_pFontList->getValue( nFont )->m_aOption;
1647     return String();
1648 }
1649 
1650 rtl::OUString PPDParser::translateKey( const rtl::OUString& i_rKey,
1651                                        const com::sun::star::lang::Locale& i_rLocale ) const
1652 {
1653     rtl::OUString aResult( m_pTranslator->translateKey( i_rKey, i_rLocale ) );
1654     if( aResult.getLength() == 0 )
1655         aResult = i_rKey;
1656     return aResult;
1657 }
1658 
1659 rtl::OUString PPDParser::translateOption( const rtl::OUString& i_rKey,
1660                                           const rtl::OUString& i_rOption,
1661                                           const com::sun::star::lang::Locale& i_rLocale ) const
1662 {
1663     rtl::OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption, i_rLocale ) );
1664     if( aResult.getLength() == 0 )
1665         aResult = i_rOption;
1666     return aResult;
1667 }
1668 
1669 rtl::OUString PPDParser::translateValue( const rtl::OUString& i_rKey,
1670                                          const rtl::OUString& i_rOption,
1671                                          const rtl::OUString& i_rValue,
1672                                          const com::sun::star::lang::Locale& i_rLocale ) const
1673 {
1674     rtl::OUString aResult( m_pTranslator->translateValue( i_rKey, i_rOption, i_rValue, i_rLocale ) );
1675     if( aResult.getLength() == 0 )
1676         aResult = i_rValue;
1677     return aResult;
1678 }
1679 
1680 /*
1681  *  PPDKey
1682  */
1683 
1684 PPDKey::PPDKey( const String& rKey ) :
1685         m_aKey( rKey ),
1686         m_pDefaultValue( NULL ),
1687         m_bQueryValue( false ),
1688         m_bUIOption( false ),
1689         m_eUIType( PickOne ),
1690         m_nOrderDependency( 100 ),
1691         m_eSetupType( AnySetup )
1692 {
1693 }
1694 
1695 // -------------------------------------------------------------------
1696 
1697 PPDKey::~PPDKey()
1698 {
1699 }
1700 
1701 // -------------------------------------------------------------------
1702 
1703 const PPDValue* PPDKey::getValue( int n ) const
1704 {
1705     return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL;
1706 }
1707 
1708 // -------------------------------------------------------------------
1709 
1710 const PPDValue* PPDKey::getValue( const String& rOption ) const
1711 {
1712     PPDKey::hash_type::const_iterator it = m_aValues.find( rOption );
1713     return it != m_aValues.end() ? &it->second : NULL;
1714 }
1715 
1716 // -------------------------------------------------------------------
1717 
1718 const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const
1719 {
1720     const PPDValue* pValue = getValue( rOption );
1721     if( ! pValue )
1722     {
1723         for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ )
1724             if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) )
1725                 pValue = m_aOrderedValues[n];
1726     }
1727 
1728     return pValue;
1729 }
1730 
1731 // -------------------------------------------------------------------
1732 
1733 void PPDKey::eraseValue( const String& rOption )
1734 {
1735     PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1736     if( it == m_aValues.end() )
1737         return;
1738 
1739     for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit )
1740     {
1741         if( *vit == &(it->second ) )
1742         {
1743             m_aOrderedValues.erase( vit );
1744             break;
1745         }
1746     }
1747     m_aValues.erase( it );
1748 }
1749 
1750 // -------------------------------------------------------------------
1751 
1752 PPDValue* PPDKey::insertValue( const String& rOption )
1753 {
1754     if( m_aValues.find( rOption ) != m_aValues.end() )
1755         return NULL;
1756 
1757     PPDValue aValue;
1758     aValue.m_aOption = rOption;
1759     m_aValues[ rOption ] = aValue;
1760     PPDValue* pValue = &m_aValues[rOption];
1761     m_aOrderedValues.push_back( pValue );
1762     return pValue;
1763 }
1764 
1765 // -------------------------------------------------------------------
1766 
1767 /*
1768  * PPDContext
1769  */
1770 
1771 PPDContext::PPDContext( const PPDParser* pParser ) :
1772         m_pParser( pParser )
1773 {
1774 }
1775 
1776 // -------------------------------------------------------------------
1777 
1778 PPDContext& PPDContext::operator=( const PPDContext& rCopy )
1779 {
1780     m_pParser           = rCopy.m_pParser;
1781     m_aCurrentValues    = rCopy.m_aCurrentValues;
1782     return *this;
1783 }
1784 
1785 // -------------------------------------------------------------------
1786 
1787 PPDContext::~PPDContext()
1788 {
1789 }
1790 
1791 // -------------------------------------------------------------------
1792 
1793 const PPDKey* PPDContext::getModifiedKey( int n ) const
1794 {
1795     hash_type::const_iterator it;
1796     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it )
1797         ;
1798     return it != m_aCurrentValues.end() ? it->first : NULL;
1799 }
1800 
1801 // -------------------------------------------------------------------
1802 
1803 void PPDContext::setParser( const PPDParser* pParser )
1804 {
1805     if( pParser != m_pParser )
1806     {
1807         m_aCurrentValues.clear();
1808         m_pParser = pParser;
1809     }
1810 }
1811 
1812 // -------------------------------------------------------------------
1813 
1814 const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const
1815 {
1816     if( ! m_pParser )
1817         return NULL;
1818 
1819     hash_type::const_iterator it;
1820     it = m_aCurrentValues.find( pKey );
1821     if( it != m_aCurrentValues.end() )
1822         return it->second;
1823 
1824     if( ! m_pParser->hasKey( pKey ) )
1825         return NULL;
1826 
1827     const PPDValue* pValue = pKey->getDefaultValue();
1828     if( ! pValue )
1829         pValue = pKey->getValue( 0 );
1830 
1831     return pValue;
1832 }
1833 
1834 // -------------------------------------------------------------------
1835 
1836 const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1837 {
1838     if( ! m_pParser || ! pKey )
1839         return NULL;
1840 
1841     // pValue can be NULL - it means ignore this option
1842 
1843     if( ! m_pParser->hasKey( pKey ) )
1844         return NULL;
1845 
1846     // check constraints
1847     if( pValue )
1848     {
1849         if( bDontCareForConstraints )
1850         {
1851             m_aCurrentValues[ pKey ] = pValue;
1852         }
1853         else if( checkConstraints( pKey, pValue, true ) )
1854         {
1855             m_aCurrentValues[ pKey ] = pValue;
1856 
1857             // after setting this value, check all constraints !
1858             hash_type::iterator it = m_aCurrentValues.begin();
1859             while(  it != m_aCurrentValues.end() )
1860             {
1861                 if( it->first != pKey &&
1862                     ! checkConstraints( it->first, it->second, false ) )
1863                 {
1864 #ifdef __DEBUG
1865                     fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n",
1866                              it->first->getKey().GetStr(),
1867                              it->second->m_aOption.GetStr(),
1868                              pKey->getKey().GetStr(),
1869                              pValue->m_aOption.GetStr() );
1870 #endif
1871                     resetValue( it->first, true );
1872                     it = m_aCurrentValues.begin();
1873                 }
1874                 else
1875                     ++it;
1876             }
1877         }
1878     }
1879     else
1880         m_aCurrentValues[ pKey ] = NULL;
1881 
1882     return pValue;
1883 }
1884 
1885 // -------------------------------------------------------------------
1886 
1887 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1888 {
1889     if( ! m_pParser || ! pKey || ! pValue )
1890         return false;
1891 
1892     // ensure that this key is already in the list if it exists at all
1893     if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() )
1894         return checkConstraints( pKey, pValue, false );
1895 
1896     // it is not in the list, insert it temporarily
1897     bool bRet = false;
1898     if( m_pParser->hasKey( pKey ) )
1899     {
1900         const PPDValue* pDefValue = pKey->getDefaultValue();
1901         m_aCurrentValues[ pKey ] = pDefValue;
1902         bRet = checkConstraints( pKey, pValue, false );
1903         m_aCurrentValues.erase( pKey );
1904     }
1905 
1906     return bRet;
1907 }
1908 
1909 // -------------------------------------------------------------------
1910 
1911 bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1912 {
1913     if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1914         return false;
1915 
1916     const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
1917     if( ! pResetValue )
1918         pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) );
1919     if( ! pResetValue && bDefaultable )
1920         pResetValue = pKey->getDefaultValue();
1921 
1922     bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false;
1923 
1924     return bRet;
1925 }
1926 
1927 // -------------------------------------------------------------------
1928 
1929 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1930 {
1931     if( ! pNewValue )
1932         return true;
1933 
1934     // sanity checks
1935     if( ! m_pParser )
1936         return false;
1937 
1938     if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1939         return false;
1940 
1941     // None / False and the default can always be set, but be careful !
1942     // setting them might influence constrained values
1943     if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) ||
1944         pNewValue == pKey->getDefaultValue() )
1945         return true;
1946 
1947     const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() );
1948     for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it )
1949     {
1950         const PPDKey* pLeft     = it->m_pKey1;
1951         const PPDKey* pRight    = it->m_pKey2;
1952         if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) )
1953             continue;
1954 
1955         const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft;
1956         const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1;
1957         const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2;
1958 
1959         // syntax *Key1 option1 *Key2 option2
1960         if( pKeyOption && pOtherKeyOption )
1961         {
1962             if( pNewValue != pKeyOption )
1963                 continue;
1964             if( pOtherKeyOption == getValue( pOtherKey ) )
1965             {
1966                 return false;
1967             }
1968         }
1969         // syntax *Key1 option *Key2  or  *Key1 *Key2 option
1970         else if( pOtherKeyOption || pKeyOption )
1971         {
1972             if( pKeyOption )
1973             {
1974                 if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) )
1975                     continue; // this should not happen, PPD broken
1976 
1977                 if( pKeyOption == pNewValue &&
1978                     ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) &&
1979                     ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) )
1980                 {
1981                     // check if the other value can be reset and
1982                     // do so if possible
1983                     if( bDoReset && resetValue( pOtherKey ) )
1984                         continue;
1985 
1986                     return false;
1987                 }
1988             }
1989             else if( pOtherKeyOption )
1990             {
1991                 if( getValue( pOtherKey ) == pOtherKeyOption &&
1992                     ! pNewValue->m_aOption.EqualsAscii( "None" ) &&
1993                     ! pNewValue->m_aOption.EqualsAscii( "False" ) )
1994                     return false;
1995             }
1996             else
1997             {
1998                 // this should not happen, PPD is broken
1999             }
2000         }
2001         // syntax *Key1 *Key2
2002         else
2003         {
2004             const PPDValue* pOtherValue = getValue( pOtherKey );
2005             if( ! pOtherValue->m_aOption.EqualsAscii( "None" )  &&
2006                 ! pOtherValue->m_aOption.EqualsAscii( "False" )     &&
2007                 ! pNewValue->m_aOption.EqualsAscii( "None" )        &&
2008                 ! pNewValue->m_aOption.EqualsAscii( "False" ) )
2009                 return false;
2010         }
2011     }
2012     return true;
2013 }
2014 
2015 // -------------------------------------------------------------------
2016 
2017 void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues )
2018 {
2019     rValues.clear();
2020 
2021     if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) )
2022         return;
2023 
2024     int nValues = pKey->countValues();
2025     for( int i = 0; i < nValues; i++ )
2026     {
2027         const PPDValue* pValue = pKey->getValue( i );
2028         if( checkConstraints( pKey, pValue ) )
2029             rValues.push_back( pValue );
2030     }
2031 }
2032 
2033 
2034 // -------------------------------------------------------------------
2035 
2036 void* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const
2037 {
2038     rBytes = 0;
2039     if( ! m_aCurrentValues.size() )
2040         return NULL;
2041     hash_type::const_iterator it;
2042     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
2043     {
2044         ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
2045         rBytes += aCopy.Len();
2046         rBytes += 1; // for ':'
2047         if( it->second )
2048         {
2049             aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
2050             rBytes += aCopy.Len();
2051         }
2052         else
2053             rBytes += 4;
2054         rBytes += 1; // for '\0'
2055     }
2056     rBytes += 1;
2057     void* pBuffer = new char[ rBytes ];
2058     memset( pBuffer, 0, rBytes );
2059     char* pRun = (char*)pBuffer;
2060     for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
2061     {
2062         ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
2063         int nBytes = aCopy.Len();
2064         memcpy( pRun, aCopy.GetBuffer(), nBytes );
2065         pRun += nBytes;
2066         *pRun++ = ':';
2067         if( it->second )
2068             aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
2069         else
2070             aCopy = "*nil";
2071         nBytes = aCopy.Len();
2072         memcpy( pRun, aCopy.GetBuffer(), nBytes );
2073         pRun += nBytes;
2074 
2075         *pRun++ = 0;
2076     }
2077     return pBuffer;
2078 }
2079 
2080 // -------------------------------------------------------------------
2081 
2082 void PPDContext::rebuildFromStreamBuffer( void* pBuffer, sal_uLong nBytes )
2083 {
2084     if( ! m_pParser )
2085         return;
2086 
2087     m_aCurrentValues.clear();
2088 
2089     char* pRun = (char*)pBuffer;
2090     while( nBytes && *pRun )
2091     {
2092         ByteString aLine( pRun );
2093         int nPos = aLine.Search( ':' );
2094         if( nPos != STRING_NOTFOUND )
2095         {
2096             const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
2097             if( pKey )
2098             {
2099                 const PPDValue* pValue = NULL;
2100                 String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 );
2101                 if( ! aOption.EqualsAscii( "*nil" ) )
2102                     pValue = pKey->getValue( aOption );
2103                 m_aCurrentValues[ pKey ] = pValue;
2104 #ifdef __DEBUG
2105                 fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "<nil>" );
2106 #endif
2107             }
2108         }
2109         nBytes -= aLine.Len()+1;
2110         pRun += aLine.Len()+1;
2111     }
2112 }
2113 
2114 // -------------------------------------------------------------------
2115 
2116 int PPDContext::getRenderResolution() const
2117 {
2118     // initialize to reasonable default, if parser is not set
2119     int nDPI = 300;
2120     if( m_pParser )
2121     {
2122         int nDPIx = 300, nDPIy = 300;
2123         const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
2124         if( pKey )
2125         {
2126             const PPDValue* pValue = getValue( pKey );
2127             if( pValue )
2128                 m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
2129             else
2130                 m_pParser->getDefaultResolution( nDPIx, nDPIy );
2131         }
2132         else
2133             m_pParser->getDefaultResolution( nDPIx, nDPIy );
2134 
2135         nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy;
2136     }
2137     return  nDPI;
2138 }
2139 
2140 // -------------------------------------------------------------------
2141 
2142 void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const
2143 {
2144     // initialize to reasonable default, if parser is not set
2145     rPaper  = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) );
2146     rWidth  = 595;
2147     rHeight = 842;
2148     if( m_pParser )
2149     {
2150         const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
2151         if( pKey )
2152         {
2153             const PPDValue* pValue = getValue( pKey );
2154             if( pValue )
2155             {
2156                 rPaper = pValue->m_aOption;
2157                 m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
2158             }
2159             else
2160             {
2161                 rPaper = m_pParser->getDefaultPaperDimension();
2162                 m_pParser->getDefaultPaperDimension( rWidth, rHeight );
2163             }
2164         }
2165     }
2166 }
2167