Update ooo320-m1
[ooovba.git] / vcl / unx / source / printer / ppdparser.cxx
blobc37ac0d316625d6241b5b236fb6ca687bab42e54
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: ppdparser.cxx,v $
10 * $Revision: 1.27 $
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_vcl.hxx"
34 #include <stdlib.h>
35 #include <stdio.h>
37 #include <hash_map>
39 #include "vcl/ppdparser.hxx"
40 #include "vcl/strhelper.hxx"
41 #include "vcl/helper.hxx"
42 #include "cupsmgr.hxx"
43 #include "tools/debug.hxx"
44 #include "tools/urlobj.hxx"
45 #include "tools/stream.hxx"
46 #include "tools/zcodec.hxx"
47 #include "osl/mutex.hxx"
48 #include "osl/file.hxx"
49 #include "osl/process.h"
50 #include "osl/thread.h"
51 #include "rtl/strbuf.hxx"
52 #include "rtl/ustrbuf.hxx"
54 using namespace psp;
55 using namespace rtl;
57 #undef DBG_ASSERT
58 #if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1)
59 #define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() )
60 #define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); }
61 #else
62 #define DBG_ASSERT( x, y )
63 #endif
65 std::list< PPDParser* > PPDParser::aAllParsers;
66 std::hash_map< OUString, OUString, OUStringHash >* PPDParser::pAllPPDFiles = NULL;
67 static String aEmptyString;
69 class PPDDecompressStream
71 SvFileStream* mpFileStream;
72 SvMemoryStream* mpMemStream;
73 rtl::OUString maFileName;
75 // forbid copying
76 PPDDecompressStream( const PPDDecompressStream& );
77 PPDDecompressStream& operator=(const PPDDecompressStream& );
79 public:
80 PPDDecompressStream( const rtl::OUString& rFile );
81 ~PPDDecompressStream();
83 bool IsOpen() const;
84 bool IsEof() const;
85 void ReadLine( ByteString& o_rLine);
86 void Open( const rtl::OUString& i_rFile );
87 void Close();
88 const rtl::OUString& GetFileName() const { return maFileName; }
91 PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) :
92 mpFileStream( NULL ),
93 mpMemStream( NULL )
95 Open( i_rFile );
98 PPDDecompressStream::~PPDDecompressStream()
100 Close();
103 void PPDDecompressStream::Open( const rtl::OUString& i_rFile )
105 Close();
107 mpFileStream = new SvFileStream( i_rFile, STREAM_READ );
108 maFileName = mpFileStream->GetFileName();
110 if( ! mpFileStream->IsOpen() )
112 Close();
113 return;
116 ByteString aLine;
117 mpFileStream->ReadLine( aLine );
118 mpFileStream->Seek( 0 );
120 // check for compress'ed or gzip'ed file
121 ULONG nCompressMethod = 0;
122 if( aLine.Len() > 1 && static_cast<unsigned char>(aLine.GetChar( 0 )) == 0x1f )
124 if( static_cast<unsigned char>(aLine.GetChar( 1 )) == 0x8b ) // check for gzip
125 nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB;
128 if( nCompressMethod != 0 )
130 // so let's try to decompress the stream
131 mpMemStream = new SvMemoryStream( 4096, 4096 );
132 ZCodec aCodec;
133 aCodec.BeginCompression( nCompressMethod );
134 long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream );
135 aCodec.EndCompression();
136 if( nComp < 0 )
138 // decompression failed, must be an uncompressed stream after all
139 delete mpMemStream, mpMemStream = NULL;
140 mpFileStream->Seek( 0 );
142 else
144 // compression successfull, can get rid of file stream
145 delete mpFileStream, mpFileStream = NULL;
146 mpMemStream->Seek( 0 );
151 void PPDDecompressStream::Close()
153 delete mpMemStream, mpMemStream = NULL;
154 delete mpFileStream, mpFileStream = NULL;
157 bool PPDDecompressStream::IsOpen() const
159 return (mpMemStream || (mpFileStream && mpFileStream->IsOpen()));
162 bool PPDDecompressStream::IsEof() const
164 return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) );
167 void PPDDecompressStream::ReadLine( ByteString& o_rLine )
169 if( mpMemStream )
170 mpMemStream->ReadLine( o_rLine );
171 else if( mpFileStream )
172 mpFileStream->ReadLine( o_rLine );
175 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 )
177 osl::DirectoryItem aLinkItem;
178 osl::FileBase::RC aRet = osl::FileBase::E_None;
180 if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None )
182 osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL );
183 if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None )
185 if( aStatus.getFileType() == osl::FileStatus::Link )
187 if( nLinkLevel > 0 )
188 aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 );
189 else
190 aRet = osl::FileBase::E_MULTIHOP;
192 else
194 o_rResolvedURL = i_rURL;
195 o_rBaseName = aStatus.getFileName();
196 o_rType = aStatus.getFileType();
200 return aRet;
203 void PPDParser::scanPPDDir( const String& rDir )
205 static struct suffix_t
207 const sal_Char* pSuffix;
208 const sal_Int32 nSuffixLen;
209 } const pSuffixes[] =
210 { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } };
212 const int nSuffixes = sizeof(pSuffixes)/sizeof(pSuffixes[0]);
214 osl::Directory aDir( rDir );
215 aDir.open();
216 osl::DirectoryItem aItem;
218 INetURLObject aPPDDir(rDir);
219 while( aDir.getNextItem( aItem ) == osl::FileBase::E_None )
221 osl::FileStatus aStatus( FileStatusMask_FileName );
222 if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
224 rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 );
225 aURLBuf.append( rDir );
226 aURLBuf.append( sal_Unicode( '/' ) );
227 aURLBuf.append( aStatus.getFileName() );
229 rtl::OUString aFileURL, aFileName;
230 osl::FileStatus::Type eType = osl::FileStatus::Unknown;
232 if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None )
234 if( eType == osl::FileStatus::Regular )
236 INetURLObject aPPDFile = aPPDDir;
237 aPPDFile.Append( aFileName );
239 // match extension
240 for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ )
242 if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen )
244 if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) )
246 (*pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName();
247 break;
252 else if( eType == osl::FileStatus::Directory )
254 scanPPDDir( aFileURL );
259 aDir.close();
262 void PPDParser::initPPDFiles()
264 if( pAllPPDFiles )
265 return;
267 pAllPPDFiles = new std::hash_map< OUString, OUString, OUStringHash >();
269 // check installation directories
270 std::list< OUString > aPathList;
271 psp::getPrinterPathList( aPathList, PRINTER_PPDDIR );
272 for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it )
274 INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
275 scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) );
277 if( pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() )
279 // last try: search in directory of executable (mainly for setup)
280 OUString aExe;
281 if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
283 INetURLObject aDir( aExe );
284 aDir.removeSegment();
285 #ifdef DEBUG
286 fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() );
287 #endif
288 scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) );
289 #ifdef DEBUG
290 fprintf( stderr, "SGENPRT %s\n", pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ? "not found" : "found" );
291 #endif
296 void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers, bool bRefresh )
298 if( bRefresh )
300 delete pAllPPDFiles;
301 pAllPPDFiles = NULL;
304 initPPDFiles();
305 o_rDrivers.clear();
307 std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
308 for( it = pAllPPDFiles->begin(); it != pAllPPDFiles->end(); ++it )
309 o_rDrivers.push_back( it->first );
312 String PPDParser::getPPDFile( const String& rFile )
314 INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL );
315 // someone might enter a full qualified name here
316 PPDDecompressStream aStream( aPPD.PathToFileName() );
317 if( ! aStream.IsOpen() )
319 std::hash_map< OUString, OUString, OUStringHash >::const_iterator it;
321 bool bRetry = true;
324 initPPDFiles();
325 // some PPD files contain dots beside the extension, so try name first
326 // and cut of points after that
327 rtl::OUString aBase( rFile );
328 sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) );
329 if( nLastIndex >= 0 )
330 aBase = aBase.copy( nLastIndex+1 );
333 it = pAllPPDFiles->find( aBase );
334 nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) );
335 if( nLastIndex > 0 )
336 aBase = aBase.copy( 0, nLastIndex );
337 } while( it == pAllPPDFiles->end() && nLastIndex > 0 );
339 if( it == pAllPPDFiles->end() && bRetry )
341 // a new file ? rehash
342 delete pAllPPDFiles; pAllPPDFiles = NULL;
343 bRetry = false;
344 // note this is optimized for office start where
345 // no new files occur and initPPDFiles is called only once
347 } while( ! pAllPPDFiles );
349 if( it != pAllPPDFiles->end() )
350 aStream.Open( it->second );
353 String aRet;
354 if( aStream.IsOpen() )
356 ByteString aLine;
357 aStream.ReadLine( aLine );
358 if( aLine.Search( "*PPD-Adobe" ) == 0 )
359 aRet = aStream.GetFileName();
360 else
362 // our *Include hack does usually not begin
363 // with *PPD-Adobe, so try some lines for *Include
364 int nLines = 10;
365 while( aLine.Search( "*Include" ) != 0 && --nLines )
366 aStream.ReadLine( aLine );
367 if( nLines )
368 aRet = aStream.GetFileName();
372 return aRet;
375 String PPDParser::getPPDPrinterName( const String& rFile )
377 String aPath = getPPDFile( rFile );
378 String aName;
380 // read in the file
381 PPDDecompressStream aStream( aPath );
382 if( aStream.IsOpen() )
384 String aCurLine;
385 while( ! aStream.IsEof() && aStream.IsOpen() )
387 ByteString aByteLine;
388 aStream.ReadLine( aByteLine );
389 aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 );
390 if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
392 aCurLine.Erase( 0, 9 );
393 aCurLine.EraseLeadingChars( ' ' );
394 aCurLine.EraseTrailingChars( ' ' );
395 aCurLine.EraseLeadingChars( '\t' );
396 aCurLine.EraseTrailingChars( '\t' );
397 aCurLine.EraseTrailingChars( '\r' );
398 aCurLine.EraseTrailingChars( '\n' );
399 aCurLine.EraseLeadingChars( '"' );
400 aCurLine.EraseTrailingChars( '"' );
401 aStream.Close();
402 aStream.Open( getPPDFile( aCurLine ) );
403 continue;
405 if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL )
407 aName = aCurLine.GetToken( 1, '"' );
408 break;
410 else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL )
411 aName = aCurLine.GetToken( 1, '"' );
414 return aName;
417 const PPDParser* PPDParser::getParser( const String& rFile )
419 static ::osl::Mutex aMutex;
420 ::osl::Guard< ::osl::Mutex > aGuard( aMutex );
422 String aFile = rFile;
423 if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
424 aFile = getPPDFile( rFile );
425 if( ! aFile.Len() )
427 #if OSL_DEBUG_LEVEL > 1
428 fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() );
429 #endif
430 return NULL;
433 for( ::std::list< PPDParser* >::const_iterator it = aAllParsers.begin(); it != aAllParsers.end(); ++it )
434 if( (*it)->m_aFile == aFile )
435 return *it;
437 PPDParser* pNewParser = NULL;
438 if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL )
439 pNewParser = new PPDParser( aFile );
440 else
442 PrinterInfoManager& rMgr = PrinterInfoManager::get();
443 if( rMgr.getType() == PrinterInfoManager::CUPS )
445 pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
448 if( pNewParser )
450 // this may actually be the SGENPRT parser,
451 // so ensure uniquness here
452 aAllParsers.remove( pNewParser );
453 // insert new parser to list
454 aAllParsers.push_front( pNewParser );
456 return pNewParser;
459 void PPDParser::freeAll()
461 while( aAllParsers.begin() != aAllParsers.end() )
463 delete aAllParsers.front();
464 aAllParsers.pop_front();
466 delete pAllPPDFiles;
467 pAllPPDFiles = NULL;
470 PPDParser::PPDParser( const String& rFile ) :
471 m_aFile( rFile ),
472 m_bType42Capable( false ),
473 m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ),
474 m_pDefaultImageableArea( NULL ),
475 m_pImageableAreas( NULL ),
476 m_pDefaultPaperDimension( NULL ),
477 m_pPaperDimensions( NULL ),
478 m_pDefaultInputSlot( NULL ),
479 m_pInputSlots( NULL ),
480 m_pDefaultResolution( NULL ),
481 m_pResolutions( NULL ),
482 m_pDefaultDuplexType( NULL ),
483 m_pDuplexTypes( NULL ),
484 m_pFontList( NULL )
486 // read in the file
487 std::list< ByteString > aLines;
488 PPDDecompressStream aStream( m_aFile );
489 bool bLanguageEncoding = false;
490 if( aStream.IsOpen() )
492 ByteString aCurLine;
493 while( ! aStream.IsEof() )
495 aStream.ReadLine( aCurLine );
496 if( aCurLine.GetChar( 0 ) == '*' )
498 if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL )
500 aCurLine.Erase( 0, 9 );
501 aCurLine.EraseLeadingChars( ' ' );
502 aCurLine.EraseTrailingChars( ' ' );
503 aCurLine.EraseLeadingChars( '\t' );
504 aCurLine.EraseTrailingChars( '\t' );
505 aCurLine.EraseTrailingChars( '\r' );
506 aCurLine.EraseTrailingChars( '\n' );
507 aCurLine.EraseLeadingChars( '"' );
508 aCurLine.EraseTrailingChars( '"' );
509 aStream.Close();
510 aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) );
511 continue;
513 else if( ! bLanguageEncoding &&
514 aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL )
516 bLanguageEncoding = true; // generally only the first one counts
517 ByteString aLower = aCurLine;
518 aLower.ToLowerAscii();
519 if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND ||
520 aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND )
521 m_aFileEncoding = RTL_TEXTENCODING_MS_1252;
522 else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND )
523 m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2;
524 else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND )
525 m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5;
526 else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND )
527 m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS;
528 else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND )
529 m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
530 else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND )
531 m_aFileEncoding = RTL_TEXTENCODING_UTF8;
534 aLines.push_back( aCurLine );
537 aStream.Close();
539 // now get the Values
540 parse( aLines );
541 #if OSL_DEBUG_LEVEL > 2
542 fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() );
543 for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
545 const PPDKey* pKey = it->second;
546 char* pSetupType = "<unknown>";
547 switch( pKey->m_eSetupType )
549 case PPDKey::ExitServer: pSetupType = "ExitServer";break;
550 case PPDKey::Prolog: pSetupType = "Prolog";break;
551 case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break;
552 case PPDKey::PageSetup: pSetupType = "PageSetup";break;
553 case PPDKey::JCLSetup: pSetupType = "JCLSetup";break;
554 case PPDKey::AnySetup: pSetupType = "AnySetup";break;
555 default: break;
557 fprintf( stderr, "\t\"%s\" (\"%s\") (%d values) OrderDependency: %d %s\n",
558 BSTRING( pKey->getKey() ).GetBuffer(),
559 BSTRING( pKey->m_aUITranslation ).GetBuffer(),
560 pKey->countValues(),
561 pKey->m_nOrderDependency,
562 pSetupType );
563 for( int j = 0; j < pKey->countValues(); j++ )
565 fprintf( stderr, "\t\t" );
566 const PPDValue* pValue = pKey->getValue( j );
567 if( pValue == pKey->m_pDefaultValue )
568 fprintf( stderr, "(Default:) " );
569 char* pVType = "<unknown>";
570 switch( pValue->m_eType )
572 case eInvocation: pVType = "invocation";break;
573 case eQuoted: pVType = "quoted";break;
574 case eString: pVType = "string";break;
575 case eSymbol: pVType = "symbol";break;
576 case eNo: pVType = "no";break;
577 default: break;
579 fprintf( stderr, "option: \"%s\" (\"%s\"), value: type %s \"%s\" (\"%s\")\n",
580 BSTRING( pValue->m_aOption ).GetBuffer(),
581 BSTRING( pValue->m_aOptionTranslation ).GetBuffer(),
582 pVType,
583 BSTRING( pValue->m_aValue ).GetBuffer(),
584 BSTRING( pValue->m_aValueTranslation ).GetBuffer() );
587 fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() );
588 for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit )
590 fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n",
591 BSTRING( cit->m_pKey1->getKey() ).GetBuffer(),
592 cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "<nil>",
593 BSTRING( cit->m_pKey2->getKey() ).GetBuffer(),
594 cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "<nil>"
597 #endif
599 // fill in shortcuts
600 const PPDKey* pKey;
602 m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) );
603 if( m_pImageableAreas )
604 m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
605 DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" );
606 DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" );
608 m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) );
609 if( m_pPaperDimensions )
610 m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
611 DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" );
612 DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" );
614 m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
615 if( m_pResolutions )
616 m_pDefaultResolution = m_pResolutions->getDefaultValue();
617 DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" );
618 DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" );
620 m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
621 if( m_pInputSlots )
622 m_pDefaultInputSlot = m_pInputSlots->getDefaultValue();
623 DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" );
624 DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" );
626 m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
627 if( m_pDuplexTypes )
628 m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue();
630 m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) );
631 DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" );
633 // fill in direct values
634 if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) )
635 m_aPrinterName = pKey->getValue( 0 )->m_aValue;
636 if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) )
637 m_aNickName = pKey->getValue( 0 )->m_aValue;
638 if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) )
639 m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false;
641 if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) )
642 m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32();
643 if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) )
644 m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false;
647 PPDParser::~PPDParser()
649 for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it )
650 delete it->second;
653 void PPDParser::insertKey( const String& rKey, PPDKey* pKey )
655 m_aKeys[ rKey ] = pKey;
656 m_aOrderedKeys.push_back( pKey );
659 const PPDKey* PPDParser::getKey( int n ) const
661 return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL;
664 const PPDKey* PPDParser::getKey( const String& rKey ) const
666 PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey );
667 return it != m_aKeys.end() ? it->second : NULL;
670 bool PPDParser::hasKey( const PPDKey* pKey ) const
672 return
673 pKey ?
674 ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) :
675 false;
678 static sal_uInt8 getNibble( sal_Char cChar )
680 sal_uInt8 nRet = 0;
681 if( cChar >= '0' && cChar <= '9' )
682 nRet = sal_uInt8( cChar - '0' );
683 else if( cChar >= 'A' && cChar <= 'F' )
684 nRet = 10 + sal_uInt8( cChar - 'A' );
685 else if( cChar >= 'a' && cChar <= 'f' )
686 nRet = 10 + sal_uInt8( cChar - 'a' );
687 return nRet;
690 String PPDParser::handleTranslation( const ByteString& rString )
692 int nOrigLen = rString.Len();
693 OStringBuffer aTrans( nOrigLen );
694 const sal_Char* pStr = rString.GetBuffer();
695 const sal_Char* pEnd = pStr + nOrigLen;
696 while( pStr < pEnd )
698 if( *pStr == '<' )
700 pStr++;
701 sal_Char cChar;
702 while( *pStr != '>' && pStr < pEnd-1 )
704 cChar = getNibble( *pStr++ ) << 4;
705 cChar |= getNibble( *pStr++ );
706 aTrans.append( cChar );
708 pStr++;
710 else
711 aTrans.append( *pStr++ );
713 return OStringToOUString( aTrans.makeStringAndClear(), m_aFileEncoding );
716 void PPDParser::parse( ::std::list< ByteString >& rLines )
718 PPDValue* pValue = NULL;
719 PPDKey* pKey = NULL;
721 std::list< ByteString >::iterator line = rLines.begin();
722 PPDParser::hash_type::const_iterator keyit;
723 while( line != rLines.end() )
725 ByteString aCurrentLine( *line );
726 ++line;
727 if( aCurrentLine.GetChar(0) != '*' )
728 continue;
729 if( aCurrentLine.GetChar(1) == '%' )
730 continue;
732 ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) );
733 int nPos = aKey.Search( '/' );
734 if( nPos != STRING_NOTFOUND )
735 aKey.Erase( nPos );
736 aKey.Erase( 0, 1 ); // remove the '*'
738 if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) )
739 continue;
741 if( aKey.Equals( "OpenUI" ) )
743 parseOpenUI( aCurrentLine );
744 continue;
746 else if( aKey.Equals( "OrderDependency" ) )
748 parseOrderDependency( aCurrentLine );
749 continue;
751 else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) )
752 continue; // parsed in pass 2
753 else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled
754 continue;
756 // default values are parsed in pass 2
757 if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL )
758 continue;
760 bool bQuery = false;
761 if( aKey.GetChar( 0 ) == '?' )
763 aKey.Erase( 0, 1 );
764 bQuery = true;
767 String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
768 keyit = m_aKeys.find( aUniKey );
769 if( keyit == m_aKeys.end() )
771 pKey = new PPDKey( aUniKey );
772 insertKey( aUniKey, pKey );
774 else
775 pKey = keyit->second;
777 String aOption;
778 nPos = aCurrentLine.Search( ':' );
779 if( nPos != STRING_NOTFOUND )
781 aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 );
782 aOption = GetCommandLineToken( 1, aOption );
783 int nTransPos = aOption.Search( '/' );
784 if( nTransPos != STRING_NOTFOUND )
785 aOption.Erase( nTransPos );
787 pValue = pKey->insertValue( aOption );
788 if( ! pValue )
789 continue;
791 if( nPos == STRING_NOTFOUND )
793 // have a single main keyword
794 pValue->m_eType = eNo;
795 if( bQuery )
796 pKey->eraseValue( aOption );
797 continue;
800 // found a colon, there may be an option
801 ByteString aLine = aCurrentLine.Copy( 1, nPos-1 );
802 aLine = WhitespaceToSpace( aLine );
803 int nTransPos = aLine.Search( '/' );
804 if( nTransPos != STRING_NOTFOUND )
805 pValue->m_aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ) );
807 // read in more lines if necessary for multiline values
808 aLine = aCurrentLine.Copy( nPos+1 );
809 while( ! ( aLine.GetTokenCount( '"' ) & 1 ) &&
810 line != rLines.end() )
811 // while there is an even number of tokens; that m_eans
812 // an odd number of doubleqoutes
814 // copy the newlines also
815 aLine += '\n';
816 aLine += *line;
817 ++line;
819 aLine = WhitespaceToSpace( aLine );
821 // check for invocation or quoted value
822 if( aLine.GetChar(0) == '"' )
824 aLine.Erase( 0, 1 );
825 nTransPos = aLine.Search( '"' );
826 pValue->m_aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
827 // after the second doublequote can follow a / and a translation
828 pValue->m_aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ) );
829 // check for quoted value
830 if( pValue->m_aOption.Len() &&
831 aKey.CompareTo( "JCL", 3 ) != COMPARE_EQUAL )
832 pValue->m_eType = eInvocation;
833 else
834 pValue->m_eType = eQuoted;
836 // check for symbol value
837 else if( aLine.GetChar(0) == '^' )
839 aLine.Erase( 0, 1 );
840 pValue->m_aValue = String( aLine, RTL_TEXTENCODING_MS_1252 );
841 pValue->m_eType = eSymbol;
843 else
845 // must be a string value then
846 // strictly this is false because string values
847 // can contain any whitespace which is reduced
848 // to one space by now
849 // who cares ...
850 nTransPos = aLine.Search( '/' );
851 if( nTransPos == STRING_NOTFOUND )
852 nTransPos = aLine.Len();
853 pValue->m_aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 );
854 pValue->m_aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ) );
855 pValue->m_eType = eString;
858 // eventually update query and remove from option list
859 if( bQuery && pKey->m_bQueryValue == FALSE )
861 pKey->m_aQueryValue = *pValue;
862 pKey->m_bQueryValue = true;
863 pKey->eraseValue( pValue->m_aOption );
867 // second pass: fill in defaults
868 for( line = rLines.begin(); line != rLines.end(); ++line )
870 ByteString aLine( *line );
871 if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL )
873 String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 );
874 USHORT nPos = aKey.Search( ':' );
875 if( nPos != STRING_NOTFOUND )
877 aKey.Erase( nPos );
878 String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 );
879 keyit = m_aKeys.find( aKey );
880 if( keyit != m_aKeys.end() )
882 pKey = keyit->second;
883 const PPDValue* pDefValue = pKey->getValue( aOption );
884 if( pKey->m_pDefaultValue == NULL )
885 pKey->m_pDefaultValue = pDefValue;
887 else
889 // some PPDs contain defaults for keys that
890 // do not exist otherwise
891 // (example: DefaultResolution)
892 // so invent that key here and have a default value
893 pKey = new PPDKey( aKey );
894 PPDValue* pNewValue = pKey->insertValue( aOption );
895 pNewValue->m_eType = eInvocation; // or what ?
896 insertKey( aKey, pKey );
900 else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL ||
901 aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL )
902 parseConstraint( aLine );
907 void PPDParser::parseOpenUI( const ByteString& rLine )
909 String aTranslation;
910 ByteString aKey = rLine;
912 int nPos = aKey.Search( ':' );
913 if( nPos != STRING_NOTFOUND )
914 aKey.Erase( nPos );
915 nPos = aKey.Search( '/' );
916 if( nPos != STRING_NOTFOUND )
918 aTranslation = handleTranslation( aKey.Copy( nPos + 1 ) );
919 aKey.Erase( nPos );
921 aKey = GetCommandLineToken( 1, aKey );
922 aKey.Erase( 0, 1 );
924 String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 );
925 PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey );
926 PPDKey* pKey;
927 if( keyit == m_aKeys.end() )
929 pKey = new PPDKey( aUniKey );
930 insertKey( aUniKey, pKey );
932 else
933 pKey = keyit->second;
935 pKey->m_bUIOption = true;
936 pKey->m_aUITranslation = aTranslation;
938 ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) );
939 if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL )
940 pKey->m_eUIType = PPDKey::Boolean;
941 else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL )
942 pKey->m_eUIType = PPDKey::PickMany;
943 else
944 pKey->m_eUIType = PPDKey::PickOne;
947 void PPDParser::parseOrderDependency( const ByteString& rLine )
949 ByteString aLine( rLine );
950 int nPos = aLine.Search( ':' );
951 if( nPos != STRING_NOTFOUND )
952 aLine.Erase( 0, nPos+1 );
954 int nOrder = GetCommandLineToken( 0, aLine ).ToInt32();
955 ByteString aSetup = GetCommandLineToken( 1, aLine );
956 String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 );
957 if( aKey.GetChar( 0 ) != '*' )
958 return; // invalid order depency
959 aKey.Erase( 0, 1 );
961 PPDKey* pKey;
962 PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey );
963 if( keyit == m_aKeys.end() )
965 pKey = new PPDKey( aKey );
966 insertKey( aKey, pKey );
968 else
969 pKey = keyit->second;
971 pKey->m_nOrderDependency = nOrder;
972 if( aSetup.Equals( "ExitServer" ) )
973 pKey->m_eSetupType = PPDKey::ExitServer;
974 else if( aSetup.Equals( "Prolog" ) )
975 pKey->m_eSetupType = PPDKey::Prolog;
976 else if( aSetup.Equals( "DocumentSetup" ) )
977 pKey->m_eSetupType = PPDKey::DocumentSetup;
978 else if( aSetup.Equals( "PageSetup" ) )
979 pKey->m_eSetupType = PPDKey::PageSetup;
980 else if( aSetup.Equals( "JCLSetup" ) )
981 pKey->m_eSetupType = PPDKey::JCLSetup;
982 else
983 pKey->m_eSetupType = PPDKey::AnySetup;
986 void PPDParser::parseConstraint( const ByteString& rLine )
988 bool bFailed = false;
990 String aLine( rLine, RTL_TEXTENCODING_MS_1252 );
991 aLine.Erase( 0, rLine.Search( ':' )+1 );
992 PPDConstraint aConstraint;
993 int nTokens = GetCommandLineTokenCount( aLine );
994 for( int i = 0; i < nTokens; i++ )
996 String aToken = GetCommandLineToken( i, aLine );
997 if( aToken.GetChar( 0 ) == '*' )
999 aToken.Erase( 0, 1 );
1000 if( aConstraint.m_pKey1 )
1001 aConstraint.m_pKey2 = getKey( aToken );
1002 else
1003 aConstraint.m_pKey1 = getKey( aToken );
1005 else
1007 if( aConstraint.m_pKey2 )
1009 if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1010 bFailed = true;
1012 else if( aConstraint.m_pKey1 )
1014 if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1015 bFailed = true;
1017 else
1018 // constraint for nonexistent keys; this happens
1019 // e.g. in HP4PLUS3 (#75636#)
1020 bFailed = true;
1023 // there must be two keywords
1024 if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed )
1026 #ifdef __DEBUG
1027 fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() );
1028 #endif
1030 else
1031 m_aConstraints.push_back( aConstraint );
1034 const String& PPDParser::getDefaultPaperDimension() const
1036 if( m_pDefaultPaperDimension )
1037 return m_pDefaultPaperDimension->m_aOption;
1039 return aEmptyString;
1042 bool PPDParser::getMargins(
1043 const String& rPaperName,
1044 int& rLeft, int& rRight,
1045 int& rUpper, int& rLower ) const
1047 if( ! m_pImageableAreas || ! m_pPaperDimensions )
1048 return false;
1050 int nPDim=-1, nImArea=-1, i;
1051 for( i = 0; i < m_pImageableAreas->countValues(); i++ )
1052 if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption )
1053 nImArea = i;
1054 for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1055 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1056 nPDim = i;
1057 if( nPDim == -1 || nImArea == -1 )
1058 return false;
1060 double ImLLx, ImLLy, ImURx, ImURy;
1061 double PDWidth, PDHeight;
1062 String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue;
1063 ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) );
1064 ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) );
1065 ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) );
1066 ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) );
1067 // sscanf( m_pImageableAreas->getValue( nImArea )->m_aValue.GetStr(),
1068 // "%lg%lg%lg%lg", &ImLLx, &ImLLy, &ImURx, &ImURy );
1069 aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1070 PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1071 PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1072 // sscanf( m_pPaperDimensions->getValue( nPDim )->m_aValue.GetStr(),
1073 // "%lg%lg", &PDWidth, &PDHeight );
1074 rLeft = (int)(ImLLx + 0.5);
1075 rLower = (int)(ImLLy + 0.5);
1076 rUpper = (int)(PDHeight - ImURy + 0.5);
1077 rRight = (int)(PDWidth - ImURx + 0.5);
1079 return true;
1082 bool PPDParser::getPaperDimension(
1083 const String& rPaperName,
1084 int& rWidth, int& rHeight ) const
1086 if( ! m_pPaperDimensions )
1087 return false;
1089 int nPDim=-1;
1090 for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1091 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1092 nPDim = i;
1093 if( nPDim == -1 )
1094 return false;
1096 double PDWidth, PDHeight;
1097 String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1098 PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1099 PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1100 rHeight = (int)(PDHeight + 0.5);
1101 rWidth = (int)(PDWidth + 0.5);
1103 return true;
1106 const String& PPDParser::matchPaper( int nWidth, int nHeight ) const
1108 if( ! m_pPaperDimensions )
1109 return aEmptyString;
1111 int nPDim = -1;
1112 double PDWidth, PDHeight;
1113 double fSort = 2e36, fNewSort;
1115 for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1117 String aArea = m_pPaperDimensions->getValue( i )->m_aValue;
1118 PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1119 PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1120 PDWidth /= (double)nWidth;
1121 PDHeight /= (double)nHeight;
1122 if( PDWidth >= 0.9 && PDWidth <= 1.1 &&
1123 PDHeight >= 0.9 && PDHeight <= 1.1 )
1125 fNewSort =
1126 (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight);
1127 if( fNewSort == 0.0 ) // perfect match
1128 return m_pPaperDimensions->getValue( i )->m_aOption;
1130 if( fNewSort < fSort )
1132 fSort = fNewSort;
1133 nPDim = i;
1138 static bool bDontSwap = false;
1139 if( nPDim == -1 && ! bDontSwap )
1141 // swap portrait/landscape and try again
1142 bDontSwap = true;
1143 const String& rRet = matchPaper( nHeight, nWidth );
1144 bDontSwap = false;
1145 return rRet;
1148 return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : aEmptyString;
1151 const String& PPDParser::getDefaultInputSlot() const
1153 if( m_pDefaultInputSlot )
1154 return m_pDefaultInputSlot->m_aValue;
1155 return aEmptyString;
1158 const String& PPDParser::getSlot( int nSlot ) const
1160 if( ! m_pInputSlots )
1161 return aEmptyString;
1163 if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1164 return m_pInputSlots->getValue( nSlot )->m_aOption;
1165 else if( m_pInputSlots->countValues() > 0 )
1166 return m_pInputSlots->getValue( (ULONG)0 )->m_aOption;
1168 return aEmptyString;
1171 const String& PPDParser::getSlotCommand( int nSlot ) const
1173 if( ! m_pInputSlots )
1174 return aEmptyString;
1176 if( nSlot > 0 && nSlot < m_pInputSlots->countValues() )
1177 return m_pInputSlots->getValue( nSlot )->m_aValue;
1178 else if( m_pInputSlots->countValues() > 0 )
1179 return m_pInputSlots->getValue( (ULONG)0 )->m_aValue;
1181 return aEmptyString;
1184 const String& PPDParser::getSlotCommand( const String& rSlot ) const
1186 if( ! m_pInputSlots )
1187 return aEmptyString;
1189 for( int i=0; i < m_pInputSlots->countValues(); i++ )
1191 const PPDValue* pValue = m_pInputSlots->getValue( i );
1192 if( pValue->m_aOption == rSlot )
1193 return pValue->m_aValue;
1195 return aEmptyString;
1198 const String& PPDParser::getPaperDimension( int nPaperDimension ) const
1200 if( ! m_pPaperDimensions )
1201 return aEmptyString;
1203 if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1204 return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption;
1205 else if( m_pPaperDimensions->countValues() > 0 )
1206 return m_pPaperDimensions->getValue( (ULONG)0 )->m_aOption;
1208 return aEmptyString;
1211 const String& PPDParser::getPaperDimensionCommand( int nPaperDimension ) const
1213 if( ! m_pPaperDimensions )
1214 return aEmptyString;
1216 if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() )
1217 return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue;
1218 else if( m_pPaperDimensions->countValues() > 0 )
1219 return m_pPaperDimensions->getValue( (ULONG)0 )->m_aValue;
1221 return aEmptyString;
1224 const String& PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const
1226 if( ! m_pPaperDimensions )
1227 return aEmptyString;
1229 for( int i=0; i < m_pPaperDimensions->countValues(); i++ )
1231 const PPDValue* pValue = m_pPaperDimensions->getValue( i );
1232 if( pValue->m_aOption == rPaperDimension )
1233 return pValue->m_aValue;
1235 return aEmptyString;
1238 void PPDParser::getResolutionFromString(
1239 const String& rString,
1240 int& rXRes, int& rYRes ) const
1242 int nPos = 0, nDPIPos;
1244 rXRes = rYRes = 300;
1246 nDPIPos = rString.SearchAscii( "dpi" );
1247 if( nDPIPos != STRING_NOTFOUND )
1249 if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND )
1251 rXRes = rString.Copy( 0, nPos ).ToInt32();
1252 rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32();
1254 else
1255 rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32();
1259 void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const
1261 if( m_pDefaultResolution )
1263 getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes );
1264 return;
1267 rXRes = 300;
1268 rYRes = 300;
1271 int PPDParser::getResolutions() const
1273 if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) &&
1274 m_pDefaultResolution )
1275 return 1;
1276 return m_pResolutions ? m_pResolutions->countValues() : 0;
1279 void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const
1281 if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 )
1283 getDefaultResolution( rXRes, rYRes );
1284 return;
1286 if( ! m_pResolutions )
1287 return;
1289 getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption,
1290 rXRes, rYRes );
1293 const String& PPDParser::getResolutionCommand( int nXRes, int nYRes ) const
1295 if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution )
1296 return m_pDefaultResolution->m_aValue;
1298 if( ! m_pResolutions )
1299 return aEmptyString;
1301 int nX, nY;
1302 for( int i = 0; i < m_pResolutions->countValues(); i++ )
1304 getResolutionFromString( m_pResolutions->getValue( i )->m_aOption,
1305 nX, nY );
1306 if( nX == nXRes && nY == nYRes )
1307 return m_pResolutions->getValue( i )->m_aValue;
1309 return aEmptyString;
1312 const String& PPDParser::getDefaultDuplexType() const
1314 if( m_pDefaultDuplexType )
1315 return m_pDefaultDuplexType->m_aValue;
1316 return aEmptyString;
1319 const String& PPDParser::getDuplex( int nDuplex ) const
1321 if( ! m_pDuplexTypes )
1322 return aEmptyString;
1324 if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1325 return m_pDuplexTypes->getValue( nDuplex )->m_aOption;
1326 else if( m_pDuplexTypes->countValues() > 0 )
1327 return m_pDuplexTypes->getValue( (ULONG)0 )->m_aOption;
1329 return aEmptyString;
1332 const String& PPDParser::getDuplexCommand( int nDuplex ) const
1334 if( ! m_pDuplexTypes )
1335 return aEmptyString;
1337 if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() )
1338 return m_pDuplexTypes->getValue( nDuplex )->m_aValue;
1339 else if( m_pDuplexTypes->countValues() > 0 )
1340 return m_pDuplexTypes->getValue( (ULONG)0 )->m_aValue;
1342 return aEmptyString;
1345 const String& PPDParser::getDuplexCommand( const String& rDuplex ) const
1347 if( ! m_pDuplexTypes )
1348 return aEmptyString;
1350 for( int i=0; i < m_pDuplexTypes->countValues(); i++ )
1352 const PPDValue* pValue = m_pDuplexTypes->getValue( i );
1353 if( pValue->m_aOption == rDuplex )
1354 return pValue->m_aValue;
1356 return aEmptyString;
1359 void PPDParser::getFontAttributes(
1360 int nFont,
1361 String& rEncoding,
1362 String& rCharset ) const
1364 if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() )
1366 String aAttribs =
1367 WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue );
1368 rEncoding = GetCommandLineToken( 0, aAttribs );
1369 rCharset = GetCommandLineToken( 2, aAttribs );
1373 void PPDParser::getFontAttributes(
1374 const String& rFont,
1375 String& rEncoding,
1376 String& rCharset ) const
1378 if( m_pFontList )
1380 for( int i = 0; i < m_pFontList->countValues(); i++ )
1381 if( m_pFontList->getValue( i )->m_aOption == rFont )
1382 getFontAttributes( i, rEncoding, rCharset );
1386 const String& PPDParser::getFont( int nFont ) const
1388 if( ! m_pFontList )
1389 return aEmptyString;
1391 if( nFont >=0 && nFont < m_pFontList->countValues() )
1392 return m_pFontList->getValue( nFont )->m_aOption;
1393 return aEmptyString;
1397 * PPDKey
1400 PPDKey::PPDKey( const String& rKey ) :
1401 m_aKey( rKey ),
1402 m_pDefaultValue( NULL ),
1403 m_bQueryValue( false ),
1404 m_bUIOption( false ),
1405 m_eUIType( PickOne ),
1406 m_nOrderDependency( 100 ),
1407 m_eSetupType( AnySetup )
1411 // -------------------------------------------------------------------
1413 PPDKey::~PPDKey()
1417 // -------------------------------------------------------------------
1419 const PPDValue* PPDKey::getValue( int n ) const
1421 return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL;
1424 // -------------------------------------------------------------------
1426 const PPDValue* PPDKey::getValue( const String& rOption ) const
1428 PPDKey::hash_type::const_iterator it = m_aValues.find( rOption );
1429 return it != m_aValues.end() ? &it->second : NULL;
1432 // -------------------------------------------------------------------
1434 const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const
1436 const PPDValue* pValue = getValue( rOption );
1437 if( ! pValue )
1439 for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ )
1440 if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) )
1441 pValue = m_aOrderedValues[n];
1444 return pValue;
1447 // -------------------------------------------------------------------
1449 void PPDKey::eraseValue( const String& rOption )
1451 PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1452 if( it == m_aValues.end() )
1453 return;
1455 for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit )
1457 if( *vit == &(it->second ) )
1459 m_aOrderedValues.erase( vit );
1460 break;
1463 m_aValues.erase( it );
1466 // -------------------------------------------------------------------
1468 PPDValue* PPDKey::insertValue( const String& rOption )
1470 if( m_aValues.find( rOption ) != m_aValues.end() )
1471 return NULL;
1473 PPDValue aValue;
1474 aValue.m_aOption = rOption;
1475 m_aValues[ rOption ] = aValue;
1476 PPDValue* pValue = &m_aValues[rOption];
1477 m_aOrderedValues.push_back( pValue );
1478 return pValue;
1481 // -------------------------------------------------------------------
1484 * PPDContext
1487 PPDContext::PPDContext( const PPDParser* pParser ) :
1488 m_pParser( pParser )
1492 // -------------------------------------------------------------------
1494 PPDContext& PPDContext::operator=( const PPDContext& rCopy )
1496 m_pParser = rCopy.m_pParser;
1497 m_aCurrentValues = rCopy.m_aCurrentValues;
1498 return *this;
1501 // -------------------------------------------------------------------
1503 PPDContext::~PPDContext()
1507 // -------------------------------------------------------------------
1509 const PPDKey* PPDContext::getModifiedKey( int n ) const
1511 hash_type::const_iterator it;
1512 for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it )
1514 return it != m_aCurrentValues.end() ? it->first : NULL;
1517 // -------------------------------------------------------------------
1519 void PPDContext::setParser( const PPDParser* pParser )
1521 if( pParser != m_pParser )
1523 m_aCurrentValues.clear();
1524 m_pParser = pParser;
1528 // -------------------------------------------------------------------
1530 const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const
1532 if( ! m_pParser )
1533 return NULL;
1535 hash_type::const_iterator it;
1536 it = m_aCurrentValues.find( pKey );
1537 if( it != m_aCurrentValues.end() )
1538 return it->second;
1540 if( ! m_pParser->hasKey( pKey ) )
1541 return NULL;
1543 const PPDValue* pValue = pKey->getDefaultValue();
1544 if( ! pValue )
1545 pValue = pKey->getValue( 0 );
1547 return pValue;
1550 // -------------------------------------------------------------------
1552 const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1554 if( ! m_pParser || ! pKey )
1555 return NULL;
1557 // pValue can be NULL - it means ignore this option
1559 if( ! m_pParser->hasKey( pKey ) )
1560 return NULL;
1562 // check constraints
1563 if( pValue )
1565 if( bDontCareForConstraints )
1567 m_aCurrentValues[ pKey ] = pValue;
1569 else if( checkConstraints( pKey, pValue, true ) )
1571 m_aCurrentValues[ pKey ] = pValue;
1573 // after setting this value, check all constraints !
1574 hash_type::iterator it = m_aCurrentValues.begin();
1575 while( it != m_aCurrentValues.end() )
1577 if( it->first != pKey &&
1578 ! checkConstraints( it->first, it->second, false ) )
1580 #ifdef __DEBUG
1581 fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n",
1582 it->first->getKey().GetStr(),
1583 it->second->m_aOption.GetStr(),
1584 pKey->getKey().GetStr(),
1585 pValue->m_aOption.GetStr() );
1586 #endif
1587 resetValue( it->first, true );
1588 it = m_aCurrentValues.begin();
1590 else
1591 ++it;
1595 else
1596 m_aCurrentValues[ pKey ] = NULL;
1598 return pValue;
1601 // -------------------------------------------------------------------
1603 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1605 if( ! m_pParser || ! pKey || ! pValue )
1606 return false;
1608 // ensure that this key is already in the list if it exists at all
1609 if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() )
1610 return checkConstraints( pKey, pValue, false );
1612 // it is not in the list, insert it temporarily
1613 bool bRet = false;
1614 if( m_pParser->hasKey( pKey ) )
1616 const PPDValue* pDefValue = pKey->getDefaultValue();
1617 m_aCurrentValues[ pKey ] = pDefValue;
1618 bRet = checkConstraints( pKey, pValue, false );
1619 m_aCurrentValues.erase( pKey );
1622 return bRet;
1625 // -------------------------------------------------------------------
1627 bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1629 if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1630 return false;
1632 const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
1633 if( ! pResetValue )
1634 pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) );
1635 if( ! pResetValue && bDefaultable )
1636 pResetValue = pKey->getDefaultValue();
1638 bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false;
1640 return bRet;
1643 // -------------------------------------------------------------------
1645 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1647 if( ! pNewValue )
1648 return true;
1650 // sanity checks
1651 if( ! m_pParser )
1652 return false;
1654 if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1655 return false;
1657 // None / False and the default can always be set, but be careful !
1658 // setting them might influence constrained values
1659 if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) ||
1660 pNewValue == pKey->getDefaultValue() )
1661 return true;
1663 const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() );
1664 for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it )
1666 const PPDKey* pLeft = it->m_pKey1;
1667 const PPDKey* pRight = it->m_pKey2;
1668 if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) )
1669 continue;
1671 const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft;
1672 const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1;
1673 const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2;
1675 // syntax *Key1 option1 *Key2 option2
1676 if( pKeyOption && pOtherKeyOption )
1678 if( pNewValue != pKeyOption )
1679 continue;
1680 if( pOtherKeyOption == getValue( pOtherKey ) )
1682 return false;
1685 // syntax *Key1 option *Key2 or *Key1 *Key2 option
1686 else if( pOtherKeyOption || pKeyOption )
1688 if( pKeyOption )
1690 if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) )
1691 continue; // this should not happen, PPD broken
1693 if( pKeyOption == pNewValue &&
1694 ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) &&
1695 ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) )
1697 // check if the other value can be reset and
1698 // do so if possible
1699 if( bDoReset && resetValue( pOtherKey ) )
1700 continue;
1702 return false;
1705 else if( pOtherKeyOption )
1707 if( getValue( pOtherKey ) == pOtherKeyOption &&
1708 ! pNewValue->m_aOption.EqualsAscii( "None" ) &&
1709 ! pNewValue->m_aOption.EqualsAscii( "False" ) )
1710 return false;
1712 else
1714 // this should not happen, PPD is broken
1717 // syntax *Key1 *Key2
1718 else
1720 const PPDValue* pOtherValue = getValue( pOtherKey );
1721 if( ! pOtherValue->m_aOption.EqualsAscii( "None" ) &&
1722 ! pOtherValue->m_aOption.EqualsAscii( "False" ) &&
1723 ! pNewValue->m_aOption.EqualsAscii( "None" ) &&
1724 ! pNewValue->m_aOption.EqualsAscii( "False" ) )
1725 return false;
1728 return true;
1731 // -------------------------------------------------------------------
1733 void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues )
1735 rValues.clear();
1737 if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) )
1738 return;
1740 int nValues = pKey->countValues();
1741 for( int i = 0; i < nValues; i++ )
1743 const PPDValue* pValue = pKey->getValue( i );
1744 if( checkConstraints( pKey, pValue ) )
1745 rValues.push_back( pValue );
1750 // -------------------------------------------------------------------
1752 void* PPDContext::getStreamableBuffer( ULONG& rBytes ) const
1754 rBytes = 0;
1755 if( ! m_aCurrentValues.size() )
1756 return NULL;
1757 hash_type::const_iterator it;
1758 for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
1760 ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
1761 rBytes += aCopy.Len();
1762 rBytes += 1; // for ':'
1763 if( it->second )
1765 aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
1766 rBytes += aCopy.Len();
1768 else
1769 rBytes += 4;
1770 rBytes += 1; // for '\0'
1772 rBytes += 1;
1773 void* pBuffer = new char[ rBytes ];
1774 memset( pBuffer, 0, rBytes );
1775 char* pRun = (char*)pBuffer;
1776 for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it )
1778 ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 );
1779 int nBytes = aCopy.Len();
1780 memcpy( pRun, aCopy.GetBuffer(), nBytes );
1781 pRun += nBytes;
1782 *pRun++ = ':';
1783 if( it->second )
1784 aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 );
1785 else
1786 aCopy = "*nil";
1787 nBytes = aCopy.Len();
1788 memcpy( pRun, aCopy.GetBuffer(), nBytes );
1789 pRun += nBytes;
1791 *pRun++ = 0;
1793 return pBuffer;
1796 // -------------------------------------------------------------------
1798 void PPDContext::rebuildFromStreamBuffer( void* pBuffer, ULONG nBytes )
1800 if( ! m_pParser )
1801 return;
1803 m_aCurrentValues.clear();
1805 char* pRun = (char*)pBuffer;
1806 while( nBytes && *pRun )
1808 ByteString aLine( pRun );
1809 int nPos = aLine.Search( ':' );
1810 if( nPos != STRING_NOTFOUND )
1812 const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
1813 if( pKey )
1815 const PPDValue* pValue = NULL;
1816 String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 );
1817 if( ! aOption.EqualsAscii( "*nil" ) )
1818 pValue = pKey->getValue( aOption );
1819 m_aCurrentValues[ pKey ] = pValue;
1820 #ifdef __DEBUG
1821 fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "<nil>" );
1822 #endif
1825 nBytes -= aLine.Len()+1;
1826 pRun += aLine.Len()+1;
1830 // -------------------------------------------------------------------
1832 int PPDContext::getRenderResolution() const
1834 // initialize to reasonable default, if parser is not set
1835 int nDPI = 300;
1836 if( m_pParser )
1838 int nDPIx = 300, nDPIy = 300;
1839 const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) );
1840 if( pKey )
1842 const PPDValue* pValue = getValue( pKey );
1843 if( pValue )
1844 m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
1845 else
1846 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1848 else
1849 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1851 nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy;
1853 return nDPI;
1856 // -------------------------------------------------------------------
1858 void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const
1860 // initialize to reasonable default, if parser is not set
1861 rPaper = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) );
1862 rWidth = 595;
1863 rHeight = 842;
1864 if( m_pParser )
1866 const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
1867 if( pKey )
1869 const PPDValue* pValue = getValue( pKey );
1870 if( pValue )
1872 rPaper = pValue->m_aOption;
1873 m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
1875 else
1877 rPaper = m_pParser->getDefaultPaperDimension();
1878 m_pParser->getDefaultPaperDimension( rWidth, rHeight );