1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 #include <boost/noncopyable.hpp>
24 #include <boost/unordered_map.hpp>
26 #include <comphelper/string.hxx>
27 #include "vcl/ppdparser.hxx"
28 #include "vcl/strhelper.hxx"
29 #include "vcl/helper.hxx"
30 #include "vcl/svapp.hxx"
31 #include "vcl/settings.hxx"
32 #include "cupsmgr.hxx"
33 #include "tools/urlobj.hxx"
34 #include "tools/stream.hxx"
35 #include "tools/zcodec.hxx"
36 #include "osl/mutex.hxx"
37 #include "osl/file.hxx"
38 #include "osl/process.h"
39 #include "osl/thread.h"
40 #include "rtl/strbuf.hxx"
41 #include "rtl/ustrbuf.hxx"
42 #include "rtl/instance.hxx"
43 #include <sal/macros.h>
44 #include <salhelper/linkhelper.hxx>
46 #include "com/sun/star/lang/Locale.hpp"
54 bool operator()(const com::sun::star::lang::Locale
& i_rLeft
,
55 const com::sun::star::lang::Locale
& i_rRight
) const
57 return i_rLeft
.Language
.equals( i_rRight
.Language
) &&
58 i_rLeft
.Country
.equals( i_rRight
.Country
) &&
59 i_rLeft
.Variant
.equals( i_rRight
.Variant
);
65 size_t operator()(const com::sun::star::lang::Locale
& rLocale
) const
67 (size_t)rLocale
.Language
.hashCode()
68 ^ (size_t)rLocale
.Country
.hashCode()
69 ^ (size_t)rLocale
.Variant
.hashCode()
74 typedef boost::unordered_map
< com::sun::star::lang::Locale
, OUString
, LocaleHash
, LocaleEqual
> translation_map
;
75 typedef boost::unordered_map
< OUString
, translation_map
, OUStringHash
> key_translation_map
;
77 key_translation_map m_aTranslations
;
83 const OUString
& i_rKey
,
84 const OUString
& i_rOption
,
85 const OUString
& i_rValue
,
86 const OUString
& i_rTranslation
,
87 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale()
90 void insertOption( const OUString
& i_rKey
,
91 const OUString
& i_rOption
,
92 const OUString
& i_rTranslation
,
93 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale() )
95 insertValue( i_rKey
, i_rOption
, OUString(), i_rTranslation
, i_rLocale
);
98 void insertKey( const OUString
& i_rKey
,
99 const OUString
& i_rTranslation
,
100 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale() )
102 insertValue( i_rKey
, OUString(), OUString(), i_rTranslation
, i_rLocale
);
105 OUString
translateValue(
106 const OUString
& i_rKey
,
107 const OUString
& i_rOption
,
108 const OUString
& i_rValue
,
109 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale()
112 OUString
translateOption( const OUString
& i_rKey
,
113 const OUString
& i_rOption
,
114 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale() ) const
116 return translateValue( i_rKey
, i_rOption
, OUString(), i_rLocale
);
119 OUString
translateKey( const OUString
& i_rKey
,
120 const com::sun::star::lang::Locale
& i_rLocale
= com::sun::star::lang::Locale() ) const
122 return translateValue( i_rKey
, OUString(), OUString(), i_rLocale
);
126 static com::sun::star::lang::Locale
normalizeInputLocale(
127 const com::sun::star::lang::Locale
& i_rLocale
,
128 bool bInsertDefault
= false
131 com::sun::star::lang::Locale
aLoc( i_rLocale
);
132 if( bInsertDefault
&& aLoc
.Language
.isEmpty() )
134 // empty locale requested, fill in application UI locale
135 aLoc
= Application::GetSettings().GetUILanguageTag().getLocale();
137 #if OSL_DEBUG_LEVEL > 1
138 static const char* pEnvLocale
= getenv( "SAL_PPDPARSER_LOCALE" );
139 if( pEnvLocale
&& *pEnvLocale
)
141 OString
aStr( pEnvLocale
);
142 sal_Int32 nLen
= aStr
.getLength();
143 aLoc
.Language
= OStringToOUString( aStr
.copy( 0, nLen
> 2 ? 2 : nLen
), RTL_TEXTENCODING_MS_1252
);
144 if( nLen
>=5 && aStr
[2] == '_' )
145 aLoc
.Country
= OStringToOUString( aStr
.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252
);
147 aLoc
.Country
= OUString();
148 aLoc
.Variant
= OUString();
152 /* FIXME-BCP47: using Variant, uppercase? */
153 aLoc
.Language
= aLoc
.Language
.toAsciiLowerCase();
154 aLoc
.Country
= aLoc
.Country
.toAsciiUpperCase();
155 aLoc
.Variant
= aLoc
.Variant
.toAsciiUpperCase();
160 void PPDTranslator::insertValue(
161 const OUString
& i_rKey
,
162 const OUString
& i_rOption
,
163 const OUString
& i_rValue
,
164 const OUString
& i_rTranslation
,
165 const com::sun::star::lang::Locale
& i_rLocale
168 OUStringBuffer
aKey( i_rKey
.getLength() + i_rOption
.getLength() + i_rValue
.getLength() + 2 );
169 aKey
.append( i_rKey
);
170 if( !i_rOption
.isEmpty() || !i_rValue
.isEmpty() )
173 aKey
.append( i_rOption
);
175 if( !i_rValue
.isEmpty() )
178 aKey
.append( i_rValue
);
180 if( !aKey
.isEmpty() && !i_rTranslation
.isEmpty() )
182 OUString
aK( aKey
.makeStringAndClear() );
183 com::sun::star::lang::Locale aLoc
;
184 /* FIXME-BCP47: using Variant, uppercase? */
185 aLoc
.Language
= i_rLocale
.Language
.toAsciiLowerCase();
186 aLoc
.Country
= i_rLocale
.Country
.toAsciiUpperCase();
187 aLoc
.Variant
= i_rLocale
.Variant
.toAsciiUpperCase();
188 m_aTranslations
[ aK
][ aLoc
] = i_rTranslation
;
192 OUString
PPDTranslator::translateValue(
193 const OUString
& i_rKey
,
194 const OUString
& i_rOption
,
195 const OUString
& i_rValue
,
196 const com::sun::star::lang::Locale
& i_rLocale
201 OUStringBuffer
aKey( i_rKey
.getLength() + i_rOption
.getLength() + i_rValue
.getLength() + 2 );
202 aKey
.append( i_rKey
);
203 if( !i_rOption
.isEmpty() || !i_rValue
.isEmpty() )
206 aKey
.append( i_rOption
);
208 if( !i_rValue
.isEmpty() )
211 aKey
.append( i_rValue
);
213 if( !aKey
.isEmpty() )
215 OUString
aK( aKey
.makeStringAndClear() );
216 key_translation_map::const_iterator it
= m_aTranslations
.find( aK
);
217 if( it
!= m_aTranslations
.end() )
219 const translation_map
& rMap( it
->second
);
221 com::sun::star::lang::Locale
aLoc( normalizeInputLocale( i_rLocale
, true ) );
222 /* FIXME-BCP47: use LanguageTag::getFallbackStrings()? */
223 for( int nTry
= 0; nTry
< 4; nTry
++ )
225 translation_map::const_iterator tr
= rMap
.find( aLoc
);
226 if( tr
!= rMap
.end() )
228 aResult
= tr
->second
;
233 case 0: aLoc
.Variant
= OUString();break;
234 case 1: aLoc
.Country
= OUString();break;
235 case 2: aLoc
.Language
= OUString();break;
246 std::list
< PPDParser
* > aAllParsers
;
247 boost::unordered_map
< OUString
, OUString
, OUStringHash
>* pAllPPDFiles
;
253 while( aAllParsers
.begin() != aAllParsers
.end() )
255 delete aAllParsers
.front();
256 aAllParsers
.pop_front();
268 struct thePPDCache
: public rtl::Static
<PPDCache
, thePPDCache
> {};
271 class PPDDecompressStream
: private boost::noncopyable
273 SvFileStream
* mpFileStream
;
274 SvMemoryStream
* mpMemStream
;
278 PPDDecompressStream( const OUString
& rFile
);
279 ~PPDDecompressStream();
284 void Open( const OUString
& i_rFile
);
286 const OUString
& GetFileName() const { return maFileName
; }
289 PPDDecompressStream::PPDDecompressStream( const OUString
& i_rFile
) :
290 mpFileStream( NULL
),
296 PPDDecompressStream::~PPDDecompressStream()
301 void PPDDecompressStream::Open( const OUString
& i_rFile
)
305 mpFileStream
= new SvFileStream( i_rFile
, STREAM_READ
);
306 maFileName
= mpFileStream
->GetFileName();
308 if( ! mpFileStream
->IsOpen() )
315 mpFileStream
->ReadLine( aLine
);
316 mpFileStream
->Seek( 0 );
318 // check for compress'ed or gzip'ed file
319 if( aLine
.getLength() > 1 && static_cast<unsigned char>(aLine
[0]) == 0x1f
320 && static_cast<unsigned char>(aLine
[1]) == 0x8b /* check for gzip */ )
322 // so let's try to decompress the stream
323 mpMemStream
= new SvMemoryStream( 4096, 4096 );
325 aCodec
.BeginCompression( ZCODEC_DEFAULT_COMPRESSION
, false, true );
326 long nComp
= aCodec
.Decompress( *mpFileStream
, *mpMemStream
);
327 aCodec
.EndCompression();
330 // decompression failed, must be an uncompressed stream after all
331 delete mpMemStream
, mpMemStream
= NULL
;
332 mpFileStream
->Seek( 0 );
336 // compression successful, can get rid of file stream
337 delete mpFileStream
, mpFileStream
= NULL
;
338 mpMemStream
->Seek( 0 );
343 void PPDDecompressStream::Close()
345 delete mpMemStream
, mpMemStream
= NULL
;
346 delete mpFileStream
, mpFileStream
= NULL
;
349 bool PPDDecompressStream::IsOpen() const
351 return (mpMemStream
|| (mpFileStream
&& mpFileStream
->IsOpen()));
354 bool PPDDecompressStream::IsEof() const
356 return ( mpMemStream
? mpMemStream
->IsEof() : ( mpFileStream
? mpFileStream
->IsEof() : true ) );
359 OString
PPDDecompressStream::ReadLine()
363 mpMemStream
->ReadLine( o_rLine
);
364 else if( mpFileStream
)
365 mpFileStream
->ReadLine( o_rLine
);
369 static osl::FileBase::RC
resolveLink( const OUString
& i_rURL
, OUString
& o_rResolvedURL
, OUString
& o_rBaseName
, osl::FileStatus::Type
& o_rType
, int nLinkLevel
= 10 )
371 salhelper::LinkResolver
aResolver(osl_FileStatus_Mask_FileName
|
372 osl_FileStatus_Mask_Type
|
373 osl_FileStatus_Mask_FileURL
);
375 osl::FileBase::RC aRet
= aResolver
.fetchFileStatus(i_rURL
, nLinkLevel
);
377 if (aRet
== osl::FileBase::E_None
)
379 o_rResolvedURL
= aResolver
.m_aStatus
.getFileURL();
380 o_rBaseName
= aResolver
.m_aStatus
.getFileName();
381 o_rType
= aResolver
.m_aStatus
.getFileType();
387 void PPDParser::scanPPDDir( const OUString
& rDir
)
389 static struct suffix_t
391 const sal_Char
* pSuffix
;
392 const sal_Int32 nSuffixLen
;
393 } const pSuffixes
[] =
394 { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } };
396 const int nSuffixes
= SAL_N_ELEMENTS(pSuffixes
);
398 PPDCache
&rPPDCache
= thePPDCache::get();
400 osl::Directory
aDir( rDir
);
401 if ( aDir
.open() == osl::FileBase::E_None
)
403 osl::DirectoryItem aItem
;
405 INetURLObject
aPPDDir(rDir
);
406 while( aDir
.getNextItem( aItem
) == osl::FileBase::E_None
)
408 osl::FileStatus
aStatus( osl_FileStatus_Mask_FileName
);
409 if( aItem
.getFileStatus( aStatus
) == osl::FileBase::E_None
)
411 OUString aFileURL
, aFileName
;
412 osl::FileStatus::Type eType
= osl::FileStatus::Unknown
;
413 OUString aURL
= rDir
+ "/" + aStatus
.getFileName();
415 if(resolveLink( aURL
, aFileURL
, aFileName
, eType
) == osl::FileBase::E_None
)
417 if( eType
== osl::FileStatus::Regular
)
419 INetURLObject aPPDFile
= aPPDDir
;
420 aPPDFile
.Append( aFileName
);
423 for( int nSuffix
= 0; nSuffix
< nSuffixes
; nSuffix
++ )
425 if( aFileName
.getLength() > pSuffixes
[nSuffix
].nSuffixLen
)
427 if( aFileName
.endsWithIgnoreAsciiCaseAsciiL( pSuffixes
[nSuffix
].pSuffix
, pSuffixes
[nSuffix
].nSuffixLen
) )
429 (*rPPDCache
.pAllPPDFiles
)[ aFileName
.copy( 0, aFileName
.getLength() - pSuffixes
[nSuffix
].nSuffixLen
) ] = aPPDFile
.PathToFileName();
435 else if( eType
== osl::FileStatus::Directory
)
437 scanPPDDir( aFileURL
);
446 void PPDParser::initPPDFiles(PPDCache
&rPPDCache
)
448 if( rPPDCache
.pAllPPDFiles
)
451 rPPDCache
.pAllPPDFiles
= new boost::unordered_map
< OUString
, OUString
, OUStringHash
>();
453 // check installation directories
454 std::list
< OUString
> aPathList
;
455 psp::getPrinterPathList( aPathList
, PRINTER_PPDDIR
);
456 for( std::list
< OUString
>::const_iterator ppd_it
= aPathList
.begin(); ppd_it
!= aPathList
.end(); ++ppd_it
)
458 INetURLObject
aPPDDir( *ppd_it
, INET_PROT_FILE
, INetURLObject::ENCODE_ALL
);
459 scanPPDDir( aPPDDir
.GetMainURL( INetURLObject::NO_DECODE
) );
461 if( rPPDCache
.pAllPPDFiles
->find( OUString( "SGENPRT" ) ) == rPPDCache
.pAllPPDFiles
->end() )
463 // last try: search in directory of executable (mainly for setup)
465 if( osl_getExecutableFile( &aExe
.pData
) == osl_Process_E_None
)
467 INetURLObject
aDir( aExe
);
468 aDir
.removeSegment();
469 SAL_INFO("vcl.unx.print", "scanning last chance dir: "
470 << aDir
.GetMainURL(INetURLObject::NO_DECODE
));
471 scanPPDDir( aDir
.GetMainURL( INetURLObject::NO_DECODE
) );
472 SAL_INFO("vcl.unx.print", "SGENPRT "
473 << (rPPDCache
.pAllPPDFiles
->find("SGENPRT") ==
474 rPPDCache
.pAllPPDFiles
->end() ? "not found" : "found"));
479 OUString
PPDParser::getPPDFile( const OUString
& rFile
)
481 INetURLObject
aPPD( rFile
, INET_PROT_FILE
, INetURLObject::ENCODE_ALL
);
482 // someone might enter a full qualified name here
483 PPDDecompressStream
aStream( aPPD
.PathToFileName() );
484 if( ! aStream
.IsOpen() )
486 boost::unordered_map
< OUString
, OUString
, OUStringHash
>::const_iterator it
;
487 PPDCache
&rPPDCache
= thePPDCache::get();
492 initPPDFiles(rPPDCache
);
493 // some PPD files contain dots beside the extension, so try name first
494 // and cut of points after that
495 OUString
aBase( rFile
);
496 sal_Int32 nLastIndex
= aBase
.lastIndexOf( '/' );
497 if( nLastIndex
>= 0 )
498 aBase
= aBase
.copy( nLastIndex
+1 );
501 it
= rPPDCache
.pAllPPDFiles
->find( aBase
);
502 nLastIndex
= aBase
.lastIndexOf( '.' );
504 aBase
= aBase
.copy( 0, nLastIndex
);
505 } while( it
== rPPDCache
.pAllPPDFiles
->end() && nLastIndex
> 0 );
507 if( it
== rPPDCache
.pAllPPDFiles
->end() && bRetry
)
509 // a new file ? rehash
510 delete rPPDCache
.pAllPPDFiles
; rPPDCache
.pAllPPDFiles
= NULL
;
512 // note this is optimized for office start where
513 // no new files occur and initPPDFiles is called only once
515 } while( ! rPPDCache
.pAllPPDFiles
);
517 if( it
!= rPPDCache
.pAllPPDFiles
->end() )
518 aStream
.Open( it
->second
);
522 if( aStream
.IsOpen() )
524 OString aLine
= aStream
.ReadLine();
525 if (aLine
.startsWith("*PPD-Adobe"))
526 aRet
= aStream
.GetFileName();
529 // our *Include hack does usually not begin
530 // with *PPD-Adobe, so try some lines for *Include
532 while (aLine
.indexOf("*Include") != 0 && --nLines
)
533 aLine
= aStream
.ReadLine();
535 aRet
= aStream
.GetFileName();
542 const PPDParser
* PPDParser::getParser( const OUString
& rFile
)
544 static ::osl::Mutex aMutex
;
545 ::osl::Guard
< ::osl::Mutex
> aGuard( aMutex
);
547 OUString aFile
= rFile
;
548 if( !rFile
.startsWith( "CUPS:" ) )
549 aFile
= getPPDFile( rFile
);
550 if( aFile
.isEmpty() )
552 SAL_INFO("vcl.unx.print", "Could not get printer PPD file \""
557 PPDCache
&rPPDCache
= thePPDCache::get();
558 for( ::std::list
< PPDParser
* >::const_iterator it
= rPPDCache
.aAllParsers
.begin(); it
!= rPPDCache
.aAllParsers
.end(); ++it
)
559 if( (*it
)->m_aFile
== aFile
)
562 PPDParser
* pNewParser
= NULL
;
563 if( !aFile
.startsWith( "CUPS:" ) )
564 pNewParser
= new PPDParser( aFile
);
567 PrinterInfoManager
& rMgr
= PrinterInfoManager::get();
568 if( rMgr
.getType() == PrinterInfoManager::CUPS
)
571 pNewParser
= const_cast<PPDParser
*>(static_cast<CUPSManager
&>(rMgr
).createCUPSParser( aFile
));
577 // this may actually be the SGENPRT parser,
578 // so ensure uniquness here
579 rPPDCache
.aAllParsers
.remove( pNewParser
);
580 // insert new parser to list
581 rPPDCache
.aAllParsers
.push_front( pNewParser
);
586 PPDParser::PPDParser( const OUString
& rFile
) :
588 m_bType42Capable( false ),
589 m_aFileEncoding( RTL_TEXTENCODING_MS_1252
),
590 m_pDefaultImageableArea( NULL
),
591 m_pImageableAreas( NULL
),
592 m_pDefaultPaperDimension( NULL
),
593 m_pPaperDimensions( NULL
),
594 m_pDefaultInputSlot( NULL
),
595 m_pInputSlots( NULL
),
596 m_pDefaultResolution( NULL
),
597 m_pResolutions( NULL
),
598 m_pDefaultDuplexType( NULL
),
599 m_pDuplexTypes( NULL
),
601 m_pTranslator( new PPDTranslator() )
604 std::list
< OString
> aLines
;
605 PPDDecompressStream
aStream( m_aFile
);
606 if( aStream
.IsOpen() )
608 bool bLanguageEncoding
= false;
609 while( ! aStream
.IsEof() )
611 OString aCurLine
= aStream
.ReadLine();
612 if( aCurLine
.startsWith("*") )
614 if (aCurLine
.matchIgnoreAsciiCase(OString("*include:")))
616 aCurLine
= aCurLine
.copy(9);
617 aCurLine
= comphelper::string::stripStart(aCurLine
, ' ');
618 aCurLine
= comphelper::string::stripEnd(aCurLine
, ' ');
619 aCurLine
= comphelper::string::stripStart(aCurLine
, '\t');
620 aCurLine
= comphelper::string::stripEnd(aCurLine
, '\t');
621 aCurLine
= comphelper::string::stripEnd(aCurLine
, '\r');
622 aCurLine
= comphelper::string::stripEnd(aCurLine
, '\n');
623 aCurLine
= comphelper::string::stripStart(aCurLine
, '"');
624 aCurLine
= comphelper::string::stripEnd(aCurLine
, '"');
626 aStream
.Open(getPPDFile(OStringToOUString(aCurLine
, m_aFileEncoding
)));
629 else if( ! bLanguageEncoding
&&
630 aCurLine
.matchIgnoreAsciiCase(OString("*languageencoding")) )
632 bLanguageEncoding
= true; // generally only the first one counts
633 OString aLower
= aCurLine
.toAsciiLowerCase();
634 if( aLower
.indexOf("isolatin1", 17 ) != -1 ||
635 aLower
.indexOf("windowsansi", 17 ) != -1 )
636 m_aFileEncoding
= RTL_TEXTENCODING_MS_1252
;
637 else if( aLower
.indexOf("isolatin2", 17 ) != -1 )
638 m_aFileEncoding
= RTL_TEXTENCODING_ISO_8859_2
;
639 else if( aLower
.indexOf("isolatin5", 17 ) != -1 )
640 m_aFileEncoding
= RTL_TEXTENCODING_ISO_8859_5
;
641 else if( aLower
.indexOf("jis83-rksj", 17 ) != -1 )
642 m_aFileEncoding
= RTL_TEXTENCODING_SHIFT_JIS
;
643 else if( aLower
.indexOf("macstandard", 17 ) != -1 )
644 m_aFileEncoding
= RTL_TEXTENCODING_APPLE_ROMAN
;
645 else if( aLower
.indexOf("utf-8", 17 ) != -1 )
646 m_aFileEncoding
= RTL_TEXTENCODING_UTF8
;
649 aLines
.push_back( aCurLine
);
654 // now get the Values
656 #if OSL_DEBUG_LEVEL > 1
657 SAL_INFO("vcl.unx.print", "acquired " << m_aKeys
.size()
658 << " Keys from PPD " << m_aFile
<< ":");
659 for( PPDParser::hash_type::const_iterator it
= m_aKeys
.begin(); it
!= m_aKeys
.end(); ++it
)
661 const PPDKey
* pKey
= it
->second
;
662 char const* pSetupType
= "<unknown>";
663 switch( pKey
->m_eSetupType
)
665 case PPDKey::ExitServer
: pSetupType
= "ExitServer";break;
666 case PPDKey::Prolog
: pSetupType
= "Prolog";break;
667 case PPDKey::DocumentSetup
: pSetupType
= "DocumentSetup";break;
668 case PPDKey::PageSetup
: pSetupType
= "PageSetup";break;
669 case PPDKey::JCLSetup
: pSetupType
= "JCLSetup";break;
670 case PPDKey::AnySetup
: pSetupType
= "AnySetup";break;
673 SAL_INFO("vcl.unx.print", "\t\"" << pKey
->getKey() << "\" ("
674 << pKey
->countValues() << "values) OrderDependency: "
675 << pKey
->m_nOrderDependency
<< pSetupType
);
676 for( int j
= 0; j
< pKey
->countValues(); j
++ )
678 const PPDValue
* pValue
= pKey
->getValue( j
);
679 char const* pVType
= "<unknown>";
680 switch( pValue
->m_eType
)
682 case eInvocation
: pVType
= "invocation";break;
683 case eQuoted
: pVType
= "quoted";break;
684 case eString
: pVType
= "string";break;
685 case eSymbol
: pVType
= "symbol";break;
686 case eNo
: pVType
= "no";break;
689 SAL_INFO("vcl.unx.print", "\t\t"
690 << (pValue
== pKey
->m_pDefaultValue
? "(Default:) " : "")
691 << "option: \"" << pValue
->m_aOption
692 << "\", value: type " << pVType
<< " \""
693 << pValue
->m_aValue
<< "\"");
696 SAL_INFO("vcl.unx.print",
697 "constraints: (" << m_aConstraints
.size() << " found)");
698 for( std::list
< PPDConstraint
>::const_iterator cit
= m_aConstraints
.begin(); cit
!= m_aConstraints
.end(); ++cit
)
700 SAL_INFO("vcl.unx.print", "*\"" << cit
->m_pKey1
->getKey() << "\" \""
701 << (cit
->m_pOption1
? cit
->m_pOption1
->m_aOption
: "<nil>")
702 << "\" *\"" << cit
->m_pKey2
->getKey() << "\" \""
703 << (cit
->m_pOption2
? cit
->m_pOption2
->m_aOption
: "<nil>")
711 m_pImageableAreas
= getKey( OUString( "ImageableArea" ) );
712 if( m_pImageableAreas
)
713 m_pDefaultImageableArea
= m_pImageableAreas
->getDefaultValue();
714 if (m_pImageableAreas
== 0) {
716 "Warning: no ImageableArea in %s\n",
717 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
719 if (m_pDefaultImageableArea
== 0) {
721 "Warning: no DefaultImageableArea in %s\n",
722 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
725 m_pPaperDimensions
= getKey( OUString( "PaperDimension" ) );
726 if( m_pPaperDimensions
)
727 m_pDefaultPaperDimension
= m_pPaperDimensions
->getDefaultValue();
728 if (m_pPaperDimensions
== 0) {
730 "Warning: no PaperDimensions in %s\n",
731 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
733 if (m_pDefaultPaperDimension
== 0) {
735 "Warning: no DefaultPaperDimensions in %s\n",
736 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
739 m_pResolutions
= getKey( OUString( "Resolution" ) );
741 m_pDefaultResolution
= m_pResolutions
->getDefaultValue();
742 if (m_pResolutions
== 0) {
744 "Warning: no Resolution in %s\n",
745 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
747 if (m_pDefaultResolution
== 0) {
749 "Warning: no DefaultResolution in %s\n",
750 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
753 m_pInputSlots
= getKey( OUString( "InputSlot" ) );
755 m_pDefaultInputSlot
= m_pInputSlots
->getDefaultValue();
756 if (m_pInputSlots
== 0) {
758 "Warning: no InputSlot in %s\n",
759 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
761 if (m_pDefaultInputSlot
== 0) {
763 "Warning: no DefaultInputSlot in %s\n",
764 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
767 m_pDuplexTypes
= getKey( OUString( "Duplex" ) );
769 m_pDefaultDuplexType
= m_pDuplexTypes
->getDefaultValue();
771 m_pFontList
= getKey( OUString( "Font" ) );
772 if (m_pFontList
== 0) {
774 "Warning: no Font in %s\n",
775 OUStringToOString(m_aFile
, RTL_TEXTENCODING_UTF8
).getStr());
778 // fill in direct values
779 if( (pKey
= getKey( OUString( "ModelName" ) )) )
780 m_aPrinterName
= pKey
->getValue( 0 )->m_aValue
;
781 if( (pKey
= getKey( OUString( "NickName" ) )) )
782 m_aNickName
= pKey
->getValue( 0 )->m_aValue
;
783 if( (pKey
= getKey( OUString( "ColorDevice" ) )) )
784 m_bColorDevice
= pKey
->getValue( 0 )->m_aValue
.startsWithIgnoreAsciiCase( "true" );
786 if( (pKey
= getKey( OUString( "LanguageLevel" ) )) )
787 m_nLanguageLevel
= pKey
->getValue( 0 )->m_aValue
.toInt32();
788 if( (pKey
= getKey( OUString( "TTRasterizer" ) )) )
789 m_bType42Capable
= pKey
->getValue( 0 )->m_aValue
.equalsIgnoreAsciiCase( "Type42" );
792 PPDParser::~PPDParser()
794 for( PPDParser::hash_type::iterator it
= m_aKeys
.begin(); it
!= m_aKeys
.end(); ++it
)
796 delete m_pTranslator
;
799 void PPDParser::insertKey( const OUString
& rKey
, PPDKey
* pKey
)
801 m_aKeys
[ rKey
] = pKey
;
802 m_aOrderedKeys
.push_back( pKey
);
805 const PPDKey
* PPDParser::getKey( int n
) const
807 return ((unsigned int)n
< m_aOrderedKeys
.size() && n
>= 0) ? m_aOrderedKeys
[n
] : NULL
;
810 const PPDKey
* PPDParser::getKey( const OUString
& rKey
) const
812 PPDParser::hash_type::const_iterator it
= m_aKeys
.find( rKey
);
813 return it
!= m_aKeys
.end() ? it
->second
: NULL
;
816 bool PPDParser::hasKey( const PPDKey
* pKey
) const
818 return pKey
&& ( m_aKeys
.find( pKey
->getKey() ) != m_aKeys
.end() );
821 static sal_uInt8
getNibble( sal_Char cChar
)
824 if( cChar
>= '0' && cChar
<= '9' )
825 nRet
= sal_uInt8( cChar
- '0' );
826 else if( cChar
>= 'A' && cChar
<= 'F' )
827 nRet
= 10 + sal_uInt8( cChar
- 'A' );
828 else if( cChar
>= 'a' && cChar
<= 'f' )
829 nRet
= 10 + sal_uInt8( cChar
- 'a' );
833 OUString
PPDParser::handleTranslation(const OString
& i_rString
, bool bIsGlobalized
)
835 sal_Int32 nOrigLen
= i_rString
.getLength();
836 OStringBuffer
aTrans( nOrigLen
);
837 const sal_Char
* pStr
= i_rString
.getStr();
838 const sal_Char
* pEnd
= pStr
+ nOrigLen
;
845 while( *pStr
!= '>' && pStr
< pEnd
-1 )
847 cChar
= getNibble( *pStr
++ ) << 4;
848 cChar
|= getNibble( *pStr
++ );
849 aTrans
.append( cChar
);
854 aTrans
.append( *pStr
++ );
856 return OStringToOUString( aTrans
.makeStringAndClear(), bIsGlobalized
? RTL_TEXTENCODING_UTF8
: m_aFileEncoding
);
861 bool oddDoubleQuoteCount(OStringBuffer
&rBuffer
)
863 bool bHasOddCount
= false;
864 for (sal_Int32 i
= 0; i
< rBuffer
.getLength(); ++i
)
866 if (rBuffer
[i
] == '"')
867 bHasOddCount
= !bHasOddCount
;
873 void PPDParser::parse( ::std::list
< OString
>& rLines
)
875 std::list
< OString
>::iterator line
= rLines
.begin();
876 PPDParser::hash_type::const_iterator keyit
;
877 while( line
!= rLines
.end() )
879 OString
aCurrentLine( *line
);
881 if (aCurrentLine
.getLength() < 2 || aCurrentLine
[0] != '*')
883 if( aCurrentLine
[1] == '%' )
886 OString aKey
= GetCommandLineToken( 0, aCurrentLine
.getToken(0, ':') );
887 sal_Int32 nPos
= aKey
.indexOf('/');
889 aKey
= aKey
.copy(0, nPos
);
890 aKey
= aKey
.copy(1); // remove the '*'
892 if ((aKey
== "CloseUI") ||
893 (aKey
== "JCLCloseUI") ||
894 (aKey
== "OpenGroup") ||
895 (aKey
== "CloseGroup") ||
897 (aKey
== "JCLEnd") ||
898 (aKey
== "OpenSubGroup") ||
899 (aKey
== "CloseSubGroup"))
904 if ((aKey
== "OpenUI") || (aKey
== "JCLOpenUI"))
906 parseOpenUI( aCurrentLine
);
909 else if (aKey
== "OrderDependency")
911 parseOrderDependency( aCurrentLine
);
914 else if (aKey
== "UIConstraints" ||
915 aKey
== "NonUIConstraints")
917 continue; // parsed in pass 2
919 else if( aKey
== "CustomPageSize" ) // currently not handled
921 else if (aKey
.startsWith("Custom", &aKey
) )
923 //fdo#43049 very basic support for Custom entries, we ignore the
924 //validation params and types
926 OUString
aUniKey(OStringToOUString(aKey
, RTL_TEXTENCODING_MS_1252
));
927 keyit
= m_aKeys
.find( aUniKey
);
928 if(keyit
!= m_aKeys
.end())
930 pKey
= keyit
->second
;
931 pKey
->insertValue("Custom", eInvocation
, true);
936 // default values are parsed in pass 2
937 if (aKey
.startsWith("Default"))
947 OUString
aUniKey(OStringToOUString(aKey
, RTL_TEXTENCODING_MS_1252
));
948 // handle CUPS extension for globalized PPDs
949 /* FIXME-BCP47: really only ISO 639-1 two character language codes?
951 bool bIsGlobalizedLine
= false;
952 com::sun::star::lang::Locale aTransLocale
;
953 if( ( aUniKey
.getLength() > 3 && aUniKey
[ 2 ] == '.' ) ||
954 ( aUniKey
.getLength() > 5 && aUniKey
[ 2 ] == '_' && aUniKey
[ 5 ] == '.' ) )
956 if( aUniKey
[ 2 ] == '.' )
958 aTransLocale
.Language
= aUniKey
.copy( 0, 2 );
959 aUniKey
= aUniKey
.copy( 3 );
963 aTransLocale
.Language
= aUniKey
.copy( 0, 2 );
964 aTransLocale
.Country
= aUniKey
.copy( 3, 2 );
965 aUniKey
= aUniKey
.copy( 6 );
967 bIsGlobalizedLine
= true;
971 nPos
= aCurrentLine
.indexOf(':');
974 aOption
= OStringToOUString( aCurrentLine
.copy( 1, nPos
-1 ), RTL_TEXTENCODING_MS_1252
);
975 aOption
= GetCommandLineToken( 1, aOption
);
976 sal_Int32 nTransPos
= aOption
.indexOf( '/' );
977 if( nTransPos
!= -1 )
978 aOption
= aOption
.copy(0, nTransPos
);
981 PPDValueType eType
= eNo
;
983 OUString aOptionTranslation
;
984 OUString aValueTranslation
;
987 // found a colon, there may be an option
988 OString aLine
= aCurrentLine
.copy( 1, nPos
-1 );
989 aLine
= WhitespaceToSpace( aLine
);
990 sal_Int32 nTransPos
= aLine
.indexOf('/');
992 aOptionTranslation
= handleTranslation( aLine
.copy(nTransPos
+1), bIsGlobalizedLine
);
994 // read in more lines if necessary for multiline values
995 aLine
= aCurrentLine
.copy( nPos
+1 );
996 if (!aLine
.isEmpty())
998 OStringBuffer
aBuffer(aLine
);
999 while (line
!= rLines
.end() && oddDoubleQuoteCount(aBuffer
))
1001 // copy the newlines also
1002 aBuffer
.append('\n');
1003 aBuffer
.append(*line
);
1006 aLine
= aBuffer
.makeStringAndClear();
1008 aLine
= WhitespaceToSpace( aLine
);
1010 // #i100644# handle a missing value (actually a broken PPD)
1011 if( aLine
.isEmpty() )
1013 if( !aOption
.isEmpty() &&
1014 !aUniKey
.startsWith( "JCL" ) )
1015 eType
= eInvocation
;
1019 // check for invocation or quoted value
1020 else if(aLine
[0] == '"')
1022 aLine
= aLine
.copy(1);
1023 nTransPos
= aLine
.indexOf('"');
1024 if (nTransPos
== -1)
1025 nTransPos
= aLine
.getLength();
1026 aValue
= OStringToOUString(aLine
.copy(0, nTransPos
), RTL_TEXTENCODING_MS_1252
);
1027 // after the second doublequote can follow a / and a translation
1028 if (nTransPos
< aLine
.getLength() - 2)
1030 aValueTranslation
= handleTranslation( aLine
.copy( nTransPos
+2 ), bIsGlobalizedLine
);
1032 // check for quoted value
1033 if( !aOption
.isEmpty() &&
1034 !aUniKey
.startsWith( "JCL" ) )
1035 eType
= eInvocation
;
1039 // check for symbol value
1040 else if(aLine
[0] == '^')
1042 aLine
= aLine
.copy(1);
1043 aValue
= OStringToOUString(aLine
, RTL_TEXTENCODING_MS_1252
);
1048 // must be a string value then
1049 // strictly this is false because string values
1050 // can contain any whitespace which is reduced
1051 // to one space by now
1053 nTransPos
= aLine
.indexOf('/');
1054 if (nTransPos
== -1)
1055 nTransPos
= aLine
.getLength();
1056 aValue
= OStringToOUString(aLine
.copy(0, nTransPos
), RTL_TEXTENCODING_MS_1252
);
1057 if (nTransPos
+1 < aLine
.getLength())
1058 aValueTranslation
= handleTranslation( aLine
.copy( nTransPos
+1 ), bIsGlobalizedLine
);
1063 // handle globalized PPD entries
1064 if( bIsGlobalizedLine
)
1066 // handle main key translations of form:
1067 // *ll_CC.Translation MainKeyword/translated text: ""
1068 if( aUniKey
.equalsAscii( "Translation" ) )
1070 m_pTranslator
->insertKey( aOption
, aOptionTranslation
, aTransLocale
);
1072 // handle options translations of for:
1073 // *ll_CC.MainKeyword OptionKeyword/translated text: ""
1076 m_pTranslator
->insertOption( aUniKey
, aOption
, aOptionTranslation
, aTransLocale
);
1081 PPDKey
* pKey
= NULL
;
1082 keyit
= m_aKeys
.find( aUniKey
);
1083 if( keyit
== m_aKeys
.end() )
1085 pKey
= new PPDKey( aUniKey
);
1086 insertKey( aUniKey
, pKey
);
1089 pKey
= keyit
->second
;
1091 if( eType
== eNo
&& bQuery
)
1094 PPDValue
* pValue
= pKey
->insertValue( aOption
, eType
);
1097 pValue
->m_aValue
= aValue
;
1099 if( !aOptionTranslation
.isEmpty() )
1100 m_pTranslator
->insertOption( aUniKey
, aOption
, aOptionTranslation
, aTransLocale
);
1101 if( !aValueTranslation
.isEmpty() )
1102 m_pTranslator
->insertValue( aUniKey
, aOption
, aValue
, aValueTranslation
, aTransLocale
);
1104 // eventually update query and remove from option list
1105 if( bQuery
&& !pKey
->m_bQueryValue
)
1107 pKey
->m_aQueryValue
= *pValue
;
1108 pKey
->m_bQueryValue
= true;
1109 pKey
->eraseValue( pValue
->m_aOption
);
1113 // second pass: fill in defaults
1114 for( line
= rLines
.begin(); line
!= rLines
.end(); ++line
)
1116 OString
aLine(*line
);
1117 if (aLine
.startsWith("*Default"))
1119 OUString
aKey(OStringToOUString(aLine
.copy(8), RTL_TEXTENCODING_MS_1252
));
1120 sal_Int32 nPos
= aKey
.indexOf( ':' );
1123 aKey
= aKey
.copy(0, nPos
);
1124 OUString
aOption(OStringToOUString(
1125 WhitespaceToSpace(aLine
.copy(nPos
+9)),
1126 RTL_TEXTENCODING_MS_1252
));
1127 keyit
= m_aKeys
.find( aKey
);
1128 if( keyit
!= m_aKeys
.end() )
1130 PPDKey
* pKey
= keyit
->second
;
1131 const PPDValue
* pDefValue
= pKey
->getValue( aOption
);
1132 if( pKey
->m_pDefaultValue
== NULL
)
1133 pKey
->m_pDefaultValue
= pDefValue
;
1137 // some PPDs contain defaults for keys that
1138 // do not exist otherwise
1139 // (example: DefaultResolution)
1140 // so invent that key here and have a default value
1141 PPDKey
* pKey
= new PPDKey( aKey
);
1142 pKey
->insertValue( aOption
, eInvocation
/*or what ?*/ );
1143 insertKey( aKey
, pKey
);
1147 else if (aLine
.startsWith("*UIConstraints") ||
1148 aLine
.startsWith("*NonUIConstraints"))
1150 parseConstraint( aLine
);
1155 void PPDParser::parseOpenUI(const OString
& rLine
)
1157 OUString aTranslation
;
1158 OString aKey
= rLine
;
1160 sal_Int32 nPos
= aKey
.indexOf(':');
1162 aKey
= aKey
.copy(0, nPos
);
1163 nPos
= aKey
.indexOf('/');
1166 aTranslation
= handleTranslation( aKey
.copy( nPos
+ 1 ), false );
1167 aKey
= aKey
.copy(0, nPos
);
1169 aKey
= GetCommandLineToken( 1, aKey
);
1170 aKey
= aKey
.copy(1);
1172 OUString
aUniKey(OStringToOUString(aKey
, RTL_TEXTENCODING_MS_1252
));
1173 PPDParser::hash_type::const_iterator keyit
= m_aKeys
.find( aUniKey
);
1175 if( keyit
== m_aKeys
.end() )
1177 pKey
= new PPDKey( aUniKey
);
1178 insertKey( aUniKey
, pKey
);
1181 pKey
= keyit
->second
;
1183 pKey
->m_bUIOption
= true;
1184 m_pTranslator
->insertKey( pKey
->getKey(), aTranslation
);
1186 sal_Int32 nIndex
= 0;
1187 OString aValue
= WhitespaceToSpace( rLine
.getToken( 1, ':', nIndex
) );
1188 if( aValue
.equalsIgnoreAsciiCase("boolean"))
1189 pKey
->m_eUIType
= PPDKey::Boolean
;
1190 else if (aValue
.equalsIgnoreAsciiCase("pickmany"))
1191 pKey
->m_eUIType
= PPDKey::PickMany
;
1193 pKey
->m_eUIType
= PPDKey::PickOne
;
1196 void PPDParser::parseOrderDependency(const OString
& rLine
)
1198 OString
aLine(rLine
);
1199 sal_Int32 nPos
= aLine
.indexOf(':');
1201 aLine
= aLine
.copy( nPos
+1 );
1203 sal_Int32 nOrder
= GetCommandLineToken( 0, aLine
).toInt32();
1204 OString aSetup
= GetCommandLineToken( 1, aLine
);
1205 OUString
aKey(OStringToOUString(GetCommandLineToken(2, aLine
), RTL_TEXTENCODING_MS_1252
));
1206 if( aKey
[ 0 ] != '*' )
1207 return; // invalid order depency
1208 aKey
= aKey
.replaceAt( 0, 1, "" );
1211 PPDParser::hash_type::const_iterator keyit
= m_aKeys
.find( aKey
);
1212 if( keyit
== m_aKeys
.end() )
1214 pKey
= new PPDKey( aKey
);
1215 insertKey( aKey
, pKey
);
1218 pKey
= keyit
->second
;
1220 pKey
->m_nOrderDependency
= nOrder
;
1221 if( aSetup
== "ExitServer" )
1222 pKey
->m_eSetupType
= PPDKey::ExitServer
;
1223 else if( aSetup
== "Prolog" )
1224 pKey
->m_eSetupType
= PPDKey::Prolog
;
1225 else if( aSetup
== "DocumentSetup" )
1226 pKey
->m_eSetupType
= PPDKey::DocumentSetup
;
1227 else if( aSetup
== "PageSetup" )
1228 pKey
->m_eSetupType
= PPDKey::PageSetup
;
1229 else if( aSetup
== "JCLSetup" )
1230 pKey
->m_eSetupType
= PPDKey::JCLSetup
;
1232 pKey
->m_eSetupType
= PPDKey::AnySetup
;
1235 void PPDParser::parseConstraint( const OString
& rLine
)
1237 bool bFailed
= false;
1239 OUString
aLine(OStringToOUString(rLine
, RTL_TEXTENCODING_MS_1252
));
1240 sal_Int32 nIdx
= rLine
.indexOf(':');
1242 aLine
= aLine
.replaceAt(0, nIdx
+ 1, "");
1243 PPDConstraint aConstraint
;
1244 int nTokens
= GetCommandLineTokenCount( aLine
);
1245 for( int i
= 0; i
< nTokens
; i
++ )
1247 OUString aToken
= GetCommandLineToken( i
, aLine
);
1248 if( aToken
[ 0 ] == '*' )
1250 aToken
= aToken
.replaceAt( 0, 1, "" );
1251 if( aConstraint
.m_pKey1
)
1252 aConstraint
.m_pKey2
= getKey( aToken
);
1254 aConstraint
.m_pKey1
= getKey( aToken
);
1258 if( aConstraint
.m_pKey2
)
1260 if( ! ( aConstraint
.m_pOption2
= aConstraint
.m_pKey2
->getValue( aToken
) ) )
1263 else if( aConstraint
.m_pKey1
)
1265 if( ! ( aConstraint
.m_pOption1
= aConstraint
.m_pKey1
->getValue( aToken
) ) )
1269 // constraint for nonexistent keys; this happens
1274 // there must be two keywords
1275 if( ! aConstraint
.m_pKey1
|| ! aConstraint
.m_pKey2
|| bFailed
)
1277 SAL_INFO("vcl.unx.print",
1278 "Warning: constraint \"" << rLine
<< "\" is invalid");
1281 m_aConstraints
.push_back( aConstraint
);
1284 OUString
PPDParser::getDefaultPaperDimension() const
1286 if( m_pDefaultPaperDimension
)
1287 return m_pDefaultPaperDimension
->m_aOption
;
1292 bool PPDParser::getMargins(
1293 const OUString
& rPaperName
,
1294 int& rLeft
, int& rRight
,
1295 int& rUpper
, int& rLower
) const
1297 if( ! m_pImageableAreas
|| ! m_pPaperDimensions
)
1300 int nPDim
=-1, nImArea
=-1, i
;
1301 for( i
= 0; i
< m_pImageableAreas
->countValues(); i
++ )
1302 if( rPaperName
== m_pImageableAreas
->getValue( i
)->m_aOption
)
1304 for( i
= 0; i
< m_pPaperDimensions
->countValues(); i
++ )
1305 if( rPaperName
== m_pPaperDimensions
->getValue( i
)->m_aOption
)
1307 if( nPDim
== -1 || nImArea
== -1 )
1310 double ImLLx
, ImLLy
, ImURx
, ImURy
;
1311 double PDWidth
, PDHeight
;
1312 OUString aArea
= m_pImageableAreas
->getValue( nImArea
)->m_aValue
;
1313 ImLLx
= StringToDouble( GetCommandLineToken( 0, aArea
) );
1314 ImLLy
= StringToDouble( GetCommandLineToken( 1, aArea
) );
1315 ImURx
= StringToDouble( GetCommandLineToken( 2, aArea
) );
1316 ImURy
= StringToDouble( GetCommandLineToken( 3, aArea
) );
1317 aArea
= m_pPaperDimensions
->getValue( nPDim
)->m_aValue
;
1318 PDWidth
= StringToDouble( GetCommandLineToken( 0, aArea
) );
1319 PDHeight
= StringToDouble( GetCommandLineToken( 1, aArea
) );
1320 rLeft
= (int)(ImLLx
+ 0.5);
1321 rLower
= (int)(ImLLy
+ 0.5);
1322 rUpper
= (int)(PDHeight
- ImURy
+ 0.5);
1323 rRight
= (int)(PDWidth
- ImURx
+ 0.5);
1328 bool PPDParser::getPaperDimension(
1329 const OUString
& rPaperName
,
1330 int& rWidth
, int& rHeight
) const
1332 if( ! m_pPaperDimensions
)
1336 for( int i
= 0; i
< m_pPaperDimensions
->countValues(); i
++ )
1337 if( rPaperName
== m_pPaperDimensions
->getValue( i
)->m_aOption
)
1342 double PDWidth
, PDHeight
;
1343 OUString aArea
= m_pPaperDimensions
->getValue( nPDim
)->m_aValue
;
1344 PDWidth
= StringToDouble( GetCommandLineToken( 0, aArea
) );
1345 PDHeight
= StringToDouble( GetCommandLineToken( 1, aArea
) );
1346 rHeight
= (int)(PDHeight
+ 0.5);
1347 rWidth
= (int)(PDWidth
+ 0.5);
1352 OUString
PPDParser::matchPaper( int nWidth
, int nHeight
) const
1354 if( ! m_pPaperDimensions
)
1358 double PDWidth
, PDHeight
;
1359 double fSort
= 2e36
, fNewSort
;
1361 for( int i
= 0; i
< m_pPaperDimensions
->countValues(); i
++ )
1363 OUString aArea
= m_pPaperDimensions
->getValue( i
)->m_aValue
;
1364 PDWidth
= StringToDouble( GetCommandLineToken( 0, aArea
) );
1365 PDHeight
= StringToDouble( GetCommandLineToken( 1, aArea
) );
1366 PDWidth
/= (double)nWidth
;
1367 PDHeight
/= (double)nHeight
;
1368 if( PDWidth
>= 0.9 && PDWidth
<= 1.1 &&
1369 PDHeight
>= 0.9 && PDHeight
<= 1.1 )
1372 (1.0-PDWidth
)*(1.0-PDWidth
) + (1.0-PDHeight
)*(1.0-PDHeight
);
1373 if( fNewSort
== 0.0 ) // perfect match
1374 return m_pPaperDimensions
->getValue( i
)->m_aOption
;
1376 if( fNewSort
< fSort
)
1384 static bool bDontSwap
= false;
1385 if( nPDim
== -1 && ! bDontSwap
)
1387 // swap portrait/landscape and try again
1389 OUString rRet
= matchPaper( nHeight
, nWidth
);
1394 return nPDim
!= -1 ? m_pPaperDimensions
->getValue( nPDim
)->m_aOption
: OUString();
1397 OUString
PPDParser::getDefaultInputSlot() const
1399 if( m_pDefaultInputSlot
)
1400 return m_pDefaultInputSlot
->m_aValue
;
1404 void PPDParser::getResolutionFromString(
1405 const OUString
& rString
,
1406 int& rXRes
, int& rYRes
) const
1410 rXRes
= rYRes
= 300;
1412 nDPIPos
= rString
.indexOf( "dpi" );
1416 if( ( nPos
= rString
.indexOf( 'x' ) ) != -1 )
1418 rXRes
= rString
.copy( 0, nPos
).toInt32();
1419 rYRes
= rString
.getToken( 1, 'x' ).copy(0, nDPIPos
- nPos
- 1).toInt32();
1422 rXRes
= rYRes
= rString
.copy( 0, nDPIPos
).toInt32();
1426 void PPDParser::getDefaultResolution( int& rXRes
, int& rYRes
) const
1428 if( m_pDefaultResolution
)
1430 getResolutionFromString( m_pDefaultResolution
->m_aValue
, rXRes
, rYRes
);
1438 OUString
PPDParser::translateKey( const OUString
& i_rKey
,
1439 const com::sun::star::lang::Locale
& i_rLocale
) const
1441 OUString
aResult( m_pTranslator
->translateKey( i_rKey
, i_rLocale
) );
1442 if( aResult
.isEmpty() )
1447 OUString
PPDParser::translateOption( const OUString
& i_rKey
,
1448 const OUString
& i_rOption
,
1449 const com::sun::star::lang::Locale
& i_rLocale
) const
1451 OUString
aResult( m_pTranslator
->translateOption( i_rKey
, i_rOption
, i_rLocale
) );
1452 if( aResult
.isEmpty() )
1453 aResult
= i_rOption
;
1461 PPDKey::PPDKey( const OUString
& rKey
) :
1463 m_pDefaultValue( NULL
),
1464 m_bQueryValue( false ),
1465 m_bUIOption( false ),
1466 m_eUIType( PickOne
),
1467 m_nOrderDependency( 100 ),
1468 m_eSetupType( AnySetup
)
1476 const PPDValue
* PPDKey::getValue( int n
) const
1478 return ((unsigned int)n
< m_aOrderedValues
.size() && n
>= 0) ? m_aOrderedValues
[n
] : NULL
;
1481 const PPDValue
* PPDKey::getValue( const OUString
& rOption
) const
1483 PPDKey::hash_type::const_iterator it
= m_aValues
.find( rOption
);
1484 return it
!= m_aValues
.end() ? &it
->second
: NULL
;
1487 const PPDValue
* PPDKey::getValueCaseInsensitive( const OUString
& rOption
) const
1489 const PPDValue
* pValue
= getValue( rOption
);
1492 for( size_t n
= 0; n
< m_aOrderedValues
.size() && ! pValue
; n
++ )
1493 if( m_aOrderedValues
[n
]->m_aOption
.equalsIgnoreAsciiCase( rOption
) )
1494 pValue
= m_aOrderedValues
[n
];
1500 void PPDKey::eraseValue( const OUString
& rOption
)
1502 PPDKey::hash_type::iterator it
= m_aValues
.find( rOption
);
1503 if( it
== m_aValues
.end() )
1506 for( PPDKey::value_type::iterator vit
= m_aOrderedValues
.begin(); vit
!= m_aOrderedValues
.end(); ++vit
)
1508 if( *vit
== &(it
->second
) )
1510 m_aOrderedValues
.erase( vit
);
1514 m_aValues
.erase( it
);
1517 PPDValue
* PPDKey::insertValue(const OUString
& rOption
, PPDValueType eType
, bool bCustomOption
)
1519 if( m_aValues
.find( rOption
) != m_aValues
.end() )
1523 aValue
.m_aOption
= rOption
;
1524 aValue
.m_bCustomOption
= bCustomOption
;
1525 aValue
.m_eType
= eType
;
1526 m_aValues
[ rOption
] = aValue
;
1527 PPDValue
* pValue
= &m_aValues
[rOption
];
1528 m_aOrderedValues
.push_back( pValue
);
1536 PPDContext::PPDContext( const PPDParser
* pParser
) :
1537 m_pParser( pParser
)
1541 PPDContext
& PPDContext::operator=( const PPDContext
& rCopy
)
1543 m_pParser
= rCopy
.m_pParser
;
1544 m_aCurrentValues
= rCopy
.m_aCurrentValues
;
1548 PPDContext::~PPDContext()
1552 const PPDKey
* PPDContext::getModifiedKey( int n
) const
1554 hash_type::const_iterator it
;
1555 for( it
= m_aCurrentValues
.begin(); it
!= m_aCurrentValues
.end() && n
--; ++it
)
1557 return it
!= m_aCurrentValues
.end() ? it
->first
: NULL
;
1560 void PPDContext::setParser( const PPDParser
* pParser
)
1562 if( pParser
!= m_pParser
)
1564 m_aCurrentValues
.clear();
1565 m_pParser
= pParser
;
1569 const PPDValue
* PPDContext::getValue( const PPDKey
* pKey
) const
1574 hash_type::const_iterator it
;
1575 it
= m_aCurrentValues
.find( pKey
);
1576 if( it
!= m_aCurrentValues
.end() )
1579 if( ! m_pParser
->hasKey( pKey
) )
1582 const PPDValue
* pValue
= pKey
->getDefaultValue();
1584 pValue
= pKey
->getValue( 0 );
1589 const PPDValue
* PPDContext::setValue( const PPDKey
* pKey
, const PPDValue
* pValue
, bool bDontCareForConstraints
)
1591 if( ! m_pParser
|| ! pKey
)
1594 // pValue can be NULL - it means ignore this option
1596 if( ! m_pParser
->hasKey( pKey
) )
1599 // check constraints
1602 if( bDontCareForConstraints
)
1604 m_aCurrentValues
[ pKey
] = pValue
;
1606 else if( checkConstraints( pKey
, pValue
, true ) )
1608 m_aCurrentValues
[ pKey
] = pValue
;
1610 // after setting this value, check all constraints !
1611 hash_type::iterator it
= m_aCurrentValues
.begin();
1612 while( it
!= m_aCurrentValues
.end() )
1614 if( it
->first
!= pKey
&&
1615 ! checkConstraints( it
->first
, it
->second
, false ) )
1617 SAL_INFO("vcl.unx.print", "PPDContext::setValue: option "
1618 << it
->first
->getKey()
1619 << " (" << it
->second
->m_aOption
1620 << ") is constrained after setting "
1622 << " to " << pValue
->m_aOption
);
1623 resetValue( it
->first
, true );
1624 it
= m_aCurrentValues
.begin();
1632 m_aCurrentValues
[ pKey
] = NULL
;
1637 bool PPDContext::checkConstraints( const PPDKey
* pKey
, const PPDValue
* pValue
)
1639 if( ! m_pParser
|| ! pKey
|| ! pValue
)
1642 // ensure that this key is already in the list if it exists at all
1643 if( m_aCurrentValues
.find( pKey
) != m_aCurrentValues
.end() )
1644 return checkConstraints( pKey
, pValue
, false );
1646 // it is not in the list, insert it temporarily
1648 if( m_pParser
->hasKey( pKey
) )
1650 const PPDValue
* pDefValue
= pKey
->getDefaultValue();
1651 m_aCurrentValues
[ pKey
] = pDefValue
;
1652 bRet
= checkConstraints( pKey
, pValue
, false );
1653 m_aCurrentValues
.erase( pKey
);
1659 bool PPDContext::resetValue( const PPDKey
* pKey
, bool bDefaultable
)
1661 if( ! pKey
|| ! m_pParser
|| ! m_pParser
->hasKey( pKey
) )
1664 const PPDValue
* pResetValue
= pKey
->getValue( OUString( "None" ) );
1666 pResetValue
= pKey
->getValue( OUString( "False" ) );
1667 if( ! pResetValue
&& bDefaultable
)
1668 pResetValue
= pKey
->getDefaultValue();
1670 bool bRet
= pResetValue
&& ( setValue( pKey
, pResetValue
) == pResetValue
);
1675 bool PPDContext::checkConstraints( const PPDKey
* pKey
, const PPDValue
* pNewValue
, bool bDoReset
)
1684 if( pKey
->getValue( pNewValue
->m_aOption
) != pNewValue
)
1687 // None / False and the default can always be set, but be careful !
1688 // setting them might influence constrained values
1689 if( pNewValue
->m_aOption
.equalsAscii( "None" ) || pNewValue
->m_aOption
.equalsAscii( "False" ) ||
1690 pNewValue
== pKey
->getDefaultValue() )
1693 const ::std::list
< PPDParser::PPDConstraint
>& rConstraints( m_pParser
->getConstraints() );
1694 for( ::std::list
< PPDParser::PPDConstraint
>::const_iterator it
= rConstraints
.begin(); it
!= rConstraints
.end(); ++it
)
1696 const PPDKey
* pLeft
= it
->m_pKey1
;
1697 const PPDKey
* pRight
= it
->m_pKey2
;
1698 if( ! pLeft
|| ! pRight
|| ( pKey
!= pLeft
&& pKey
!= pRight
) )
1701 const PPDKey
* pOtherKey
= pKey
== pLeft
? pRight
: pLeft
;
1702 const PPDValue
* pOtherKeyOption
= pKey
== pLeft
? it
->m_pOption2
: it
->m_pOption1
;
1703 const PPDValue
* pKeyOption
= pKey
== pLeft
? it
->m_pOption1
: it
->m_pOption2
;
1705 // syntax *Key1 option1 *Key2 option2
1706 if( pKeyOption
&& pOtherKeyOption
)
1708 if( pNewValue
!= pKeyOption
)
1710 if( pOtherKeyOption
== getValue( pOtherKey
) )
1715 // syntax *Key1 option *Key2 or *Key1 *Key2 option
1716 else if( pOtherKeyOption
|| pKeyOption
)
1720 if( ! ( pOtherKeyOption
= getValue( pOtherKey
) ) )
1721 continue; // this should not happen, PPD broken
1723 if( pKeyOption
== pNewValue
&&
1724 ! pOtherKeyOption
->m_aOption
.equalsAscii( "None" ) &&
1725 ! pOtherKeyOption
->m_aOption
.equalsAscii( "False" ) )
1727 // check if the other value can be reset and
1728 // do so if possible
1729 if( bDoReset
&& resetValue( pOtherKey
) )
1735 else if( pOtherKeyOption
)
1737 if( getValue( pOtherKey
) == pOtherKeyOption
&&
1738 ! pNewValue
->m_aOption
.equalsAscii( "None" ) &&
1739 ! pNewValue
->m_aOption
.equalsAscii( "False" ) )
1744 // this should not happen, PPD is broken
1747 // syntax *Key1 *Key2
1750 const PPDValue
* pOtherValue
= getValue( pOtherKey
);
1751 if( ! pOtherValue
->m_aOption
.equalsAscii( "None" ) &&
1752 ! pOtherValue
->m_aOption
.equalsAscii( "False" ) &&
1753 ! pNewValue
->m_aOption
.equalsAscii( "None" ) &&
1754 ! pNewValue
->m_aOption
.equalsAscii( "False" ) )
1761 char* PPDContext::getStreamableBuffer( sal_uLong
& rBytes
) const
1764 if( ! m_aCurrentValues
.size() )
1766 hash_type::const_iterator it
;
1767 for( it
= m_aCurrentValues
.begin(); it
!= m_aCurrentValues
.end(); ++it
)
1769 OString
aCopy(OUStringToOString(it
->first
->getKey(), RTL_TEXTENCODING_MS_1252
));
1770 rBytes
+= aCopy
.getLength();
1771 rBytes
+= 1; // for ':'
1774 aCopy
= OUStringToOString(it
->second
->m_aOption
, RTL_TEXTENCODING_MS_1252
);
1775 rBytes
+= aCopy
.getLength();
1779 rBytes
+= 1; // for '\0'
1782 char* pBuffer
= new char[ rBytes
];
1783 memset( pBuffer
, 0, rBytes
);
1784 char* pRun
= pBuffer
;
1785 for( it
= m_aCurrentValues
.begin(); it
!= m_aCurrentValues
.end(); ++it
)
1787 OString
aCopy(OUStringToOString(it
->first
->getKey(), RTL_TEXTENCODING_MS_1252
));
1788 int nBytes
= aCopy
.getLength();
1789 memcpy( pRun
, aCopy
.getStr(), nBytes
);
1793 aCopy
= OUStringToOString(it
->second
->m_aOption
, RTL_TEXTENCODING_MS_1252
);
1796 nBytes
= aCopy
.getLength();
1797 memcpy( pRun
, aCopy
.getStr(), nBytes
);
1805 void PPDContext::rebuildFromStreamBuffer( char* pBuffer
, sal_uLong nBytes
)
1810 m_aCurrentValues
.clear();
1812 char* pRun
= pBuffer
;
1813 while( nBytes
&& *pRun
)
1815 OString
aLine( pRun
);
1816 sal_Int32 nPos
= aLine
.indexOf(':');
1819 const PPDKey
* pKey
= m_pParser
->getKey( OStringToOUString( aLine
.copy( 0, nPos
), RTL_TEXTENCODING_MS_1252
) );
1822 const PPDValue
* pValue
= NULL
;
1823 OUString
aOption(OStringToOUString(aLine
.copy(nPos
+1), RTL_TEXTENCODING_MS_1252
));
1824 if (aOption
!= "*nil")
1825 pValue
= pKey
->getValue( aOption
);
1826 m_aCurrentValues
[ pKey
] = pValue
;
1827 SAL_INFO("vcl.unx.print",
1828 "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { "
1829 << pKey
->getKey() << " , "
1830 << (pValue
? aOption
: "<nil>")
1834 nBytes
-= aLine
.getLength()+1;
1835 pRun
+= aLine
.getLength()+1;
1839 int PPDContext::getRenderResolution() const
1841 // initialize to reasonable default, if parser is not set
1845 int nDPIx
= 300, nDPIy
= 300;
1846 const PPDKey
* pKey
= m_pParser
->getKey( OUString( "Resolution" ) );
1849 const PPDValue
* pValue
= getValue( pKey
);
1851 m_pParser
->getResolutionFromString( pValue
->m_aOption
, nDPIx
, nDPIy
);
1853 m_pParser
->getDefaultResolution( nDPIx
, nDPIy
);
1856 m_pParser
->getDefaultResolution( nDPIx
, nDPIy
);
1858 nDPI
= (nDPIx
> nDPIy
) ? nDPIx
: nDPIy
;
1863 void PPDContext::getPageSize( OUString
& rPaper
, int& rWidth
, int& rHeight
) const
1865 // initialize to reasonable default, if parser is not set
1871 const PPDKey
* pKey
= m_pParser
->getKey( OUString( "PageSize" ) );
1874 const PPDValue
* pValue
= getValue( pKey
);
1877 rPaper
= pValue
->m_aOption
;
1878 m_pParser
->getPaperDimension( rPaper
, rWidth
, rHeight
);
1882 rPaper
= m_pParser
->getDefaultPaperDimension();
1883 m_pParser
->getDefaultPaperDimension( rWidth
, rHeight
);
1889 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */