/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include #include #include #include "vcl/ppdparser.hxx" #include "vcl/strhelper.hxx" #include "vcl/helper.hxx" #include "vcl/svapp.hxx" #include "cupsmgr.hxx" #include "tools/debug.hxx" #include "tools/urlobj.hxx" #include "tools/stream.hxx" #include "tools/zcodec.hxx" #include "osl/mutex.hxx" #include "osl/file.hxx" #include "osl/process.h" #include "osl/thread.h" #include "rtl/strbuf.hxx" #include "rtl/ustrbuf.hxx" #include "com/sun/star/lang/Locale.hpp" namespace psp { class PPDTranslator { struct LocaleEqual { bool operator()(const com::sun::star::lang::Locale& i_rLeft, const com::sun::star::lang::Locale& i_rRight) const { return i_rLeft.Language.equals( i_rRight.Language ) && i_rLeft.Country.equals( i_rRight.Country ) && i_rLeft.Variant.equals( i_rRight.Variant ); } }; struct LocaleHash { size_t operator()(const com::sun::star::lang::Locale& rLocale) const { return (size_t)rLocale.Language.hashCode() ^ (size_t)rLocale.Country.hashCode() ^ (size_t)rLocale.Variant.hashCode() ; } }; typedef std::hash_map< com::sun::star::lang::Locale, rtl::OUString, LocaleHash, LocaleEqual > translation_map; typedef std::hash_map< rtl::OUString, translation_map, rtl::OUStringHash > key_translation_map; key_translation_map m_aTranslations; public: PPDTranslator() {} ~PPDTranslator() {} void insertValue( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rValue, const rtl::OUString& i_rTranslation, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ); void insertOption( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rTranslation, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) { insertValue( i_rKey, i_rOption, rtl::OUString(), i_rTranslation, i_rLocale ); } void insertKey( const rtl::OUString& i_rKey, const rtl::OUString& i_rTranslation, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) { insertValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rTranslation, i_rLocale ); } rtl::OUString translateValue( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rValue, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const; rtl::OUString translateOption( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const { return translateValue( i_rKey, i_rOption, rtl::OUString(), i_rLocale ); } rtl::OUString translateKey( const rtl::OUString& i_rKey, const com::sun::star::lang::Locale& i_rLocale = com::sun::star::lang::Locale() ) const { return translateValue( i_rKey, rtl::OUString(), rtl::OUString(), i_rLocale ); } }; static com::sun::star::lang::Locale normalizeInputLocale( const com::sun::star::lang::Locale& i_rLocale, bool bInsertDefault = false ) { com::sun::star::lang::Locale aLoc( i_rLocale ); if( bInsertDefault && aLoc.Language.getLength() == 0 ) { // empty locale requested, fill in application UI locale aLoc = Application::GetSettings().GetUILocale(); #if OSL_DEBUG_LEVEL > 1 static const char* pEnvLocale = getenv( "SAL_PPDPARSER_LOCALE" ); if( pEnvLocale && *pEnvLocale ) { rtl::OString aStr( pEnvLocale ); sal_Int32 nLen = aStr.getLength(); aLoc.Language = rtl::OStringToOUString( aStr.copy( 0, nLen > 2 ? 2 : nLen ), RTL_TEXTENCODING_MS_1252 ); if( nLen >=5 && aStr.getStr()[2] == '_' ) aLoc.Country = rtl::OStringToOUString( aStr.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252 ); else aLoc.Country = rtl::OUString(); aLoc.Variant = rtl::OUString(); } #endif } aLoc.Language = aLoc.Language.toAsciiLowerCase(); aLoc.Country = aLoc.Country.toAsciiUpperCase(); aLoc.Variant = aLoc.Variant.toAsciiUpperCase(); return aLoc; } void PPDTranslator::insertValue( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rValue, const rtl::OUString& i_rTranslation, const com::sun::star::lang::Locale& i_rLocale ) { rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 ); aKey.append( i_rKey ); if( i_rOption.getLength() || i_rValue.getLength() ) { aKey.append( sal_Unicode( ':' ) ); aKey.append( i_rOption ); } if( i_rValue.getLength() ) { aKey.append( sal_Unicode( ':' ) ); aKey.append( i_rValue ); } if( aKey.getLength() && i_rTranslation.getLength() ) { rtl::OUString aK( aKey.makeStringAndClear() ); com::sun::star::lang::Locale aLoc; aLoc.Language = i_rLocale.Language.toAsciiLowerCase(); aLoc.Country = i_rLocale.Country.toAsciiUpperCase(); aLoc.Variant = i_rLocale.Variant.toAsciiUpperCase(); m_aTranslations[ aK ][ aLoc ] = i_rTranslation; } } rtl::OUString PPDTranslator::translateValue( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rValue, const com::sun::star::lang::Locale& i_rLocale ) const { rtl::OUString aResult; rtl::OUStringBuffer aKey( i_rKey.getLength() + i_rOption.getLength() + i_rValue.getLength() + 2 ); aKey.append( i_rKey ); if( i_rOption.getLength() || i_rValue.getLength() ) { aKey.append( sal_Unicode( ':' ) ); aKey.append( i_rOption ); } if( i_rValue.getLength() ) { aKey.append( sal_Unicode( ':' ) ); aKey.append( i_rValue ); } if( aKey.getLength() ) { rtl::OUString aK( aKey.makeStringAndClear() ); key_translation_map::const_iterator it = m_aTranslations.find( aK ); if( it != m_aTranslations.end() ) { const translation_map& rMap( it->second ); com::sun::star::lang::Locale aLoc( normalizeInputLocale( i_rLocale, true ) ); for( int nTry = 0; nTry < 4; nTry++ ) { translation_map::const_iterator tr = rMap.find( aLoc ); if( tr != rMap.end() ) { aResult = tr->second; break; } switch( nTry ) { case 0: aLoc.Variant = rtl::OUString();break; case 1: aLoc.Country = rtl::OUString();break; case 2: aLoc.Language = rtl::OUString();break; } } } } return aResult; } } using namespace psp; using namespace rtl; #undef DBG_ASSERT #if defined DBG_UTIL || (OSL_DEBUG_LEVEL > 1) #define BSTRING(x) ByteString( x, osl_getThreadTextEncoding() ) #define DBG_ASSERT( x, y ) { if( ! (x) ) fprintf( stderr, (y) ); } #else #define DBG_ASSERT( x, y ) #endif std::list< PPDParser* > PPDParser::aAllParsers; std::hash_map< OUString, OUString, OUStringHash >* PPDParser::pAllPPDFiles = NULL; class PPDDecompressStream { SvFileStream* mpFileStream; SvMemoryStream* mpMemStream; rtl::OUString maFileName; // forbid copying PPDDecompressStream( const PPDDecompressStream& ); PPDDecompressStream& operator=(const PPDDecompressStream& ); public: PPDDecompressStream( const rtl::OUString& rFile ); ~PPDDecompressStream(); bool IsOpen() const; bool IsEof() const; void ReadLine( ByteString& o_rLine); void Open( const rtl::OUString& i_rFile ); void Close(); const rtl::OUString& GetFileName() const { return maFileName; } }; PPDDecompressStream::PPDDecompressStream( const rtl::OUString& i_rFile ) : mpFileStream( NULL ), mpMemStream( NULL ) { Open( i_rFile ); } PPDDecompressStream::~PPDDecompressStream() { Close(); } void PPDDecompressStream::Open( const rtl::OUString& i_rFile ) { Close(); mpFileStream = new SvFileStream( i_rFile, STREAM_READ ); maFileName = mpFileStream->GetFileName(); if( ! mpFileStream->IsOpen() ) { Close(); return; } ByteString aLine; mpFileStream->ReadLine( aLine ); mpFileStream->Seek( 0 ); // check for compress'ed or gzip'ed file sal_uLong nCompressMethod = 0; if( aLine.Len() > 1 && static_cast(aLine.GetChar( 0 )) == 0x1f ) { if( static_cast(aLine.GetChar( 1 )) == 0x8b ) // check for gzip nCompressMethod = ZCODEC_DEFAULT | ZCODEC_GZ_LIB; } if( nCompressMethod != 0 ) { // so let's try to decompress the stream mpMemStream = new SvMemoryStream( 4096, 4096 ); ZCodec aCodec; aCodec.BeginCompression( nCompressMethod ); long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream ); aCodec.EndCompression(); if( nComp < 0 ) { // decompression failed, must be an uncompressed stream after all delete mpMemStream, mpMemStream = NULL; mpFileStream->Seek( 0 ); } else { // compression successful, can get rid of file stream delete mpFileStream, mpFileStream = NULL; mpMemStream->Seek( 0 ); } } } void PPDDecompressStream::Close() { delete mpMemStream, mpMemStream = NULL; delete mpFileStream, mpFileStream = NULL; } bool PPDDecompressStream::IsOpen() const { return (mpMemStream || (mpFileStream && mpFileStream->IsOpen())); } bool PPDDecompressStream::IsEof() const { return ( mpMemStream ? mpMemStream->IsEof() : ( mpFileStream ? mpFileStream->IsEof() : true ) ); } void PPDDecompressStream::ReadLine( ByteString& o_rLine ) { if( mpMemStream ) mpMemStream->ReadLine( o_rLine ); else if( mpFileStream ) mpFileStream->ReadLine( o_rLine ); } 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 ) { osl::DirectoryItem aLinkItem; osl::FileBase::RC aRet = osl::FileBase::E_None; if( ( aRet = osl::DirectoryItem::get( i_rURL, aLinkItem ) ) == osl::FileBase::E_None ) { osl::FileStatus aStatus( FileStatusMask_FileName | FileStatusMask_Type | FileStatusMask_LinkTargetURL ); if( ( aRet = aLinkItem.getFileStatus( aStatus ) ) == osl::FileBase::E_None ) { if( aStatus.getFileType() == osl::FileStatus::Link ) { if( nLinkLevel > 0 ) aRet = resolveLink( aStatus.getLinkTargetURL(), o_rResolvedURL, o_rBaseName, o_rType, nLinkLevel-1 ); else aRet = osl::FileBase::E_MULTIHOP; } else { o_rResolvedURL = i_rURL; o_rBaseName = aStatus.getFileName(); o_rType = aStatus.getFileType(); } } } return aRet; } void PPDParser::scanPPDDir( const String& rDir ) { static struct suffix_t { const sal_Char* pSuffix; const sal_Int32 nSuffixLen; } const pSuffixes[] = { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } }; const int nSuffixes = sizeof(pSuffixes)/sizeof(pSuffixes[0]); osl::Directory aDir( rDir ); if ( aDir.open() == osl::FileBase::E_None ) { osl::DirectoryItem aItem; INetURLObject aPPDDir(rDir); while( aDir.getNextItem( aItem ) == osl::FileBase::E_None ) { osl::FileStatus aStatus( FileStatusMask_FileName ); if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) { rtl::OUStringBuffer aURLBuf( rDir.Len() + 64 ); aURLBuf.append( rDir ); aURLBuf.append( sal_Unicode( '/' ) ); aURLBuf.append( aStatus.getFileName() ); rtl::OUString aFileURL, aFileName; osl::FileStatus::Type eType = osl::FileStatus::Unknown; if( resolveLink( aURLBuf.makeStringAndClear(), aFileURL, aFileName, eType ) == osl::FileBase::E_None ) { if( eType == osl::FileStatus::Regular ) { INetURLObject aPPDFile = aPPDDir; aPPDFile.Append( aFileName ); // match extension for( int nSuffix = 0; nSuffix < nSuffixes; nSuffix++ ) { if( aFileName.getLength() > pSuffixes[nSuffix].nSuffixLen ) { if( aFileName.endsWithIgnoreAsciiCaseAsciiL( pSuffixes[nSuffix].pSuffix, pSuffixes[nSuffix].nSuffixLen ) ) { (*pAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - pSuffixes[nSuffix].nSuffixLen ) ] = aPPDFile.PathToFileName(); break; } } } } else if( eType == osl::FileStatus::Directory ) { scanPPDDir( aFileURL ); } } } } aDir.close(); } } void PPDParser::initPPDFiles() { if( pAllPPDFiles ) return; pAllPPDFiles = new std::hash_map< OUString, OUString, OUStringHash >(); // check installation directories std::list< OUString > aPathList; psp::getPrinterPathList( aPathList, PRINTER_PPDDIR ); for( std::list< OUString >::const_iterator ppd_it = aPathList.begin(); ppd_it != aPathList.end(); ++ppd_it ) { INetURLObject aPPDDir( *ppd_it, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); scanPPDDir( aPPDDir.GetMainURL( INetURLObject::NO_DECODE ) ); } if( pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ) { // last try: search in directory of executable (mainly for setup) OUString aExe; if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None ) { INetURLObject aDir( aExe ); aDir.removeSegment(); #ifdef DEBUG fprintf( stderr, "scanning last chance dir: %s\n", OUStringToOString( aDir.GetMainURL( INetURLObject::NO_DECODE ), osl_getThreadTextEncoding() ).getStr() ); #endif scanPPDDir( aDir.GetMainURL( INetURLObject::NO_DECODE ) ); #ifdef DEBUG fprintf( stderr, "SGENPRT %s\n", pAllPPDFiles->find( OUString( RTL_CONSTASCII_USTRINGPARAM( "SGENPRT" ) ) ) == pAllPPDFiles->end() ? "not found" : "found" ); #endif } } } void PPDParser::getKnownPPDDrivers( std::list< rtl::OUString >& o_rDrivers, bool bRefresh ) { if( bRefresh ) { delete pAllPPDFiles; pAllPPDFiles = NULL; } initPPDFiles(); o_rDrivers.clear(); std::hash_map< OUString, OUString, OUStringHash >::const_iterator it; for( it = pAllPPDFiles->begin(); it != pAllPPDFiles->end(); ++it ) o_rDrivers.push_back( it->first ); } String PPDParser::getPPDFile( const String& rFile ) { INetURLObject aPPD( rFile, INET_PROT_FILE, INetURLObject::ENCODE_ALL ); // someone might enter a full qualified name here PPDDecompressStream aStream( aPPD.PathToFileName() ); if( ! aStream.IsOpen() ) { std::hash_map< OUString, OUString, OUStringHash >::const_iterator it; bool bRetry = true; do { initPPDFiles(); // some PPD files contain dots beside the extension, so try name first // and cut of points after that rtl::OUString aBase( rFile ); sal_Int32 nLastIndex = aBase.lastIndexOf( sal_Unicode( '/' ) ); if( nLastIndex >= 0 ) aBase = aBase.copy( nLastIndex+1 ); do { it = pAllPPDFiles->find( aBase ); nLastIndex = aBase.lastIndexOf( sal_Unicode( '.' ) ); if( nLastIndex > 0 ) aBase = aBase.copy( 0, nLastIndex ); } while( it == pAllPPDFiles->end() && nLastIndex > 0 ); if( it == pAllPPDFiles->end() && bRetry ) { // a new file ? rehash delete pAllPPDFiles; pAllPPDFiles = NULL; bRetry = false; // note this is optimized for office start where // no new files occur and initPPDFiles is called only once } } while( ! pAllPPDFiles ); if( it != pAllPPDFiles->end() ) aStream.Open( it->second ); } String aRet; if( aStream.IsOpen() ) { ByteString aLine; aStream.ReadLine( aLine ); if( aLine.Search( "*PPD-Adobe" ) == 0 ) aRet = aStream.GetFileName(); else { // our *Include hack does usually not begin // with *PPD-Adobe, so try some lines for *Include int nLines = 10; while( aLine.Search( "*Include" ) != 0 && --nLines ) aStream.ReadLine( aLine ); if( nLines ) aRet = aStream.GetFileName(); } } return aRet; } String PPDParser::getPPDPrinterName( const String& rFile ) { String aPath = getPPDFile( rFile ); String aName; // read in the file PPDDecompressStream aStream( aPath ); if( aStream.IsOpen() ) { String aCurLine; while( ! aStream.IsEof() && aStream.IsOpen() ) { ByteString aByteLine; aStream.ReadLine( aByteLine ); aCurLine = String( aByteLine, RTL_TEXTENCODING_MS_1252 ); if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) { aCurLine.Erase( 0, 9 ); aCurLine.EraseLeadingChars( ' ' ); aCurLine.EraseTrailingChars( ' ' ); aCurLine.EraseLeadingChars( '\t' ); aCurLine.EraseTrailingChars( '\t' ); aCurLine.EraseTrailingChars( '\r' ); aCurLine.EraseTrailingChars( '\n' ); aCurLine.EraseLeadingChars( '"' ); aCurLine.EraseTrailingChars( '"' ); aStream.Close(); aStream.Open( getPPDFile( aCurLine ) ); continue; } if( aCurLine.CompareToAscii( "*ModelName:", 11 ) == COMPARE_EQUAL ) { aName = aCurLine.GetToken( 1, '"' ); break; } else if( aCurLine.CompareToAscii( "*NickName:", 10 ) == COMPARE_EQUAL ) aName = aCurLine.GetToken( 1, '"' ); } } return aName; } const PPDParser* PPDParser::getParser( const String& rFile ) { static ::osl::Mutex aMutex; ::osl::Guard< ::osl::Mutex > aGuard( aMutex ); String aFile = rFile; if( rFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) aFile = getPPDFile( rFile ); if( ! aFile.Len() ) { #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "Could not get printer PPD file \"%s\" !\n", OUStringToOString( rFile, osl_getThreadTextEncoding() ).getStr() ); #endif return NULL; } for( ::std::list< PPDParser* >::const_iterator it = aAllParsers.begin(); it != aAllParsers.end(); ++it ) if( (*it)->m_aFile == aFile ) return *it; PPDParser* pNewParser = NULL; if( aFile.CompareToAscii( "CUPS:", 5 ) != COMPARE_EQUAL ) pNewParser = new PPDParser( aFile ); else { PrinterInfoManager& rMgr = PrinterInfoManager::get(); if( rMgr.getType() == PrinterInfoManager::CUPS ) { pNewParser = const_cast(static_cast(rMgr).createCUPSParser( aFile )); } } if( pNewParser ) { // this may actually be the SGENPRT parser, // so ensure uniquness here aAllParsers.remove( pNewParser ); // insert new parser to list aAllParsers.push_front( pNewParser ); } return pNewParser; } void PPDParser::freeAll() { while( aAllParsers.begin() != aAllParsers.end() ) { delete aAllParsers.front(); aAllParsers.pop_front(); } delete pAllPPDFiles; pAllPPDFiles = NULL; } PPDParser::PPDParser( const String& rFile ) : m_aFile( rFile ), m_bType42Capable( false ), m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ), m_pDefaultImageableArea( NULL ), m_pImageableAreas( NULL ), m_pDefaultPaperDimension( NULL ), m_pPaperDimensions( NULL ), m_pDefaultInputSlot( NULL ), m_pInputSlots( NULL ), m_pDefaultResolution( NULL ), m_pResolutions( NULL ), m_pDefaultDuplexType( NULL ), m_pDuplexTypes( NULL ), m_pFontList( NULL ), m_pTranslator( new PPDTranslator() ) { // read in the file std::list< ByteString > aLines; PPDDecompressStream aStream( m_aFile ); bool bLanguageEncoding = false; if( aStream.IsOpen() ) { ByteString aCurLine; while( ! aStream.IsEof() ) { aStream.ReadLine( aCurLine ); if( aCurLine.GetChar( 0 ) == '*' ) { if( aCurLine.CompareIgnoreCaseToAscii( "*include:", 9 ) == COMPARE_EQUAL ) { aCurLine.Erase( 0, 9 ); aCurLine.EraseLeadingChars( ' ' ); aCurLine.EraseTrailingChars( ' ' ); aCurLine.EraseLeadingChars( '\t' ); aCurLine.EraseTrailingChars( '\t' ); aCurLine.EraseTrailingChars( '\r' ); aCurLine.EraseTrailingChars( '\n' ); aCurLine.EraseLeadingChars( '"' ); aCurLine.EraseTrailingChars( '"' ); aStream.Close(); aStream.Open( getPPDFile( String( aCurLine, m_aFileEncoding ) ) ); continue; } else if( ! bLanguageEncoding && aCurLine.CompareIgnoreCaseToAscii( "*languageencoding", 17 ) == COMPARE_EQUAL ) { bLanguageEncoding = true; // generally only the first one counts ByteString aLower = aCurLine; aLower.ToLowerAscii(); if( aLower.Search( "isolatin1", 17 ) != STRING_NOTFOUND || aLower.Search( "windowsansi", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_MS_1252; else if( aLower.Search( "isolatin2", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2; else if( aLower.Search( "isolatin5", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5; else if( aLower.Search( "jis83-rksj", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS; else if( aLower.Search( "macstandard", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN; else if( aLower.Search( "utf-8", 17 ) != STRING_NOTFOUND ) m_aFileEncoding = RTL_TEXTENCODING_UTF8; } } aLines.push_back( aCurLine ); } } aStream.Close(); // now get the Values parse( aLines ); #if OSL_DEBUG_LEVEL > 2 fprintf( stderr, "acquired %d Keys from PPD %s:\n", m_aKeys.size(), BSTRING( m_aFile ).GetBuffer() ); for( PPDParser::hash_type::const_iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) { const PPDKey* pKey = it->second; char* pSetupType = ""; switch( pKey->m_eSetupType ) { case PPDKey::ExitServer: pSetupType = "ExitServer";break; case PPDKey::Prolog: pSetupType = "Prolog";break; case PPDKey::DocumentSetup: pSetupType = "DocumentSetup";break; case PPDKey::PageSetup: pSetupType = "PageSetup";break; case PPDKey::JCLSetup: pSetupType = "JCLSetup";break; case PPDKey::AnySetup: pSetupType = "AnySetup";break; default: break; }; fprintf( stderr, "\t\"%s\" (\"%s\") (%d values) OrderDependency: %d %s\n", BSTRING( pKey->getKey() ).GetBuffer(), BSTRING( pKey->m_aUITranslation ).GetBuffer(), pKey->countValues(), pKey->m_nOrderDependency, pSetupType ); for( int j = 0; j < pKey->countValues(); j++ ) { fprintf( stderr, "\t\t" ); const PPDValue* pValue = pKey->getValue( j ); if( pValue == pKey->m_pDefaultValue ) fprintf( stderr, "(Default:) " ); char* pVType = ""; switch( pValue->m_eType ) { case eInvocation: pVType = "invocation";break; case eQuoted: pVType = "quoted";break; case eString: pVType = "string";break; case eSymbol: pVType = "symbol";break; case eNo: pVType = "no";break; default: break; }; fprintf( stderr, "option: \"%s\" (\"%s\"), value: type %s \"%s\" (\"%s\")\n", BSTRING( pValue->m_aOption ).GetBuffer(), BSTRING( pValue->m_aOptionTranslation ).GetBuffer(), pVType, BSTRING( pValue->m_aValue ).GetBuffer(), BSTRING( pValue->m_aValueTranslation ).GetBuffer() ); } } fprintf( stderr, "constraints: (%d found)\n", m_aConstraints.size() ); for( std::list< PPDConstraint >::const_iterator cit = m_aConstraints.begin(); cit != m_aConstraints.end(); ++cit ) { fprintf( stderr, "*\"%s\" \"%s\" *\"%s\" \"%s\"\n", BSTRING( cit->m_pKey1->getKey() ).GetBuffer(), cit->m_pOption1 ? BSTRING( cit->m_pOption1->m_aOption ).GetBuffer() : "", BSTRING( cit->m_pKey2->getKey() ).GetBuffer(), cit->m_pOption2 ? BSTRING( cit->m_pOption2->m_aOption ).GetBuffer() : "" ); } #endif // fill in shortcuts const PPDKey* pKey; m_pImageableAreas = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ImageableArea" ) ) ); if( m_pImageableAreas ) m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue(); DBG_ASSERT( m_pImageableAreas, "Warning: no ImageableArea in PPD\n" ); DBG_ASSERT( m_pDefaultImageableArea, "Warning: no DefaultImageableArea in PPD\n" ); m_pPaperDimensions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PaperDimension" ) ) ); if( m_pPaperDimensions ) m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue(); DBG_ASSERT( m_pPaperDimensions, "Warning: no PaperDimension in PPD\n" ); DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultPaperDimension in PPD\n" ); m_pResolutions = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); if( m_pResolutions ) m_pDefaultResolution = m_pResolutions->getDefaultValue(); DBG_ASSERT( m_pResolutions, "Warning: no Resolution in PPD\n" ); DBG_ASSERT( m_pDefaultResolution, "Warning: no DefaultResolution in PPD\n" ); m_pInputSlots = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ); if( m_pInputSlots ) m_pDefaultInputSlot = m_pInputSlots->getDefaultValue(); DBG_ASSERT( m_pPaperDimensions, "Warning: no InputSlot in PPD\n" ); DBG_ASSERT( m_pDefaultPaperDimension, "Warning: no DefaultInputSlot in PPD\n" ); m_pDuplexTypes = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) ); if( m_pDuplexTypes ) m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue(); m_pFontList = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Font" ) ) ); DBG_ASSERT( m_pFontList, "Warning: no Font in PPD\n" ); // fill in direct values if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ModelName" ) ) )) ) m_aPrinterName = pKey->getValue( 0 )->m_aValue; if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "NickName" ) ) )) ) m_aNickName = pKey->getValue( 0 )->m_aValue; if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "ColorDevice" ) ) )) ) m_bColorDevice = pKey->getValue( 0 )->m_aValue.CompareIgnoreCaseToAscii( "true", 4 ) == COMPARE_EQUAL ? true : false; if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "LanguageLevel" ) ) )) ) m_nLanguageLevel = pKey->getValue( 0 )->m_aValue.ToInt32(); if( (pKey = getKey( String( RTL_CONSTASCII_USTRINGPARAM( "TTRasterizer" ) ) )) ) m_bType42Capable = pKey->getValue( 0 )->m_aValue.EqualsIgnoreCaseAscii( "Type42" ) ? true : false; } PPDParser::~PPDParser() { for( PPDParser::hash_type::iterator it = m_aKeys.begin(); it != m_aKeys.end(); ++it ) delete it->second; delete m_pTranslator; } void PPDParser::insertKey( const String& rKey, PPDKey* pKey ) { m_aKeys[ rKey ] = pKey; m_aOrderedKeys.push_back( pKey ); } const PPDKey* PPDParser::getKey( int n ) const { return ((unsigned int)n < m_aOrderedKeys.size() && n >= 0) ? m_aOrderedKeys[n] : NULL; } const PPDKey* PPDParser::getKey( const String& rKey ) const { PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey ); return it != m_aKeys.end() ? it->second : NULL; } bool PPDParser::hasKey( const PPDKey* pKey ) const { return pKey ? ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() ? true : false ) : false; } static sal_uInt8 getNibble( sal_Char cChar ) { sal_uInt8 nRet = 0; if( cChar >= '0' && cChar <= '9' ) nRet = sal_uInt8( cChar - '0' ); else if( cChar >= 'A' && cChar <= 'F' ) nRet = 10 + sal_uInt8( cChar - 'A' ); else if( cChar >= 'a' && cChar <= 'f' ) nRet = 10 + sal_uInt8( cChar - 'a' ); return nRet; } String PPDParser::handleTranslation( const ByteString& i_rString, bool bIsGlobalized ) { int nOrigLen = i_rString.Len(); OStringBuffer aTrans( nOrigLen ); const sal_Char* pStr = i_rString.GetBuffer(); const sal_Char* pEnd = pStr + nOrigLen; while( pStr < pEnd ) { if( *pStr == '<' ) { pStr++; sal_Char cChar; while( *pStr != '>' && pStr < pEnd-1 ) { cChar = getNibble( *pStr++ ) << 4; cChar |= getNibble( *pStr++ ); aTrans.append( cChar ); } pStr++; } else aTrans.append( *pStr++ ); } return OStringToOUString( aTrans.makeStringAndClear(), bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding ); } void PPDParser::parse( ::std::list< ByteString >& rLines ) { std::list< ByteString >::iterator line = rLines.begin(); PPDParser::hash_type::const_iterator keyit; while( line != rLines.end() ) { ByteString aCurrentLine( *line ); ++line; if( aCurrentLine.GetChar(0) != '*' ) continue; if( aCurrentLine.GetChar(1) == '%' ) continue; ByteString aKey = GetCommandLineToken( 0, aCurrentLine.GetToken( 0, ':' ) ); int nPos = aKey.Search( '/' ); if( nPos != STRING_NOTFOUND ) aKey.Erase( nPos ); aKey.Erase( 0, 1 ); // remove the '*' if( aKey.Equals( "CloseUI" ) || aKey.Equals( "OpenGroup" ) || aKey.Equals( "CloseGroup" ) || aKey.Equals( "End" ) || aKey.Equals( "OpenSubGroup" ) || aKey.Equals( "CloseSubGroup" ) ) continue; if( aKey.Equals( "OpenUI" ) ) { parseOpenUI( aCurrentLine ); continue; } else if( aKey.Equals( "OrderDependency" ) ) { parseOrderDependency( aCurrentLine ); continue; } else if( aKey.Equals( "UIConstraints" ) || aKey.Equals( "NonUIConstraints" ) ) continue; // parsed in pass 2 else if( aKey.Equals( "CustomPageSize" ) ) // currently not handled continue; // default values are parsed in pass 2 if( aKey.CompareTo( "Default", 7 ) == COMPARE_EQUAL ) continue; bool bQuery = false; if( aKey.GetChar( 0 ) == '?' ) { aKey.Erase( 0, 1 ); bQuery = true; } String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); // handle CUPS extension for globalized PPDs bool bIsGlobalizedLine = false; com::sun::star::lang::Locale aTransLocale; if( ( aUniKey.Len() > 3 && aUniKey.GetChar( 2 ) == '.' ) || ( aUniKey.Len() > 5 && aUniKey.GetChar( 2 ) == '_' && aUniKey.GetChar( 5 ) == '.' ) ) { if( aUniKey.GetChar( 2 ) == '.' ) { aTransLocale.Language = aUniKey.Copy( 0, 2 ); aUniKey = aUniKey.Copy( 3 ); } else { aTransLocale.Language = aUniKey.Copy( 0, 2 ); aTransLocale.Country = aUniKey.Copy( 3, 2 ); aUniKey = aUniKey.Copy( 6 ); } bIsGlobalizedLine = true; } String aOption; nPos = aCurrentLine.Search( ':' ); if( nPos != STRING_NOTFOUND ) { aOption = String( aCurrentLine.Copy( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 ); aOption = GetCommandLineToken( 1, aOption ); int nTransPos = aOption.Search( '/' ); if( nTransPos != STRING_NOTFOUND ) aOption.Erase( nTransPos ); } PPDValueType eType = eNo; String aValue; rtl::OUString aOptionTranslation; rtl::OUString aValueTranslation; if( nPos != STRING_NOTFOUND ) { // found a colon, there may be an option ByteString aLine = aCurrentLine.Copy( 1, nPos-1 ); aLine = WhitespaceToSpace( aLine ); int nTransPos = aLine.Search( '/' ); if( nTransPos != STRING_NOTFOUND ) aOptionTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine ); // read in more lines if necessary for multiline values aLine = aCurrentLine.Copy( nPos+1 ); if( aLine.Len() ) { while( ! ( aLine.GetTokenCount( '"' ) & 1 ) && line != rLines.end() ) // while there is an even number of tokens; that means // an odd number of doubleqoutes { // copy the newlines also aLine += '\n'; aLine += *line; ++line; } } aLine = WhitespaceToSpace( aLine ); // #i100644# handle a missing value (actually a broken PPD) if( ! aLine.Len() ) { if( aOption.Len() && aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL ) eType = eInvocation; else eType = eQuoted; } // check for invocation or quoted value else if( aLine.GetChar(0) == '"' ) { aLine.Erase( 0, 1 ); nTransPos = aLine.Search( '"' ); aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); // after the second doublequote can follow a / and a translation aValueTranslation = handleTranslation( aLine.Copy( nTransPos+2 ), bIsGlobalizedLine ); // check for quoted value if( aOption.Len() && aUniKey.CompareToAscii( "JCL", 3 ) != COMPARE_EQUAL ) eType = eInvocation; else eType = eQuoted; } // check for symbol value else if( aLine.GetChar(0) == '^' ) { aLine.Erase( 0, 1 ); aValue = String( aLine, RTL_TEXTENCODING_MS_1252 ); eType = eSymbol; } else { // must be a string value then // strictly this is false because string values // can contain any whitespace which is reduced // to one space by now // who cares ... nTransPos = aLine.Search( '/' ); if( nTransPos == STRING_NOTFOUND ) nTransPos = aLine.Len(); aValue = String( aLine.Copy( 0, nTransPos ), RTL_TEXTENCODING_MS_1252 ); aValueTranslation = handleTranslation( aLine.Copy( nTransPos+1 ), bIsGlobalizedLine ); eType = eString; } } // handle globalized PPD entries if( bIsGlobalizedLine ) { // handle main key translations of form: // *ll_CC.Translation MainKeyword/translated text: "" if( aUniKey.EqualsAscii( "Translation" ) ) { m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale ); } // handle options translations of for: // *ll_CC.MainKeyword OptionKeyword/translated text: "" else { m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale ); } continue; } PPDKey* pKey = NULL; keyit = m_aKeys.find( aUniKey ); if( keyit == m_aKeys.end() ) { pKey = new PPDKey( aUniKey ); insertKey( aUniKey, pKey ); } else pKey = keyit->second; if( eType == eNo && bQuery ) continue; PPDValue* pValue = pKey->insertValue( aOption ); if( ! pValue ) continue; pValue->m_eType = eType; pValue->m_aValue = aValue; if( aOptionTranslation.getLength() ) m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale ); if( aValueTranslation.getLength() ) m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale ); // eventually update query and remove from option list if( bQuery && pKey->m_bQueryValue == sal_False ) { pKey->m_aQueryValue = *pValue; pKey->m_bQueryValue = true; pKey->eraseValue( pValue->m_aOption ); } } // second pass: fill in defaults for( line = rLines.begin(); line != rLines.end(); ++line ) { ByteString aLine( *line ); if( aLine.CompareTo( "*Default", 8 ) == COMPARE_EQUAL ) { String aKey( aLine.Copy( 8 ), RTL_TEXTENCODING_MS_1252 ); sal_uInt16 nPos = aKey.Search( ':' ); if( nPos != STRING_NOTFOUND ) { aKey.Erase( nPos ); String aOption( WhitespaceToSpace( aLine.Copy( nPos+9 ) ), RTL_TEXTENCODING_MS_1252 ); keyit = m_aKeys.find( aKey ); if( keyit != m_aKeys.end() ) { PPDKey* pKey = keyit->second; const PPDValue* pDefValue = pKey->getValue( aOption ); if( pKey->m_pDefaultValue == NULL ) pKey->m_pDefaultValue = pDefValue; } else { // some PPDs contain defaults for keys that // do not exist otherwise // (example: DefaultResolution) // so invent that key here and have a default value PPDKey* pKey = new PPDKey( aKey ); PPDValue* pNewValue = pKey->insertValue( aOption ); pNewValue->m_eType = eInvocation; // or what ? insertKey( aKey, pKey ); } } } else if( aLine.CompareTo( "*UIConstraints", 14 ) == COMPARE_EQUAL || aLine.CompareTo( "*NonUIConstraints", 17 ) == COMPARE_EQUAL ) parseConstraint( aLine ); } } void PPDParser::parseOpenUI( const ByteString& rLine ) { String aTranslation; ByteString aKey = rLine; int nPos = aKey.Search( ':' ); if( nPos != STRING_NOTFOUND ) aKey.Erase( nPos ); nPos = aKey.Search( '/' ); if( nPos != STRING_NOTFOUND ) { aTranslation = handleTranslation( aKey.Copy( nPos + 1 ), false ); aKey.Erase( nPos ); } aKey = GetCommandLineToken( 1, aKey ); aKey.Erase( 0, 1 ); String aUniKey( aKey, RTL_TEXTENCODING_MS_1252 ); PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey ); PPDKey* pKey; if( keyit == m_aKeys.end() ) { pKey = new PPDKey( aUniKey ); insertKey( aUniKey, pKey ); } else pKey = keyit->second; pKey->m_bUIOption = true; m_pTranslator->insertKey( pKey->getKey(), aTranslation ); ByteString aValue = WhitespaceToSpace( rLine.GetToken( 1, ':' ) ); if( aValue.CompareIgnoreCaseToAscii( "boolean" ) == COMPARE_EQUAL ) pKey->m_eUIType = PPDKey::Boolean; else if( aValue.CompareIgnoreCaseToAscii( "pickmany" ) == COMPARE_EQUAL ) pKey->m_eUIType = PPDKey::PickMany; else pKey->m_eUIType = PPDKey::PickOne; } void PPDParser::parseOrderDependency( const ByteString& rLine ) { ByteString aLine( rLine ); int nPos = aLine.Search( ':' ); if( nPos != STRING_NOTFOUND ) aLine.Erase( 0, nPos+1 ); int nOrder = GetCommandLineToken( 0, aLine ).ToInt32(); ByteString aSetup = GetCommandLineToken( 1, aLine ); String aKey( GetCommandLineToken( 2, aLine ), RTL_TEXTENCODING_MS_1252 ); if( aKey.GetChar( 0 ) != '*' ) return; // invalid order depency aKey.Erase( 0, 1 ); PPDKey* pKey; PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey ); if( keyit == m_aKeys.end() ) { pKey = new PPDKey( aKey ); insertKey( aKey, pKey ); } else pKey = keyit->second; pKey->m_nOrderDependency = nOrder; if( aSetup.Equals( "ExitServer" ) ) pKey->m_eSetupType = PPDKey::ExitServer; else if( aSetup.Equals( "Prolog" ) ) pKey->m_eSetupType = PPDKey::Prolog; else if( aSetup.Equals( "DocumentSetup" ) ) pKey->m_eSetupType = PPDKey::DocumentSetup; else if( aSetup.Equals( "PageSetup" ) ) pKey->m_eSetupType = PPDKey::PageSetup; else if( aSetup.Equals( "JCLSetup" ) ) pKey->m_eSetupType = PPDKey::JCLSetup; else pKey->m_eSetupType = PPDKey::AnySetup; } void PPDParser::parseConstraint( const ByteString& rLine ) { bool bFailed = false; String aLine( rLine, RTL_TEXTENCODING_MS_1252 ); aLine.Erase( 0, rLine.Search( ':' )+1 ); PPDConstraint aConstraint; int nTokens = GetCommandLineTokenCount( aLine ); for( int i = 0; i < nTokens; i++ ) { String aToken = GetCommandLineToken( i, aLine ); if( aToken.GetChar( 0 ) == '*' ) { aToken.Erase( 0, 1 ); if( aConstraint.m_pKey1 ) aConstraint.m_pKey2 = getKey( aToken ); else aConstraint.m_pKey1 = getKey( aToken ); } else { if( aConstraint.m_pKey2 ) { if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) ) bFailed = true; } else if( aConstraint.m_pKey1 ) { if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) ) bFailed = true; } else // constraint for nonexistent keys; this happens // e.g. in HP4PLUS3 (#75636#) bFailed = true; } } // there must be two keywords if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed ) { #ifdef __DEBUG fprintf( stderr, "Warning: constraint \"%s\" is invalid\n", rLine.GetStr() ); #endif } else m_aConstraints.push_back( aConstraint ); } String PPDParser::getDefaultPaperDimension() const { if( m_pDefaultPaperDimension ) return m_pDefaultPaperDimension->m_aOption; return String(); } bool PPDParser::getMargins( const String& rPaperName, int& rLeft, int& rRight, int& rUpper, int& rLower ) const { if( ! m_pImageableAreas || ! m_pPaperDimensions ) return false; int nPDim=-1, nImArea=-1, i; for( i = 0; i < m_pImageableAreas->countValues(); i++ ) if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption ) nImArea = i; for( i = 0; i < m_pPaperDimensions->countValues(); i++ ) if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) nPDim = i; if( nPDim == -1 || nImArea == -1 ) return false; double ImLLx, ImLLy, ImURx, ImURy; double PDWidth, PDHeight; String aArea = m_pImageableAreas->getValue( nImArea )->m_aValue; ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) ); ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) ); ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) ); ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) ); // sscanf( m_pImageableAreas->getValue( nImArea )->m_aValue.GetStr(), // "%lg%lg%lg%lg", &ImLLx, &ImLLy, &ImURx, &ImURy ); aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); // sscanf( m_pPaperDimensions->getValue( nPDim )->m_aValue.GetStr(), // "%lg%lg", &PDWidth, &PDHeight ); rLeft = (int)(ImLLx + 0.5); rLower = (int)(ImLLy + 0.5); rUpper = (int)(PDHeight - ImURy + 0.5); rRight = (int)(PDWidth - ImURx + 0.5); return true; } bool PPDParser::getPaperDimension( const String& rPaperName, int& rWidth, int& rHeight ) const { if( ! m_pPaperDimensions ) return false; int nPDim=-1; for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption ) nPDim = i; if( nPDim == -1 ) return false; double PDWidth, PDHeight; String aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue; PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); rHeight = (int)(PDHeight + 0.5); rWidth = (int)(PDWidth + 0.5); return true; } String PPDParser::matchPaper( int nWidth, int nHeight ) const { if( ! m_pPaperDimensions ) return String(); int nPDim = -1; double PDWidth, PDHeight; double fSort = 2e36, fNewSort; for( int i = 0; i < m_pPaperDimensions->countValues(); i++ ) { String aArea = m_pPaperDimensions->getValue( i )->m_aValue; PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) ); PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) ); PDWidth /= (double)nWidth; PDHeight /= (double)nHeight; if( PDWidth >= 0.9 && PDWidth <= 1.1 && PDHeight >= 0.9 && PDHeight <= 1.1 ) { fNewSort = (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight); if( fNewSort == 0.0 ) // perfect match return m_pPaperDimensions->getValue( i )->m_aOption; if( fNewSort < fSort ) { fSort = fNewSort; nPDim = i; } } } static bool bDontSwap = false; if( nPDim == -1 && ! bDontSwap ) { // swap portrait/landscape and try again bDontSwap = true; String rRet = matchPaper( nHeight, nWidth ); bDontSwap = false; return rRet; } return nPDim != -1 ? m_pPaperDimensions->getValue( nPDim )->m_aOption : String(); } String PPDParser::getDefaultInputSlot() const { if( m_pDefaultInputSlot ) return m_pDefaultInputSlot->m_aValue; return String(); } String PPDParser::getSlot( int nSlot ) const { if( ! m_pInputSlots ) return String(); if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) return m_pInputSlots->getValue( nSlot )->m_aOption; else if( m_pInputSlots->countValues() > 0 ) return m_pInputSlots->getValue( (sal_uLong)0 )->m_aOption; return String(); } String PPDParser::getSlotCommand( int nSlot ) const { if( ! m_pInputSlots ) return String(); if( nSlot > 0 && nSlot < m_pInputSlots->countValues() ) return m_pInputSlots->getValue( nSlot )->m_aValue; else if( m_pInputSlots->countValues() > 0 ) return m_pInputSlots->getValue( (sal_uLong)0 )->m_aValue; return String(); } String PPDParser::getSlotCommand( const String& rSlot ) const { if( ! m_pInputSlots ) return String(); for( int i=0; i < m_pInputSlots->countValues(); i++ ) { const PPDValue* pValue = m_pInputSlots->getValue( i ); if( pValue->m_aOption == rSlot ) return pValue->m_aValue; } return String(); } String PPDParser::getPaperDimension( int nPaperDimension ) const { if( ! m_pPaperDimensions ) return String(); if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) return m_pPaperDimensions->getValue( nPaperDimension )->m_aOption; else if( m_pPaperDimensions->countValues() > 0 ) return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aOption; return String(); } String PPDParser::getPaperDimensionCommand( int nPaperDimension ) const { if( ! m_pPaperDimensions ) return String(); if( nPaperDimension > 0 && nPaperDimension < m_pPaperDimensions->countValues() ) return m_pPaperDimensions->getValue( nPaperDimension )->m_aValue; else if( m_pPaperDimensions->countValues() > 0 ) return m_pPaperDimensions->getValue( (sal_uLong)0 )->m_aValue; return String(); } String PPDParser::getPaperDimensionCommand( const String& rPaperDimension ) const { if( ! m_pPaperDimensions ) return String(); for( int i=0; i < m_pPaperDimensions->countValues(); i++ ) { const PPDValue* pValue = m_pPaperDimensions->getValue( i ); if( pValue->m_aOption == rPaperDimension ) return pValue->m_aValue; } return String(); } void PPDParser::getResolutionFromString( const String& rString, int& rXRes, int& rYRes ) const { int nPos = 0, nDPIPos; rXRes = rYRes = 300; nDPIPos = rString.SearchAscii( "dpi" ); if( nDPIPos != STRING_NOTFOUND ) { if( ( nPos = rString.Search( 'x' ) ) != STRING_NOTFOUND ) { rXRes = rString.Copy( 0, nPos ).ToInt32(); rYRes = rString.GetToken( 1, 'x' ).Erase( nDPIPos - nPos - 1 ).ToInt32(); } else rXRes = rYRes = rString.Copy( 0, nDPIPos ).ToInt32(); } } void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const { if( m_pDefaultResolution ) { getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes ); return; } rXRes = 300; rYRes = 300; } int PPDParser::getResolutions() const { if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution ) return 1; return m_pResolutions ? m_pResolutions->countValues() : 0; } void PPDParser::getResolution( int nNr, int& rXRes, int& rYRes ) const { if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution && nNr == 0 ) { getDefaultResolution( rXRes, rYRes ); return; } if( ! m_pResolutions ) return; getResolutionFromString( m_pResolutions->getValue( nNr )->m_aOption, rXRes, rYRes ); } String PPDParser::getResolutionCommand( int nXRes, int nYRes ) const { if( ( ! m_pResolutions || m_pResolutions->countValues() == 0 ) && m_pDefaultResolution ) return m_pDefaultResolution->m_aValue; if( ! m_pResolutions ) return String(); int nX, nY; for( int i = 0; i < m_pResolutions->countValues(); i++ ) { getResolutionFromString( m_pResolutions->getValue( i )->m_aOption, nX, nY ); if( nX == nXRes && nY == nYRes ) return m_pResolutions->getValue( i )->m_aValue; } return String(); } String PPDParser::getDefaultDuplexType() const { if( m_pDefaultDuplexType ) return m_pDefaultDuplexType->m_aValue; return String(); } String PPDParser::getDuplex( int nDuplex ) const { if( ! m_pDuplexTypes ) return String(); if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) return m_pDuplexTypes->getValue( nDuplex )->m_aOption; else if( m_pDuplexTypes->countValues() > 0 ) return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aOption; return String(); } String PPDParser::getDuplexCommand( int nDuplex ) const { if( ! m_pDuplexTypes ) return String(); if( nDuplex > 0 && nDuplex < m_pDuplexTypes->countValues() ) return m_pDuplexTypes->getValue( nDuplex )->m_aValue; else if( m_pDuplexTypes->countValues() > 0 ) return m_pDuplexTypes->getValue( (sal_uLong)0 )->m_aValue; return String(); } String PPDParser::getDuplexCommand( const String& rDuplex ) const { if( ! m_pDuplexTypes ) return String(); for( int i=0; i < m_pDuplexTypes->countValues(); i++ ) { const PPDValue* pValue = m_pDuplexTypes->getValue( i ); if( pValue->m_aOption == rDuplex ) return pValue->m_aValue; } return String(); } void PPDParser::getFontAttributes( int nFont, String& rEncoding, String& rCharset ) const { if( m_pFontList && nFont >= 0 && nFont < m_pFontList->countValues() ) { String aAttribs = WhitespaceToSpace( m_pFontList->getValue( nFont )->m_aValue ); rEncoding = GetCommandLineToken( 0, aAttribs ); rCharset = GetCommandLineToken( 2, aAttribs ); } } void PPDParser::getFontAttributes( const String& rFont, String& rEncoding, String& rCharset ) const { if( m_pFontList ) { for( int i = 0; i < m_pFontList->countValues(); i++ ) if( m_pFontList->getValue( i )->m_aOption == rFont ) getFontAttributes( i, rEncoding, rCharset ); } } String PPDParser::getFont( int nFont ) const { if( ! m_pFontList ) return String(); if( nFont >=0 && nFont < m_pFontList->countValues() ) return m_pFontList->getValue( nFont )->m_aOption; return String(); } rtl::OUString PPDParser::translateKey( const rtl::OUString& i_rKey, const com::sun::star::lang::Locale& i_rLocale ) const { rtl::OUString aResult( m_pTranslator->translateKey( i_rKey, i_rLocale ) ); if( aResult.getLength() == 0 ) aResult = i_rKey; return aResult; } rtl::OUString PPDParser::translateOption( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const com::sun::star::lang::Locale& i_rLocale ) const { rtl::OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption, i_rLocale ) ); if( aResult.getLength() == 0 ) aResult = i_rOption; return aResult; } rtl::OUString PPDParser::translateValue( const rtl::OUString& i_rKey, const rtl::OUString& i_rOption, const rtl::OUString& i_rValue, const com::sun::star::lang::Locale& i_rLocale ) const { rtl::OUString aResult( m_pTranslator->translateValue( i_rKey, i_rOption, i_rValue, i_rLocale ) ); if( aResult.getLength() == 0 ) aResult = i_rValue; return aResult; } /* * PPDKey */ PPDKey::PPDKey( const String& rKey ) : m_aKey( rKey ), m_pDefaultValue( NULL ), m_bQueryValue( false ), m_bUIOption( false ), m_eUIType( PickOne ), m_nOrderDependency( 100 ), m_eSetupType( AnySetup ) { } // ------------------------------------------------------------------- PPDKey::~PPDKey() { } // ------------------------------------------------------------------- const PPDValue* PPDKey::getValue( int n ) const { return ((unsigned int)n < m_aOrderedValues.size() && n >= 0) ? m_aOrderedValues[n] : NULL; } // ------------------------------------------------------------------- const PPDValue* PPDKey::getValue( const String& rOption ) const { PPDKey::hash_type::const_iterator it = m_aValues.find( rOption ); return it != m_aValues.end() ? &it->second : NULL; } // ------------------------------------------------------------------- const PPDValue* PPDKey::getValueCaseInsensitive( const String& rOption ) const { const PPDValue* pValue = getValue( rOption ); if( ! pValue ) { for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ ) if( m_aOrderedValues[n]->m_aOption.EqualsIgnoreCaseAscii( rOption ) ) pValue = m_aOrderedValues[n]; } return pValue; } // ------------------------------------------------------------------- void PPDKey::eraseValue( const String& rOption ) { PPDKey::hash_type::iterator it = m_aValues.find( rOption ); if( it == m_aValues.end() ) return; for( PPDKey::value_type::iterator vit = m_aOrderedValues.begin(); vit != m_aOrderedValues.end(); ++vit ) { if( *vit == &(it->second ) ) { m_aOrderedValues.erase( vit ); break; } } m_aValues.erase( it ); } // ------------------------------------------------------------------- PPDValue* PPDKey::insertValue( const String& rOption ) { if( m_aValues.find( rOption ) != m_aValues.end() ) return NULL; PPDValue aValue; aValue.m_aOption = rOption; m_aValues[ rOption ] = aValue; PPDValue* pValue = &m_aValues[rOption]; m_aOrderedValues.push_back( pValue ); return pValue; } // ------------------------------------------------------------------- /* * PPDContext */ PPDContext::PPDContext( const PPDParser* pParser ) : m_pParser( pParser ) { } // ------------------------------------------------------------------- PPDContext& PPDContext::operator=( const PPDContext& rCopy ) { m_pParser = rCopy.m_pParser; m_aCurrentValues = rCopy.m_aCurrentValues; return *this; } // ------------------------------------------------------------------- PPDContext::~PPDContext() { } // ------------------------------------------------------------------- const PPDKey* PPDContext::getModifiedKey( int n ) const { hash_type::const_iterator it; for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end() && n--; ++it ) ; return it != m_aCurrentValues.end() ? it->first : NULL; } // ------------------------------------------------------------------- void PPDContext::setParser( const PPDParser* pParser ) { if( pParser != m_pParser ) { m_aCurrentValues.clear(); m_pParser = pParser; } } // ------------------------------------------------------------------- const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const { if( ! m_pParser ) return NULL; hash_type::const_iterator it; it = m_aCurrentValues.find( pKey ); if( it != m_aCurrentValues.end() ) return it->second; if( ! m_pParser->hasKey( pKey ) ) return NULL; const PPDValue* pValue = pKey->getDefaultValue(); if( ! pValue ) pValue = pKey->getValue( 0 ); return pValue; } // ------------------------------------------------------------------- const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints ) { if( ! m_pParser || ! pKey ) return NULL; // pValue can be NULL - it means ignore this option if( ! m_pParser->hasKey( pKey ) ) return NULL; // check constraints if( pValue ) { if( bDontCareForConstraints ) { m_aCurrentValues[ pKey ] = pValue; } else if( checkConstraints( pKey, pValue, true ) ) { m_aCurrentValues[ pKey ] = pValue; // after setting this value, check all constraints ! hash_type::iterator it = m_aCurrentValues.begin(); while( it != m_aCurrentValues.end() ) { if( it->first != pKey && ! checkConstraints( it->first, it->second, false ) ) { #ifdef __DEBUG fprintf( stderr, "PPDContext::setValue: option %s (%s) is constrained after setting %s to %s\n", it->first->getKey().GetStr(), it->second->m_aOption.GetStr(), pKey->getKey().GetStr(), pValue->m_aOption.GetStr() ); #endif resetValue( it->first, true ); it = m_aCurrentValues.begin(); } else ++it; } } } else m_aCurrentValues[ pKey ] = NULL; return pValue; } // ------------------------------------------------------------------- bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue ) { if( ! m_pParser || ! pKey || ! pValue ) return false; // ensure that this key is already in the list if it exists at all if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() ) return checkConstraints( pKey, pValue, false ); // it is not in the list, insert it temporarily bool bRet = false; if( m_pParser->hasKey( pKey ) ) { const PPDValue* pDefValue = pKey->getDefaultValue(); m_aCurrentValues[ pKey ] = pDefValue; bRet = checkConstraints( pKey, pValue, false ); m_aCurrentValues.erase( pKey ); } return bRet; } // ------------------------------------------------------------------- bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable ) { if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) ) return false; const PPDValue* pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) ); if( ! pResetValue ) pResetValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "False" ) ) ); if( ! pResetValue && bDefaultable ) pResetValue = pKey->getDefaultValue(); bool bRet = pResetValue ? ( setValue( pKey, pResetValue ) == pResetValue ? true : false ) : false; return bRet; } // ------------------------------------------------------------------- bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset ) { if( ! pNewValue ) return true; // sanity checks if( ! m_pParser ) return false; if( pKey->getValue( pNewValue->m_aOption ) != pNewValue ) return false; // None / False and the default can always be set, but be careful ! // setting them might influence constrained values if( pNewValue->m_aOption.EqualsAscii( "None" ) || pNewValue->m_aOption.EqualsAscii( "False" ) || pNewValue == pKey->getDefaultValue() ) return true; const ::std::list< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() ); for( ::std::list< PPDParser::PPDConstraint >::const_iterator it = rConstraints.begin(); it != rConstraints.end(); ++it ) { const PPDKey* pLeft = it->m_pKey1; const PPDKey* pRight = it->m_pKey2; if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) ) continue; const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft; const PPDValue* pOtherKeyOption = pKey == pLeft ? it->m_pOption2 : it->m_pOption1; const PPDValue* pKeyOption = pKey == pLeft ? it->m_pOption1 : it->m_pOption2; // syntax *Key1 option1 *Key2 option2 if( pKeyOption && pOtherKeyOption ) { if( pNewValue != pKeyOption ) continue; if( pOtherKeyOption == getValue( pOtherKey ) ) { return false; } } // syntax *Key1 option *Key2 or *Key1 *Key2 option else if( pOtherKeyOption || pKeyOption ) { if( pKeyOption ) { if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) ) continue; // this should not happen, PPD broken if( pKeyOption == pNewValue && ! pOtherKeyOption->m_aOption.EqualsAscii( "None" ) && ! pOtherKeyOption->m_aOption.EqualsAscii( "False" ) ) { // check if the other value can be reset and // do so if possible if( bDoReset && resetValue( pOtherKey ) ) continue; return false; } } else if( pOtherKeyOption ) { if( getValue( pOtherKey ) == pOtherKeyOption && ! pNewValue->m_aOption.EqualsAscii( "None" ) && ! pNewValue->m_aOption.EqualsAscii( "False" ) ) return false; } else { // this should not happen, PPD is broken } } // syntax *Key1 *Key2 else { const PPDValue* pOtherValue = getValue( pOtherKey ); if( ! pOtherValue->m_aOption.EqualsAscii( "None" ) && ! pOtherValue->m_aOption.EqualsAscii( "False" ) && ! pNewValue->m_aOption.EqualsAscii( "None" ) && ! pNewValue->m_aOption.EqualsAscii( "False" ) ) return false; } } return true; } // ------------------------------------------------------------------- void PPDContext::getUnconstrainedValues( const PPDKey* pKey, ::std::list< const PPDValue* >& rValues ) { rValues.clear(); if( ! m_pParser || ! pKey || ! m_pParser->hasKey( pKey ) ) return; int nValues = pKey->countValues(); for( int i = 0; i < nValues; i++ ) { const PPDValue* pValue = pKey->getValue( i ); if( checkConstraints( pKey, pValue ) ) rValues.push_back( pValue ); } } // ------------------------------------------------------------------- void* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const { rBytes = 0; if( ! m_aCurrentValues.size() ) return NULL; hash_type::const_iterator it; for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) { ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); rBytes += aCopy.Len(); rBytes += 1; // for ':' if( it->second ) { aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); rBytes += aCopy.Len(); } else rBytes += 4; rBytes += 1; // for '\0' } rBytes += 1; void* pBuffer = new char[ rBytes ]; memset( pBuffer, 0, rBytes ); char* pRun = (char*)pBuffer; for( it = m_aCurrentValues.begin(); it != m_aCurrentValues.end(); ++it ) { ByteString aCopy( it->first->getKey(), RTL_TEXTENCODING_MS_1252 ); int nBytes = aCopy.Len(); memcpy( pRun, aCopy.GetBuffer(), nBytes ); pRun += nBytes; *pRun++ = ':'; if( it->second ) aCopy = ByteString( it->second->m_aOption, RTL_TEXTENCODING_MS_1252 ); else aCopy = "*nil"; nBytes = aCopy.Len(); memcpy( pRun, aCopy.GetBuffer(), nBytes ); pRun += nBytes; *pRun++ = 0; } return pBuffer; } // ------------------------------------------------------------------- void PPDContext::rebuildFromStreamBuffer( void* pBuffer, sal_uLong nBytes ) { if( ! m_pParser ) return; m_aCurrentValues.clear(); char* pRun = (char*)pBuffer; while( nBytes && *pRun ) { ByteString aLine( pRun ); int nPos = aLine.Search( ':' ); if( nPos != STRING_NOTFOUND ) { const PPDKey* pKey = m_pParser->getKey( String( aLine.Copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) ); if( pKey ) { const PPDValue* pValue = NULL; String aOption( aLine.Copy( nPos+1 ), RTL_TEXTENCODING_MS_1252 ); if( ! aOption.EqualsAscii( "*nil" ) ) pValue = pKey->getValue( aOption ); m_aCurrentValues[ pKey ] = pValue; #ifdef __DEBUG fprintf( stderr, "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { %s, %s }\n", pKV->m_pKey->getKey().GetStr(), pKV->m_pCurrentValue ? pKV->m_pCurrentValue->m_aOption.GetStr() : "" ); #endif } } nBytes -= aLine.Len()+1; pRun += aLine.Len()+1; } } // ------------------------------------------------------------------- int PPDContext::getRenderResolution() const { // initialize to reasonable default, if parser is not set int nDPI = 300; if( m_pParser ) { int nDPIx = 300, nDPIy = 300; const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Resolution" ) ) ); if( pKey ) { const PPDValue* pValue = getValue( pKey ); if( pValue ) m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy ); else m_pParser->getDefaultResolution( nDPIx, nDPIy ); } else m_pParser->getDefaultResolution( nDPIx, nDPIy ); nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy; } return nDPI; } // ------------------------------------------------------------------- void PPDContext::getPageSize( String& rPaper, int& rWidth, int& rHeight ) const { // initialize to reasonable default, if parser is not set rPaper = String( RTL_CONSTASCII_USTRINGPARAM( "A4" ) ); rWidth = 595; rHeight = 842; if( m_pParser ) { const PPDKey* pKey = m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) ); if( pKey ) { const PPDValue* pValue = getValue( pKey ); if( pValue ) { rPaper = pValue->m_aOption; m_pParser->getPaperDimension( rPaper, rWidth, rHeight ); } else { rPaper = m_pParser->getDefaultPaperDimension(); m_pParser->getDefaultPaperDimension( rWidth, rHeight ); } } } }