Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / vcl / unx / generic / printer / ppdparser.cxx
blob606fd550da979e6c61108aa8f5373f7126d0309f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <stdlib.h>
21 #include <stdio.h>
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"
48 namespace psp
50 class PPDTranslator
52 struct LocaleEqual
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 );
63 struct LocaleHash
65 size_t operator()(const com::sun::star::lang::Locale& rLocale) const
66 { return
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;
78 public:
79 PPDTranslator() {}
80 ~PPDTranslator() {}
82 void insertValue(
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()
110 ) const;
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 );
146 else
147 aLoc.Country = OUString();
148 aLoc.Variant = OUString();
150 #endif
152 /* FIXME-BCP47: using Variant, uppercase? */
153 aLoc.Language = aLoc.Language.toAsciiLowerCase();
154 aLoc.Country = aLoc.Country.toAsciiUpperCase();
155 aLoc.Variant = aLoc.Variant.toAsciiUpperCase();
157 return aLoc;
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() )
172 aKey.append( ':' );
173 aKey.append( i_rOption );
175 if( !i_rValue.isEmpty() )
177 aKey.append( ':' );
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
197 ) const
199 OUString aResult;
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() )
205 aKey.append( ':' );
206 aKey.append( i_rOption );
208 if( !i_rValue.isEmpty() )
210 aKey.append( ':' );
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;
229 break;
231 switch( nTry )
233 case 0: aLoc.Variant = OUString();break;
234 case 1: aLoc.Country = OUString();break;
235 case 2: aLoc.Language = OUString();break;
240 return aResult;
243 class PPDCache
245 public:
246 std::list< PPDParser* > aAllParsers;
247 boost::unordered_map< OUString, OUString, OUStringHash >* pAllPPDFiles;
248 PPDCache()
249 : pAllPPDFiles(NULL)
251 ~PPDCache()
253 while( aAllParsers.begin() != aAllParsers.end() )
255 delete aAllParsers.front();
256 aAllParsers.pop_front();
258 delete pAllPPDFiles;
259 pAllPPDFiles = NULL;
264 using namespace psp;
266 namespace
268 struct thePPDCache : public rtl::Static<PPDCache, thePPDCache> {};
271 class PPDDecompressStream: private boost::noncopyable
273 SvFileStream* mpFileStream;
274 SvMemoryStream* mpMemStream;
275 OUString maFileName;
277 public:
278 PPDDecompressStream( const OUString& rFile );
279 ~PPDDecompressStream();
281 bool IsOpen() const;
282 bool IsEof() const;
283 OString ReadLine();
284 void Open( const OUString& i_rFile );
285 void Close();
286 const OUString& GetFileName() const { return maFileName; }
289 PPDDecompressStream::PPDDecompressStream( const OUString& i_rFile ) :
290 mpFileStream( NULL ),
291 mpMemStream( NULL )
293 Open( i_rFile );
296 PPDDecompressStream::~PPDDecompressStream()
298 Close();
301 void PPDDecompressStream::Open( const OUString& i_rFile )
303 Close();
305 mpFileStream = new SvFileStream( i_rFile, STREAM_READ );
306 maFileName = mpFileStream->GetFileName();
308 if( ! mpFileStream->IsOpen() )
310 Close();
311 return;
314 OString aLine;
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 );
324 ZCodec aCodec;
325 aCodec.BeginCompression( ZCODEC_DEFAULT_COMPRESSION, false, true );
326 long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream );
327 aCodec.EndCompression();
328 if( nComp < 0 )
330 // decompression failed, must be an uncompressed stream after all
331 delete mpMemStream, mpMemStream = NULL;
332 mpFileStream->Seek( 0 );
334 else
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()
361 OString o_rLine;
362 if( mpMemStream )
363 mpMemStream->ReadLine( o_rLine );
364 else if( mpFileStream )
365 mpFileStream->ReadLine( o_rLine );
366 return 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();
384 return aRet;
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 );
422 // match extension
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();
430 break;
435 else if( eType == osl::FileStatus::Directory )
437 scanPPDDir( aFileURL );
442 aDir.close();
446 void PPDParser::initPPDFiles(PPDCache &rPPDCache)
448 if( rPPDCache.pAllPPDFiles )
449 return;
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)
464 OUString aExe;
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();
489 bool bRetry = true;
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( '.' );
503 if( nLastIndex > 0 )
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;
511 bRetry = false;
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 );
521 OUString aRet;
522 if( aStream.IsOpen() )
524 OString aLine = aStream.ReadLine();
525 if (aLine.startsWith("*PPD-Adobe"))
526 aRet = aStream.GetFileName();
527 else
529 // our *Include hack does usually not begin
530 // with *PPD-Adobe, so try some lines for *Include
531 int nLines = 10;
532 while (aLine.indexOf("*Include") != 0 && --nLines)
533 aLine = aStream.ReadLine();
534 if( nLines )
535 aRet = aStream.GetFileName();
539 return aRet;
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 \""
553 << rFile << "\" !");
554 return NULL;
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 )
560 return *it;
562 PPDParser* pNewParser = NULL;
563 if( !aFile.startsWith( "CUPS:" ) )
564 pNewParser = new PPDParser( aFile );
565 else
567 PrinterInfoManager& rMgr = PrinterInfoManager::get();
568 if( rMgr.getType() == PrinterInfoManager::CUPS )
570 #ifdef ENABLE_CUPS
571 pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
572 #endif
575 if( pNewParser )
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 );
583 return pNewParser;
586 PPDParser::PPDParser( const OUString& rFile ) :
587 m_aFile( 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 ),
600 m_pFontList( NULL ),
601 m_pTranslator( new PPDTranslator() )
603 // read in the file
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, '"');
625 aStream.Close();
626 aStream.Open(getPPDFile(OStringToOUString(aCurLine, m_aFileEncoding)));
627 continue;
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 );
652 aStream.Close();
654 // now get the Values
655 parse( aLines );
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;
671 default: 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;
687 default: 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>")
704 << "\"");
706 #endif
708 // fill in shortcuts
709 const PPDKey* pKey;
711 m_pImageableAreas = getKey( OUString( "ImageableArea" ) );
712 if( m_pImageableAreas )
713 m_pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
714 if (m_pImageableAreas == 0) {
715 OSL_TRACE(
716 "Warning: no ImageableArea in %s\n",
717 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
719 if (m_pDefaultImageableArea == 0) {
720 OSL_TRACE(
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) {
729 OSL_TRACE(
730 "Warning: no PaperDimensions in %s\n",
731 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
733 if (m_pDefaultPaperDimension == 0) {
734 OSL_TRACE(
735 "Warning: no DefaultPaperDimensions in %s\n",
736 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
739 m_pResolutions = getKey( OUString( "Resolution" ) );
740 if( m_pResolutions )
741 m_pDefaultResolution = m_pResolutions->getDefaultValue();
742 if (m_pResolutions == 0) {
743 OSL_TRACE(
744 "Warning: no Resolution in %s\n",
745 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
747 if (m_pDefaultResolution == 0) {
748 OSL_TRACE(
749 "Warning: no DefaultResolution in %s\n",
750 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
753 m_pInputSlots = getKey( OUString( "InputSlot" ) );
754 if( m_pInputSlots )
755 m_pDefaultInputSlot = m_pInputSlots->getDefaultValue();
756 if (m_pInputSlots == 0) {
757 OSL_TRACE(
758 "Warning: no InputSlot in %s\n",
759 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
761 if (m_pDefaultInputSlot == 0) {
762 OSL_TRACE(
763 "Warning: no DefaultInputSlot in %s\n",
764 OUStringToOString(m_aFile, RTL_TEXTENCODING_UTF8).getStr());
767 m_pDuplexTypes = getKey( OUString( "Duplex" ) );
768 if( m_pDuplexTypes )
769 m_pDefaultDuplexType = m_pDuplexTypes->getDefaultValue();
771 m_pFontList = getKey( OUString( "Font" ) );
772 if (m_pFontList == 0) {
773 OSL_TRACE(
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 )
795 delete it->second;
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 )
823 sal_uInt8 nRet = 0;
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' );
830 return nRet;
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;
839 while( pStr < pEnd )
841 if( *pStr == '<' )
843 pStr++;
844 sal_Char cChar;
845 while( *pStr != '>' && pStr < pEnd-1 )
847 cChar = getNibble( *pStr++ ) << 4;
848 cChar |= getNibble( *pStr++ );
849 aTrans.append( cChar );
851 pStr++;
853 else
854 aTrans.append( *pStr++ );
856 return OStringToOUString( aTrans.makeStringAndClear(), bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding );
859 namespace
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;
869 return 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 );
880 ++line;
881 if (aCurrentLine.getLength() < 2 || aCurrentLine[0] != '*')
882 continue;
883 if( aCurrentLine[1] == '%' )
884 continue;
886 OString aKey = GetCommandLineToken( 0, aCurrentLine.getToken(0, ':') );
887 sal_Int32 nPos = aKey.indexOf('/');
888 if (nPos != -1)
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") ||
896 (aKey == "End") ||
897 (aKey == "JCLEnd") ||
898 (aKey == "OpenSubGroup") ||
899 (aKey == "CloseSubGroup"))
901 continue;
904 if ((aKey == "OpenUI") || (aKey == "JCLOpenUI"))
906 parseOpenUI( aCurrentLine );
907 continue;
909 else if (aKey == "OrderDependency")
911 parseOrderDependency( aCurrentLine );
912 continue;
914 else if (aKey == "UIConstraints" ||
915 aKey == "NonUIConstraints")
917 continue; // parsed in pass 2
919 else if( aKey == "CustomPageSize" ) // currently not handled
920 continue;
921 else if (aKey.startsWith("Custom", &aKey) )
923 //fdo#43049 very basic support for Custom entries, we ignore the
924 //validation params and types
925 PPDKey* pKey = NULL;
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);
933 continue;
936 // default values are parsed in pass 2
937 if (aKey.startsWith("Default"))
938 continue;
940 bool bQuery = false;
941 if (aKey[0] == '?')
943 aKey = aKey.copy(1);
944 bQuery = true;
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?
950 * goodnight.. */
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 );
961 else
963 aTransLocale.Language = aUniKey.copy( 0, 2 );
964 aTransLocale.Country = aUniKey.copy( 3, 2 );
965 aUniKey = aUniKey.copy( 6 );
967 bIsGlobalizedLine = true;
970 OUString aOption;
971 nPos = aCurrentLine.indexOf(':');
972 if( nPos != -1 )
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;
982 OUString aValue;
983 OUString aOptionTranslation;
984 OUString aValueTranslation;
985 if( nPos != -1 )
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('/');
991 if (nTransPos != -1)
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);
1004 ++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;
1016 else
1017 eType = eQuoted;
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;
1036 else
1037 eType = eQuoted;
1039 // check for symbol value
1040 else if(aLine[0] == '^')
1042 aLine = aLine.copy(1);
1043 aValue = OStringToOUString(aLine, RTL_TEXTENCODING_MS_1252);
1044 eType = eSymbol;
1046 else
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
1052 // who cares ...
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 );
1059 eType = eString;
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: ""
1074 else
1076 m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1078 continue;
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 );
1088 else
1089 pKey = keyit->second;
1091 if( eType == eNo && bQuery )
1092 continue;
1094 PPDValue* pValue = pKey->insertValue( aOption, eType );
1095 if( ! pValue )
1096 continue;
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( ':' );
1121 if( nPos != -1 )
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;
1135 else
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(':');
1161 if( nPos != -1 )
1162 aKey = aKey.copy(0, nPos);
1163 nPos = aKey.indexOf('/');
1164 if( nPos != -1 )
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 );
1174 PPDKey* pKey;
1175 if( keyit == m_aKeys.end() )
1177 pKey = new PPDKey( aUniKey );
1178 insertKey( aUniKey, pKey );
1180 else
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;
1192 else
1193 pKey->m_eUIType = PPDKey::PickOne;
1196 void PPDParser::parseOrderDependency(const OString& rLine)
1198 OString aLine(rLine);
1199 sal_Int32 nPos = aLine.indexOf(':');
1200 if( nPos != -1 )
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, "" );
1210 PPDKey* pKey;
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 );
1217 else
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;
1231 else
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(':');
1241 if (nIdx != -1)
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 );
1253 else
1254 aConstraint.m_pKey1 = getKey( aToken );
1256 else
1258 if( aConstraint.m_pKey2 )
1260 if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1261 bFailed = true;
1263 else if( aConstraint.m_pKey1 )
1265 if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1266 bFailed = true;
1268 else
1269 // constraint for nonexistent keys; this happens
1270 // e.g. in HP4PLUS3
1271 bFailed = true;
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");
1280 else
1281 m_aConstraints.push_back( aConstraint );
1284 OUString PPDParser::getDefaultPaperDimension() const
1286 if( m_pDefaultPaperDimension )
1287 return m_pDefaultPaperDimension->m_aOption;
1289 return OUString();
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 )
1298 return false;
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 )
1303 nImArea = i;
1304 for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1305 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1306 nPDim = i;
1307 if( nPDim == -1 || nImArea == -1 )
1308 return false;
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);
1325 return true;
1328 bool PPDParser::getPaperDimension(
1329 const OUString& rPaperName,
1330 int& rWidth, int& rHeight ) const
1332 if( ! m_pPaperDimensions )
1333 return false;
1335 int nPDim=-1;
1336 for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1337 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1338 nPDim = i;
1339 if( nPDim == -1 )
1340 return false;
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);
1349 return true;
1352 OUString PPDParser::matchPaper( int nWidth, int nHeight ) const
1354 if( ! m_pPaperDimensions )
1355 return OUString();
1357 int nPDim = -1;
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 )
1371 fNewSort =
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 )
1378 fSort = fNewSort;
1379 nPDim = i;
1384 static bool bDontSwap = false;
1385 if( nPDim == -1 && ! bDontSwap )
1387 // swap portrait/landscape and try again
1388 bDontSwap = true;
1389 OUString rRet = matchPaper( nHeight, nWidth );
1390 bDontSwap = false;
1391 return rRet;
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;
1401 return OUString();
1404 void PPDParser::getResolutionFromString(
1405 const OUString& rString,
1406 int& rXRes, int& rYRes ) const
1408 sal_Int32 nDPIPos;
1410 rXRes = rYRes = 300;
1412 nDPIPos = rString.indexOf( "dpi" );
1413 if( nDPIPos != -1 )
1415 sal_Int32 nPos = 0;
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();
1421 else
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 );
1431 return;
1434 rXRes = 300;
1435 rYRes = 300;
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() )
1443 aResult = i_rKey;
1444 return aResult;
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;
1454 return aResult;
1458 * PPDKey
1461 PPDKey::PPDKey( const OUString& rKey ) :
1462 m_aKey( 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 )
1472 PPDKey::~PPDKey()
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 );
1490 if( ! pValue )
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];
1497 return pValue;
1500 void PPDKey::eraseValue( const OUString& rOption )
1502 PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1503 if( it == m_aValues.end() )
1504 return;
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 );
1511 break;
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() )
1520 return NULL;
1522 PPDValue aValue;
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 );
1529 return pValue;
1533 * PPDContext
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;
1545 return *this;
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
1571 if( ! m_pParser )
1572 return NULL;
1574 hash_type::const_iterator it;
1575 it = m_aCurrentValues.find( pKey );
1576 if( it != m_aCurrentValues.end() )
1577 return it->second;
1579 if( ! m_pParser->hasKey( pKey ) )
1580 return NULL;
1582 const PPDValue* pValue = pKey->getDefaultValue();
1583 if( ! pValue )
1584 pValue = pKey->getValue( 0 );
1586 return pValue;
1589 const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1591 if( ! m_pParser || ! pKey )
1592 return NULL;
1594 // pValue can be NULL - it means ignore this option
1596 if( ! m_pParser->hasKey( pKey ) )
1597 return NULL;
1599 // check constraints
1600 if( pValue )
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 "
1621 << pKey->getKey()
1622 << " to " << pValue->m_aOption);
1623 resetValue( it->first, true );
1624 it = m_aCurrentValues.begin();
1626 else
1627 ++it;
1631 else
1632 m_aCurrentValues[ pKey ] = NULL;
1634 return pValue;
1637 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1639 if( ! m_pParser || ! pKey || ! pValue )
1640 return false;
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
1647 bool bRet = false;
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 );
1656 return bRet;
1659 bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1661 if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1662 return false;
1664 const PPDValue* pResetValue = pKey->getValue( OUString( "None" ) );
1665 if( ! pResetValue )
1666 pResetValue = pKey->getValue( OUString( "False" ) );
1667 if( ! pResetValue && bDefaultable )
1668 pResetValue = pKey->getDefaultValue();
1670 bool bRet = pResetValue && ( setValue( pKey, pResetValue ) == pResetValue );
1672 return bRet;
1675 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1677 if( ! pNewValue )
1678 return true;
1680 // sanity checks
1681 if( ! m_pParser )
1682 return false;
1684 if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1685 return false;
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() )
1691 return true;
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 ) )
1699 continue;
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 )
1709 continue;
1710 if( pOtherKeyOption == getValue( pOtherKey ) )
1712 return false;
1715 // syntax *Key1 option *Key2 or *Key1 *Key2 option
1716 else if( pOtherKeyOption || pKeyOption )
1718 if( 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 ) )
1730 continue;
1732 return false;
1735 else if( pOtherKeyOption )
1737 if( getValue( pOtherKey ) == pOtherKeyOption &&
1738 ! pNewValue->m_aOption.equalsAscii( "None" ) &&
1739 ! pNewValue->m_aOption.equalsAscii( "False" ) )
1740 return false;
1742 else
1744 // this should not happen, PPD is broken
1747 // syntax *Key1 *Key2
1748 else
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" ) )
1755 return false;
1758 return true;
1761 char* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const
1763 rBytes = 0;
1764 if( ! m_aCurrentValues.size() )
1765 return NULL;
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 ':'
1772 if( it->second )
1774 aCopy = OUStringToOString(it->second->m_aOption, RTL_TEXTENCODING_MS_1252);
1775 rBytes += aCopy.getLength();
1777 else
1778 rBytes += 4;
1779 rBytes += 1; // for '\0'
1781 rBytes += 1;
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 );
1790 pRun += nBytes;
1791 *pRun++ = ':';
1792 if( it->second )
1793 aCopy = OUStringToOString(it->second->m_aOption, RTL_TEXTENCODING_MS_1252);
1794 else
1795 aCopy = "*nil";
1796 nBytes = aCopy.getLength();
1797 memcpy( pRun, aCopy.getStr(), nBytes );
1798 pRun += nBytes;
1800 *pRun++ = 0;
1802 return pBuffer;
1805 void PPDContext::rebuildFromStreamBuffer( char* pBuffer, sal_uLong nBytes )
1807 if( ! m_pParser )
1808 return;
1810 m_aCurrentValues.clear();
1812 char* pRun = pBuffer;
1813 while( nBytes && *pRun )
1815 OString aLine( pRun );
1816 sal_Int32 nPos = aLine.indexOf(':');
1817 if( nPos != -1 )
1819 const PPDKey* pKey = m_pParser->getKey( OStringToOUString( aLine.copy( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
1820 if( pKey )
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>")
1831 << " }");
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
1842 int nDPI = 300;
1843 if( m_pParser )
1845 int nDPIx = 300, nDPIy = 300;
1846 const PPDKey* pKey = m_pParser->getKey( OUString( "Resolution" ) );
1847 if( pKey )
1849 const PPDValue* pValue = getValue( pKey );
1850 if( pValue )
1851 m_pParser->getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
1852 else
1853 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1855 else
1856 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1858 nDPI = (nDPIx > nDPIy) ? nDPIx : nDPIy;
1860 return nDPI;
1863 void PPDContext::getPageSize( OUString& rPaper, int& rWidth, int& rHeight ) const
1865 // initialize to reasonable default, if parser is not set
1866 rPaper = "A4";
1867 rWidth = 595;
1868 rHeight = 842;
1869 if( m_pParser )
1871 const PPDKey* pKey = m_pParser->getKey( OUString( "PageSize" ) );
1872 if( pKey )
1874 const PPDValue* pValue = getValue( pKey );
1875 if( pValue )
1877 rPaper = pValue->m_aOption;
1878 m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
1880 else
1882 rPaper = m_pParser->getDefaultPaperDimension();
1883 m_pParser->getDefaultPaperDimension( rWidth, rHeight );
1889 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */