1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <sal/config.h>
21 #include <config_cpdb.h>
22
23 #include <stdlib.h>
24
25 #include <comphelper/string.hxx>
26 #include <o3tl/string_view.hxx>
27 #include <i18nlangtag/languagetag.hxx>
28 #include <ppdparser.hxx>
29 #include <strhelper.hxx>
30 #include <utility>
31 #include <vcl/svapp.hxx>
32 #include <vcl/settings.hxx>
33
34 #include <unx/helper.hxx>
35 #include <unx/cupsmgr.hxx>
36
37 #if ENABLE_CPDB
38 #include <unx/cpdmgr.hxx>
39 #endif
40
41 #include <tools/urlobj.hxx>
42 #include <tools/stream.hxx>
43 #include <tools/zcodec.hxx>
44 #include <o3tl/safeint.hxx>
45 #include <osl/file.hxx>
46 #include <osl/process.h>
47 #include <osl/thread.h>
48 #include <rtl/strbuf.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <sal/log.hxx>
51 #include <salhelper/linkhelper.hxx>
52
53 #include <com/sun/star/lang/Locale.hpp>
54
55 #include <mutex>
56 #include <unordered_map>
57
58 #ifdef ENABLE_CUPS
59 #include <cups/cups.h>
60 #endif
61
62 #include <config_dbus.h>
63 #include <config_gio.h>
64 #include <o3tl/hash_combine.hxx>
65
66 namespace psp
67 {
68 class PPDTranslator
69 {
70 struct LocaleEqual
71 {
operator ()psp::PPDTranslator::LocaleEqual72 bool operator()(const css::lang::Locale& i_rLeft,
73 const css::lang::Locale& i_rRight) const
74 {
75 return i_rLeft.Language == i_rRight.Language &&
76 i_rLeft.Country == i_rRight.Country &&
77 i_rLeft.Variant == i_rRight.Variant;
78 }
79 };
80
81 struct LocaleHash
82 {
operator ()psp::PPDTranslator::LocaleHash83 size_t operator()(const css::lang::Locale& rLocale) const
84 {
85 std::size_t seed = 0;
86 o3tl::hash_combine(seed, rLocale.Language.hashCode());
87 o3tl::hash_combine(seed, rLocale.Country.hashCode());
88 o3tl::hash_combine(seed, rLocale.Variant.hashCode());
89 return seed;
90 }
91 };
92
93 typedef std::unordered_map< css::lang::Locale, OUString, LocaleHash, LocaleEqual > translation_map;
94 typedef std::unordered_map< OUString, translation_map > key_translation_map;
95
96 key_translation_map m_aTranslations;
97 public:
PPDTranslator()98 PPDTranslator() {}
99
100 void insertValue(
101 std::u16string_view i_rKey,
102 std::u16string_view i_rOption,
103 std::u16string_view i_rValue,
104 const OUString& i_rTranslation,
105 const css::lang::Locale& i_rLocale
106 );
107
insertOption(std::u16string_view i_rKey,std::u16string_view i_rOption,const OUString & i_rTranslation,const css::lang::Locale & i_rLocale)108 void insertOption( std::u16string_view i_rKey,
109 std::u16string_view i_rOption,
110 const OUString& i_rTranslation,
111 const css::lang::Locale& i_rLocale )
112 {
113 insertValue( i_rKey, i_rOption, u"", i_rTranslation, i_rLocale );
114 }
115
insertKey(std::u16string_view i_rKey,const OUString & i_rTranslation,const css::lang::Locale & i_rLocale=css::lang::Locale ())116 void insertKey( std::u16string_view i_rKey,
117 const OUString& i_rTranslation,
118 const css::lang::Locale& i_rLocale = css::lang::Locale() )
119 {
120 insertValue( i_rKey, u"", u"", i_rTranslation, i_rLocale );
121 }
122
123 OUString translateValue(
124 std::u16string_view i_rKey,
125 std::u16string_view i_rOption
126 ) const;
127
translateOption(std::u16string_view i_rKey,std::u16string_view i_rOption) const128 OUString translateOption( std::u16string_view i_rKey,
129 std::u16string_view i_rOption ) const
130 {
131 return translateValue( i_rKey, i_rOption );
132 }
133
translateKey(std::u16string_view i_rKey) const134 OUString translateKey( std::u16string_view i_rKey ) const
135 {
136 return translateValue( i_rKey, u"" );
137 }
138 };
139
normalizeInputLocale(const css::lang::Locale & i_rLocale)140 static css::lang::Locale normalizeInputLocale(
141 const css::lang::Locale& i_rLocale
142 )
143 {
144 css::lang::Locale aLoc( i_rLocale );
145 if( aLoc.Language.isEmpty() )
146 {
147 // empty locale requested, fill in application UI locale
148 aLoc = Application::GetSettings().GetUILanguageTag().getLocale();
149
150 #if OSL_DEBUG_LEVEL > 1
151 static const char* pEnvLocale = getenv( "SAL_PPDPARSER_LOCALE" );
152 if( pEnvLocale && *pEnvLocale )
153 {
154 OString aStr( pEnvLocale );
155 sal_Int32 nLen = aStr.getLength();
156 aLoc.Language = OStringToOUString( aStr.copy( 0, std::min(nLen, 2) ), RTL_TEXTENCODING_MS_1252 );
157 if( nLen >=5 && aStr[2] == '_' )
158 aLoc.Country = OStringToOUString( aStr.copy( 3, 2 ), RTL_TEXTENCODING_MS_1252 );
159 else
160 aLoc.Country.clear();
161 aLoc.Variant.clear();
162 }
163 #endif
164 }
165 /* FIXME-BCP47: using Variant, uppercase? */
166 aLoc.Language = aLoc.Language.toAsciiLowerCase();
167 aLoc.Country = aLoc.Country.toAsciiUpperCase();
168 aLoc.Variant = aLoc.Variant.toAsciiUpperCase();
169
170 return aLoc;
171 }
172
insertValue(std::u16string_view i_rKey,std::u16string_view i_rOption,std::u16string_view i_rValue,const OUString & i_rTranslation,const css::lang::Locale & i_rLocale)173 void PPDTranslator::insertValue(
174 std::u16string_view i_rKey,
175 std::u16string_view i_rOption,
176 std::u16string_view i_rValue,
177 const OUString& i_rTranslation,
178 const css::lang::Locale& i_rLocale
179 )
180 {
181 OUStringBuffer aKey( i_rKey.size() + i_rOption.size() + i_rValue.size() + 2 );
182 aKey.append( i_rKey );
183 if( !i_rOption.empty() || !i_rValue.empty() )
184 {
185 aKey.append( OUString::Concat(":") + i_rOption );
186 }
187 if( !i_rValue.empty() )
188 {
189 aKey.append( OUString::Concat(":") + i_rValue );
190 }
191 if( !aKey.isEmpty() && !i_rTranslation.isEmpty() )
192 {
193 OUString aK( aKey.makeStringAndClear() );
194 css::lang::Locale aLoc;
195 /* FIXME-BCP47: using Variant, uppercase? */
196 aLoc.Language = i_rLocale.Language.toAsciiLowerCase();
197 aLoc.Country = i_rLocale.Country.toAsciiUpperCase();
198 aLoc.Variant = i_rLocale.Variant.toAsciiUpperCase();
199 m_aTranslations[ aK ][ aLoc ] = i_rTranslation;
200 }
201 }
202
translateValue(std::u16string_view i_rKey,std::u16string_view i_rOption) const203 OUString PPDTranslator::translateValue(
204 std::u16string_view i_rKey,
205 std::u16string_view i_rOption
206 ) const
207 {
208 OUString aResult;
209
210 OUStringBuffer aKey( i_rKey.size() + i_rOption.size() + 2 );
211 aKey.append( i_rKey );
212 if( !i_rOption.empty() )
213 {
214 aKey.append( OUString::Concat(":") + i_rOption );
215 }
216 if( !aKey.isEmpty() )
217 {
218 OUString aK( aKey.makeStringAndClear() );
219 key_translation_map::const_iterator it = m_aTranslations.find( aK );
220 if( it != m_aTranslations.end() )
221 {
222 const translation_map& rMap( it->second );
223
224 css::lang::Locale aLoc( normalizeInputLocale( css::lang::Locale() ) );
225 /* FIXME-BCP47: use LanguageTag::getFallbackStrings()? */
226 for( int nTry = 0; nTry < 4; nTry++ )
227 {
228 translation_map::const_iterator tr = rMap.find( aLoc );
229 if( tr != rMap.end() )
230 {
231 aResult = tr->second;
232 break;
233 }
234 switch( nTry )
235 {
236 case 0: aLoc.Variant.clear();break;
237 case 1: aLoc.Country.clear();break;
238 case 2: aLoc.Language.clear();break;
239 }
240 }
241 }
242 }
243 return aResult;
244 }
245
246 class PPDCache
247 {
248 public:
249 std::vector< std::unique_ptr<PPDParser> > aAllParsers;
250 std::optional<std::unordered_map< OUString, OUString >> xAllPPDFiles;
251 };
252 }
253
254 using namespace psp;
255
256 namespace
257 {
getPPDCache()258 PPDCache& getPPDCache()
259 {
260 static PPDCache thePPDCache;
261 return thePPDCache;
262 }
263
264 class PPDDecompressStream
265 {
266 private:
267 PPDDecompressStream(const PPDDecompressStream&) = delete;
268 PPDDecompressStream& operator=(const PPDDecompressStream&) = delete;
269
270 std::unique_ptr<SvFileStream> mpFileStream;
271 std::unique_ptr<SvMemoryStream> mpMemStream;
272 OUString maFileName;
273
274 public:
275 explicit PPDDecompressStream( const OUString& rFile );
276 ~PPDDecompressStream();
277
278 bool IsOpen() const;
279 bool eof() const;
280 OString ReadLine();
281 void Open( const OUString& i_rFile );
282 void Close();
GetFileName() const283 const OUString& GetFileName() const { return maFileName; }
284 };
285
286 }
287
PPDDecompressStream(const OUString & i_rFile)288 PPDDecompressStream::PPDDecompressStream( const OUString& i_rFile )
289 {
290 Open( i_rFile );
291 }
292
~PPDDecompressStream()293 PPDDecompressStream::~PPDDecompressStream()
294 {
295 Close();
296 }
297
Open(const OUString & i_rFile)298 void PPDDecompressStream::Open( const OUString& i_rFile )
299 {
300 Close();
301
302 mpFileStream.reset( new SvFileStream( i_rFile, StreamMode::READ ) );
303 maFileName = mpFileStream->GetFileName();
304
305 if( ! mpFileStream->IsOpen() )
306 {
307 Close();
308 return;
309 }
310
311 OString aLine;
312 mpFileStream->ReadLine( aLine );
313 mpFileStream->Seek( 0 );
314
315 // check for compress'ed or gzip'ed file
316 if( aLine.getLength() <= 1 ||
317 static_cast<unsigned char>(aLine[0]) != 0x1f ||
318 static_cast<unsigned char>(aLine[1]) != 0x8b /* check for gzip */ )
319 return;
320
321 // so let's try to decompress the stream
322 mpMemStream.reset( new SvMemoryStream( 4096, 4096 ) );
323 ZCodec aCodec;
324 aCodec.BeginCompression( ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true );
325 tools::Long nComp = aCodec.Decompress( *mpFileStream, *mpMemStream );
326 aCodec.EndCompression();
327 if( nComp < 0 )
328 {
329 // decompression failed, must be an uncompressed stream after all
330 mpMemStream.reset();
331 mpFileStream->Seek( 0 );
332 }
333 else
334 {
335 // compression successful, can get rid of file stream
336 mpFileStream.reset();
337 mpMemStream->Seek( 0 );
338 }
339 }
340
Close()341 void PPDDecompressStream::Close()
342 {
343 mpMemStream.reset();
344 mpFileStream.reset();
345 }
346
IsOpen() const347 bool PPDDecompressStream::IsOpen() const
348 {
349 return (mpMemStream || (mpFileStream && mpFileStream->IsOpen()));
350 }
351
eof() const352 bool PPDDecompressStream::eof() const
353 {
354 return ( mpMemStream ? mpMemStream->eof() : ( mpFileStream == nullptr || mpFileStream->eof() ) );
355 }
356
ReadLine()357 OString PPDDecompressStream::ReadLine()
358 {
359 OString o_rLine;
360 if( mpMemStream )
361 mpMemStream->ReadLine( o_rLine );
362 else if( mpFileStream )
363 mpFileStream->ReadLine( o_rLine );
364 return o_rLine;
365 }
366
resolveLink(const OUString & i_rURL,OUString & o_rResolvedURL,OUString & o_rBaseName,osl::FileStatus::Type & o_rType)367 static osl::FileBase::RC resolveLink( const OUString& i_rURL, OUString& o_rResolvedURL, OUString& o_rBaseName, osl::FileStatus::Type& o_rType)
368 {
369 salhelper::LinkResolver aResolver(osl_FileStatus_Mask_FileName |
370 osl_FileStatus_Mask_Type |
371 osl_FileStatus_Mask_FileURL);
372
373 osl::FileBase::RC aRet = aResolver.fetchFileStatus(i_rURL, 10/*nLinkLevel*/);
374
375 if (aRet == osl::FileBase::E_None)
376 {
377 o_rResolvedURL = aResolver.m_aStatus.getFileURL();
378 o_rBaseName = aResolver.m_aStatus.getFileName();
379 o_rType = aResolver.m_aStatus.getFileType();
380 }
381
382 return aRet;
383 }
384
scanPPDDir(const OUString & rDir)385 void PPDParser::scanPPDDir( const OUString& rDir )
386 {
387 static struct suffix_t
388 {
389 const char* pSuffix;
390 const sal_Int32 nSuffixLen;
391 } const pSuffixes[] =
392 { { ".PS", 3 }, { ".PPD", 4 }, { ".PS.GZ", 6 }, { ".PPD.GZ", 7 } };
393
394 PPDCache &rPPDCache = getPPDCache();
395
396 osl::Directory aDir( rDir );
397 if ( aDir.open() != osl::FileBase::E_None )
398 return;
399
400 osl::DirectoryItem aItem;
401
402 INetURLObject aPPDDir(rDir);
403 while( aDir.getNextItem( aItem ) == osl::FileBase::E_None )
404 {
405 osl::FileStatus aStatus( osl_FileStatus_Mask_FileName );
406 if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None )
407 {
408 OUString aFileURL, aFileName;
409 osl::FileStatus::Type eType = osl::FileStatus::Unknown;
410 OUString aURL = rDir + "/" + aStatus.getFileName();
411
412 if(resolveLink( aURL, aFileURL, aFileName, eType ) == osl::FileBase::E_None)
413 {
414 if( eType == osl::FileStatus::Regular )
415 {
416 INetURLObject aPPDFile = aPPDDir;
417 aPPDFile.Append( aFileName );
418
419 // match extension
420 for(const suffix_t & rSuffix : pSuffixes)
421 {
422 if( aFileName.getLength() > rSuffix.nSuffixLen )
423 {
424 if( aFileName.endsWithIgnoreAsciiCaseAsciiL( rSuffix.pSuffix, rSuffix.nSuffixLen ) )
425 {
426 (*rPPDCache.xAllPPDFiles)[ aFileName.copy( 0, aFileName.getLength() - rSuffix.nSuffixLen ) ] = aPPDFile.PathToFileName();
427 break;
428 }
429 }
430 }
431 }
432 else if( eType == osl::FileStatus::Directory )
433 {
434 scanPPDDir( aFileURL );
435 }
436 }
437 }
438 }
439 aDir.close();
440 }
441
initPPDFiles(PPDCache & rPPDCache)442 void PPDParser::initPPDFiles(PPDCache &rPPDCache)
443 {
444 if( rPPDCache.xAllPPDFiles )
445 return;
446
447 rPPDCache.xAllPPDFiles.emplace();
448
449 // check installation directories
450 std::vector< OUString > aPathList;
451 psp::getPrinterPathList( aPathList, PRINTER_PPDDIR );
452 for (auto const& path : aPathList)
453 {
454 INetURLObject aPPDDir( path, INetProtocol::File, INetURLObject::EncodeMechanism::All );
455 scanPPDDir( aPPDDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
456 }
457 if( rPPDCache.xAllPPDFiles->find( u"SGENPRT"_ustr ) != rPPDCache.xAllPPDFiles->end() )
458 return;
459
460 // last try: search in directory of executable (mainly for setup)
461 OUString aExe;
462 if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
463 {
464 INetURLObject aDir( aExe );
465 aDir.removeSegment();
466 SAL_INFO("vcl.unx.print", "scanning last chance dir: "
467 << aDir.GetMainURL(INetURLObject::DecodeMechanism::NONE));
468 scanPPDDir( aDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
469 SAL_INFO("vcl.unx.print", "SGENPRT "
470 << (rPPDCache.xAllPPDFiles->find(u"SGENPRT"_ustr) ==
471 rPPDCache.xAllPPDFiles->end() ? "not found" : "found"));
472 }
473 }
474
getPPDFile(const OUString & rFile)475 OUString PPDParser::getPPDFile( const OUString& rFile )
476 {
477 INetURLObject aPPD( rFile, INetProtocol::File, INetURLObject::EncodeMechanism::All );
478 // someone might enter a full qualified name here
479 PPDDecompressStream aStream( aPPD.PathToFileName() );
480 if( ! aStream.IsOpen() )
481 {
482 std::unordered_map< OUString, OUString >::const_iterator it;
483 PPDCache &rPPDCache = getPPDCache();
484
485 bool bRetry = true;
486 do
487 {
488 initPPDFiles(rPPDCache);
489 // some PPD files contain dots beside the extension, so try name first
490 // and cut of points after that
491 OUString aBase( rFile );
492 sal_Int32 nLastIndex = aBase.lastIndexOf( '/' );
493 if( nLastIndex >= 0 )
494 aBase = aBase.copy( nLastIndex+1 );
495 do
496 {
497 it = rPPDCache.xAllPPDFiles->find( aBase );
498 nLastIndex = aBase.lastIndexOf( '.' );
499 if( nLastIndex > 0 )
500 aBase = aBase.copy( 0, nLastIndex );
501 } while( it == rPPDCache.xAllPPDFiles->end() && nLastIndex > 0 );
502
503 if( it == rPPDCache.xAllPPDFiles->end() && bRetry )
504 {
505 // a new file ? rehash
506 rPPDCache.xAllPPDFiles.reset();
507 bRetry = false;
508 // note this is optimized for office start where
509 // no new files occur and initPPDFiles is called only once
510 }
511 } while( ! rPPDCache.xAllPPDFiles );
512
513 if( it != rPPDCache.xAllPPDFiles->end() )
514 aStream.Open( it->second );
515 }
516
517 OUString aRet;
518 if( aStream.IsOpen() )
519 {
520 OString aLine = aStream.ReadLine();
521 if (aLine.startsWith("*PPD-Adobe"))
522 aRet = aStream.GetFileName();
523 else
524 {
525 // our *Include hack does usually not begin
526 // with *PPD-Adobe, so try some lines for *Include
527 int nLines = 10;
528 while (aLine.indexOf("*Include") != 0 && --nLines)
529 aLine = aStream.ReadLine();
530 if( nLines )
531 aRet = aStream.GetFileName();
532 }
533 }
534
535 return aRet;
536 }
537
getParser(const OUString & rFile)538 const PPDParser* PPDParser::getParser( const OUString& rFile )
539 {
540 // Recursive because we can get re-entered via CUPSManager::createCUPSParser
541 static std::recursive_mutex aMutex;
542 std::scoped_lock aGuard( aMutex );
543
544 OUString aFile = rFile;
545 if( !rFile.startsWith( "CUPS:" ) && !rFile.startsWith( "CPD:" ) )
546 aFile = getPPDFile( rFile );
547 if( aFile.isEmpty() )
548 {
549 SAL_INFO("vcl.unx.print", "Could not get printer PPD file \""
550 << rFile << "\" !");
551 return nullptr;
552 }
553 else
554 SAL_INFO("vcl.unx.print", "Parsing printer info from \""
555 << rFile << "\" !");
556
557
558 PPDCache &rPPDCache = getPPDCache();
559 for( auto const & i : rPPDCache.aAllParsers )
560 if( i->m_aFile == aFile )
561 return i.get();
562
563 PPDParser* pNewParser = nullptr;
564 if( !aFile.startsWith( "CUPS:" ) && !aFile.startsWith( "CPD:" ) )
565 pNewParser = new PPDParser( aFile );
566 else
567 {
568 PrinterInfoManager& rMgr = PrinterInfoManager::get();
569 if( rMgr.getType() == PrinterInfoManager::Type::CUPS )
570 {
571 #ifdef ENABLE_CUPS
572 pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
573 #endif
574 } else if ( rMgr.getType() == PrinterInfoManager::Type::CPD )
575 {
576 #if ENABLE_CPDB
577 pNewParser = const_cast<PPDParser*>(static_cast<CPDManager&>(rMgr).createCPDParser( aFile ));
578 #endif
579 }
580 }
581 if( pNewParser )
582 {
583 // this may actually be the SGENPRT parser,
584 // so ensure uniqueness here (but don't remove last we delete us!)
585 if (std::none_of(
586 rPPDCache.aAllParsers.begin(),
587 rPPDCache.aAllParsers.end(),
588 [pNewParser] (std::unique_ptr<PPDParser> const & x) { return x.get() == pNewParser; } ))
589 {
590 // insert new parser to vector
591 rPPDCache.aAllParsers.emplace_back(pNewParser);
592 }
593 }
594 return pNewParser;
595 }
596
PPDParser(OUString aFile,const std::vector<PPDKey * > & keys)597 PPDParser::PPDParser(OUString aFile, const std::vector<PPDKey*>& keys)
598 : m_aFile(std::move(aFile))
599 , m_aFileEncoding(RTL_TEXTENCODING_MS_1252)
600 , m_pImageableAreas(nullptr)
601 , m_pDefaultPaperDimension(nullptr)
602 , m_pPaperDimensions(nullptr)
603 , m_pDefaultInputSlot(nullptr)
604 , m_pDefaultResolution(nullptr)
605 , m_pTranslator(new PPDTranslator())
606 {
607 for (auto & key: keys)
608 {
609 insertKey( std::unique_ptr<PPDKey>(key) );
610 }
611
612 // fill in shortcuts
613 const PPDKey* pKey;
614
615 pKey = getKey( u"PageSize"_ustr );
616
617 if ( pKey ) {
618 std::unique_ptr<PPDKey> pImageableAreas(new PPDKey(u"ImageableArea"_ustr));
619 std::unique_ptr<PPDKey> pPaperDimensions(new PPDKey(u"PaperDimension"_ustr));
620 #if defined(CUPS_VERSION_MAJOR)
621 #if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 7) || CUPS_VERSION_MAJOR > 1
622 for (int i = 0; i < pKey->countValues(); i++) {
623 const PPDValue* pValue = pKey -> getValue(i);
624 OUString aValueName = pValue -> m_aOption;
625 PPDValue* pImageableAreaValue = pImageableAreas -> insertValue( aValueName, eQuoted );
626 PPDValue* pPaperDimensionValue = pPaperDimensions -> insertValue( aValueName, eQuoted );
627 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
628 OString o = OUStringToOString( aValueName, aEncoding );
629 pwg_media_t *pPWGMedia = pwgMediaForPWG(o.pData->buffer);
630 if (pPWGMedia != nullptr) {
631 OUStringBuffer aBuf( 256 );
632 aBuf = "0 0 " +
633 OUString::number(PWG_TO_POINTS(pPWGMedia -> width)) +
634 " " +
635 OUString::number(PWG_TO_POINTS(pPWGMedia -> length));
636 if ( pImageableAreaValue )
637 pImageableAreaValue->m_aValue = aBuf.makeStringAndClear();
638 aBuf.append( OUString::number(PWG_TO_POINTS(pPWGMedia -> width))
639 + " "
640 + OUString::number(PWG_TO_POINTS(pPWGMedia -> length) ));
641 if ( pPaperDimensionValue )
642 pPaperDimensionValue->m_aValue = aBuf.makeStringAndClear();
643 if (aValueName.equals(pKey -> getDefaultValue() -> m_aOption)) {
644 pImageableAreas -> m_pDefaultValue = pImageableAreaValue;
645 pPaperDimensions -> m_pDefaultValue = pPaperDimensionValue;
646 }
647 }
648 }
649 #endif // HAVE_CUPS_API_1_7
650 #endif
651 insertKey(std::move(pImageableAreas));
652 insertKey(std::move(pPaperDimensions));
653 }
654
655 m_pImageableAreas = getKey( u"ImageableArea"_ustr );
656 const PPDValue* pDefaultImageableArea = nullptr;
657 if( m_pImageableAreas )
658 pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
659 if (m_pImageableAreas == nullptr) {
660 SAL_WARN( "vcl.unx.print", "no ImageableArea in " << m_aFile);
661 }
662 if (pDefaultImageableArea == nullptr) {
663 SAL_WARN( "vcl.unx.print", "no DefaultImageableArea in " << m_aFile);
664 }
665
666 m_pPaperDimensions = getKey( u"PaperDimension"_ustr );
667 if( m_pPaperDimensions )
668 m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
669 if (m_pPaperDimensions == nullptr) {
670 SAL_WARN( "vcl.unx.print", "no PaperDimensions in " << m_aFile);
671 }
672 if (m_pDefaultPaperDimension == nullptr) {
673 SAL_WARN( "vcl.unx.print", "no DefaultPaperDimensions in " << m_aFile);
674 }
675
676 auto pResolutions = getKey( u"Resolution"_ustr );
677 if( pResolutions )
678 m_pDefaultResolution = pResolutions->getDefaultValue();
679 if (pResolutions == nullptr) {
680 SAL_INFO( "vcl.unx.print", "no Resolution in " << m_aFile);
681 }
682 SAL_INFO_IF(!m_pDefaultResolution, "vcl.unx.print", "no DefaultResolution in " + m_aFile);
683
684 auto pInputSlots = getKey( u"InputSlot"_ustr );
685 if( pInputSlots )
686 m_pDefaultInputSlot = pInputSlots->getDefaultValue();
687 SAL_INFO_IF(!pInputSlots, "vcl.unx.print", "no InputSlot in " << m_aFile);
688 SAL_INFO_IF(!m_pDefaultInputSlot, "vcl.unx.print", "no DefaultInputSlot in " << m_aFile);
689 }
690
PPDParser(OUString aFile)691 PPDParser::PPDParser( OUString aFile ) :
692 m_aFile(std::move( aFile )),
693 m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ),
694 m_pImageableAreas( nullptr ),
695 m_pDefaultPaperDimension( nullptr ),
696 m_pPaperDimensions( nullptr ),
697 m_pDefaultInputSlot( nullptr ),
698 m_pDefaultResolution( nullptr ),
699 m_pTranslator( new PPDTranslator() )
700 {
701 // read in the file
702 std::vector< OString > aLines;
703 PPDDecompressStream aStream( m_aFile );
704 if( aStream.IsOpen() )
705 {
706 bool bLanguageEncoding = false;
707 while( ! aStream.eof() )
708 {
709 OString aCurLine = aStream.ReadLine();
710 if( aCurLine.startsWith("*") )
711 {
712 if (aCurLine.matchIgnoreAsciiCase("*include:"))
713 {
714 aCurLine = aCurLine.copy(9);
715 aCurLine = comphelper::string::strip(aCurLine, ' ');
716 aCurLine = comphelper::string::strip(aCurLine, '\t');
717 aCurLine = comphelper::string::stripEnd(aCurLine, '\r');
718 aCurLine = comphelper::string::stripEnd(aCurLine, '\n');
719 aCurLine = comphelper::string::strip(aCurLine, '"');
720 aStream.Close();
721 aStream.Open(getPPDFile(OStringToOUString(aCurLine, m_aFileEncoding)));
722 continue;
723 }
724 else if( ! bLanguageEncoding &&
725 aCurLine.matchIgnoreAsciiCase("*languageencoding") )
726 {
727 bLanguageEncoding = true; // generally only the first one counts
728 OString aLower = aCurLine.toAsciiLowerCase();
729 if( aLower.indexOf("isolatin1", 17 ) != -1 ||
730 aLower.indexOf("windowsansi", 17 ) != -1 )
731 m_aFileEncoding = RTL_TEXTENCODING_MS_1252;
732 else if( aLower.indexOf("isolatin2", 17 ) != -1 )
733 m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2;
734 else if( aLower.indexOf("isolatin5", 17 ) != -1 )
735 m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5;
736 else if( aLower.indexOf("jis83-rksj", 17 ) != -1 )
737 m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS;
738 else if( aLower.indexOf("macstandard", 17 ) != -1 )
739 m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
740 else if( aLower.indexOf("utf-8", 17 ) != -1 )
741 m_aFileEncoding = RTL_TEXTENCODING_UTF8;
742 }
743 }
744 aLines.push_back( aCurLine );
745 }
746 }
747 aStream.Close();
748
749 // now get the Values
750 parse( aLines );
751 #if OSL_DEBUG_LEVEL > 1
752 SAL_INFO("vcl.unx.print", "acquired " << m_aKeys.size()
753 << " Keys from PPD " << m_aFile << ":");
754 for (auto const& key : m_aKeys)
755 {
756 const PPDKey* pKey = key.second.get();
757 char const* pSetupType = "<unknown>";
758 switch( pKey->m_eSetupType )
759 {
760 case PPDKey::SetupType::ExitServer: pSetupType = "ExitServer";break;
761 case PPDKey::SetupType::Prolog: pSetupType = "Prolog";break;
762 case PPDKey::SetupType::DocumentSetup: pSetupType = "DocumentSetup";break;
763 case PPDKey::SetupType::PageSetup: pSetupType = "PageSetup";break;
764 case PPDKey::SetupType::JCLSetup: pSetupType = "JCLSetup";break;
765 case PPDKey::SetupType::AnySetup: pSetupType = "AnySetup";break;
766 default: break;
767 }
768 SAL_INFO("vcl.unx.print", "\t\"" << pKey->getKey() << "\" ("
769 << pKey->countValues() << "values) OrderDependency: "
770 << pKey->m_nOrderDependency << pSetupType );
771 for( int j = 0; j < pKey->countValues(); j++ )
772 {
773 const PPDValue* pValue = pKey->getValue( j );
774 char const* pVType = "<unknown>";
775 switch( pValue->m_eType )
776 {
777 case eInvocation: pVType = "invocation";break;
778 case eQuoted: pVType = "quoted";break;
779 case eString: pVType = "string";break;
780 case eSymbol: pVType = "symbol";break;
781 case eNo: pVType = "no";break;
782 default: break;
783 }
784 SAL_INFO("vcl.unx.print", "\t\t"
785 << (pValue == pKey->m_pDefaultValue ? "(Default:) " : "")
786 << "option: \"" << pValue->m_aOption
787 << "\", value: type " << pVType << " \""
788 << pValue->m_aValue << "\"");
789 }
790 }
791 SAL_INFO("vcl.unx.print",
792 "constraints: (" << m_aConstraints.size() << " found)");
793 for (auto const& constraint : m_aConstraints)
794 {
795 SAL_INFO("vcl.unx.print", "*\"" << constraint.m_pKey1->getKey() << "\" \""
796 << (constraint.m_pOption1 ? constraint.m_pOption1->m_aOption : "<nil>")
797 << "\" *\"" << constraint.m_pKey2->getKey() << "\" \""
798 << (constraint.m_pOption2 ? constraint.m_pOption2->m_aOption : "<nil>")
799 << "\"");
800 }
801 #endif
802
803 m_pImageableAreas = getKey( u"ImageableArea"_ustr );
804 const PPDValue * pDefaultImageableArea = nullptr;
805 if( m_pImageableAreas )
806 pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
807 if (m_pImageableAreas == nullptr) {
808 SAL_WARN( "vcl.unx.print", "no ImageableArea in " << m_aFile);
809 }
810 if (pDefaultImageableArea == nullptr) {
811 SAL_WARN( "vcl.unx.print", "no DefaultImageableArea in " << m_aFile);
812 }
813
814 m_pPaperDimensions = getKey( u"PaperDimension"_ustr );
815 if( m_pPaperDimensions )
816 m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
817 if (m_pPaperDimensions == nullptr) {
818 SAL_WARN( "vcl.unx.print", "no PaperDimensions in " << m_aFile);
819 }
820 if (m_pDefaultPaperDimension == nullptr) {
821 SAL_WARN( "vcl.unx.print", "no DefaultPaperDimensions in " << m_aFile);
822 }
823
824 auto pResolutions = getKey( u"Resolution"_ustr );
825 if( pResolutions )
826 m_pDefaultResolution = pResolutions->getDefaultValue();
827 if (pResolutions == nullptr) {
828 SAL_INFO( "vcl.unx.print", "no Resolution in " << m_aFile);
829 }
830 SAL_INFO_IF(!m_pDefaultResolution, "vcl.unx.print", "no DefaultResolution in " + m_aFile);
831
832 auto pInputSlots = getKey( u"InputSlot"_ustr );
833 if( pInputSlots )
834 m_pDefaultInputSlot = pInputSlots->getDefaultValue();
835 SAL_INFO_IF(!pInputSlots, "vcl.unx.print", "no InputSlot in " << m_aFile);
836 SAL_INFO_IF(!m_pDefaultInputSlot, "vcl.unx.print", "no DefaultInputSlot in " << m_aFile);
837 }
838
~PPDParser()839 PPDParser::~PPDParser()
840 {
841 m_pTranslator.reset();
842 }
843
insertKey(std::unique_ptr<PPDKey> pKey)844 void PPDParser::insertKey( std::unique_ptr<PPDKey> pKey )
845 {
846 m_aOrderedKeys.push_back( pKey.get() );
847 m_aKeys[ pKey->getKey() ] = std::move(pKey);
848 }
849
getKey(int n) const850 const PPDKey* PPDParser::getKey( int n ) const
851 {
852 return (n >= 0 && o3tl::make_unsigned(n) < m_aOrderedKeys.size()) ? m_aOrderedKeys[n] : nullptr;
853 }
854
getKey(const OUString & rKey) const855 const PPDKey* PPDParser::getKey( const OUString& rKey ) const
856 {
857 PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey );
858 return it != m_aKeys.end() ? it->second.get() : nullptr;
859 }
860
hasKey(const PPDKey * pKey) const861 bool PPDParser::hasKey( const PPDKey* pKey ) const
862 {
863 return pKey && ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() );
864 }
865
getNibble(char cChar)866 static sal_uInt8 getNibble( char cChar )
867 {
868 sal_uInt8 nRet = 0;
869 if( cChar >= '0' && cChar <= '9' )
870 nRet = sal_uInt8( cChar - '0' );
871 else if( cChar >= 'A' && cChar <= 'F' )
872 nRet = 10 + sal_uInt8( cChar - 'A' );
873 else if( cChar >= 'a' && cChar <= 'f' )
874 nRet = 10 + sal_uInt8( cChar - 'a' );
875 return nRet;
876 }
877
handleTranslation(const OString & i_rString,bool bIsGlobalized)878 OUString PPDParser::handleTranslation(const OString& i_rString, bool bIsGlobalized)
879 {
880 sal_Int32 nOrigLen = i_rString.getLength();
881 OStringBuffer aTrans( nOrigLen );
882 const char* pStr = i_rString.getStr();
883 const char* pEnd = pStr + nOrigLen;
884 while( pStr < pEnd )
885 {
886 if( *pStr == '<' )
887 {
888 pStr++;
889 char cChar;
890 while( *pStr != '>' && pStr < pEnd-1 )
891 {
892 cChar = getNibble( *pStr++ ) << 4;
893 cChar |= getNibble( *pStr++ );
894 aTrans.append( cChar );
895 }
896 pStr++;
897 }
898 else
899 aTrans.append( *pStr++ );
900 }
901 return OStringToOUString( aTrans, bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding );
902 }
903
904 namespace
905 {
oddDoubleQuoteCount(OStringBuffer & rBuffer)906 bool oddDoubleQuoteCount(OStringBuffer &rBuffer)
907 {
908 bool bHasOddCount = false;
909 for (sal_Int32 i = 0; i < rBuffer.getLength(); ++i)
910 {
911 if (rBuffer[i] == '"')
912 bHasOddCount = !bHasOddCount;
913 }
914 return bHasOddCount;
915 }
916 }
917
parse(::std::vector<OString> & rLines)918 void PPDParser::parse( ::std::vector< OString >& rLines )
919 {
920 // Name for PPD group into which all options are put for which the PPD
921 // does not explicitly define a group.
922 // This is similar to how CUPS handles it,
923 // s. Sweet, Michael R. (2001): Common UNIX Printing System, p. 251:
924 // "Each option in turn is associated with a group stored in the
925 // ppd_group_t structure. Groups can be specified in the PPD file; if an
926 // option is not associated with a group, it is put in a "General" or
927 // "Extra" group depending on the option.
928 static constexpr OString aDefaultPPDGroupName("General"_ostr);
929
930 std::vector< OString >::iterator line = rLines.begin();
931 PPDParser::hash_type::const_iterator keyit;
932
933 // name of the PPD group that is currently being processed
934 OString aCurrentGroup = aDefaultPPDGroupName;
935
936 while( line != rLines.end() )
937 {
938 OString aCurrentLine( *line );
939 ++line;
940
941 SAL_INFO("vcl.unx.print", "Parse line '" << aCurrentLine << "'");
942
943 if (aCurrentLine.getLength() < 2 || aCurrentLine[0] != '*')
944 continue;
945 if( aCurrentLine[1] == '%' )
946 continue;
947
948 OString aKey = GetCommandLineToken( 0, aCurrentLine.getToken(0, ':') );
949 sal_Int32 nPos = aKey.indexOf('/');
950 if (nPos != -1)
951 aKey = aKey.copy(0, nPos);
952 if(!aKey.isEmpty())
953 {
954 aKey = aKey.copy(1); // remove the '*'
955 }
956 if(aKey.isEmpty())
957 {
958 continue;
959 }
960
961 if (aKey == "CloseGroup")
962 {
963 aCurrentGroup = aDefaultPPDGroupName;
964 continue;
965 }
966 if (aKey == "OpenGroup")
967 {
968 OString aGroupName = aCurrentLine;
969 sal_Int32 nPosition = aGroupName.indexOf('/');
970 if (nPosition != -1)
971 {
972 aGroupName = aGroupName.copy(0, nPosition);
973 }
974
975 aCurrentGroup = GetCommandLineToken(1, aGroupName);
976 continue;
977 }
978 if ((aKey == "CloseUI") ||
979 (aKey == "JCLCloseUI") ||
980 (aKey == "End") ||
981 (aKey == "JCLEnd") ||
982 (aKey == "OpenSubGroup") ||
983 (aKey == "CloseSubGroup"))
984 {
985 continue;
986 }
987
988 if ((aKey == "OpenUI") || (aKey == "JCLOpenUI"))
989 {
990 parseOpenUI( aCurrentLine, aCurrentGroup);
991 continue;
992 }
993 else if (aKey == "OrderDependency")
994 {
995 parseOrderDependency( aCurrentLine );
996 continue;
997 }
998 else if (aKey == "UIConstraints" ||
999 aKey == "NonUIConstraints")
1000 {
1001 continue; // parsed in pass 2
1002 }
1003 else if( aKey == "CustomPageSize" ) // currently not handled
1004 continue;
1005 else if (aKey.startsWith("Custom", &aKey) )
1006 {
1007 //fdo#43049 very basic support for Custom entries, we ignore the
1008 //validation params and types
1009 OUString aUniKey(OStringToOUString(aKey, RTL_TEXTENCODING_MS_1252));
1010 keyit = m_aKeys.find( aUniKey );
1011 if(keyit != m_aKeys.end())
1012 {
1013 PPDKey* pKey = keyit->second.get();
1014 pKey->insertValue(u"Custom"_ustr, eInvocation, true);
1015 }
1016 continue;
1017 }
1018
1019 // default values are parsed in pass 2
1020 if (aKey.startsWith("Default"))
1021 continue;
1022
1023 bool bQuery = false;
1024 if (aKey[0] == '?')
1025 {
1026 aKey = aKey.copy(1);
1027 bQuery = true;
1028 }
1029
1030 OUString aUniKey(OStringToOUString(aKey, RTL_TEXTENCODING_MS_1252));
1031 // handle CUPS extension for globalized PPDs
1032 /* FIXME-BCP47: really only ISO 639-1 two character language codes?
1033 * goodnight... */
1034 bool bIsGlobalizedLine = false;
1035 css::lang::Locale aTransLocale;
1036 if( ( aUniKey.getLength() > 3 && aUniKey[ 2 ] == '.' ) ||
1037 ( aUniKey.getLength() > 5 && aUniKey[ 2 ] == '_' && aUniKey[ 5 ] == '.' ) )
1038 {
1039 if( aUniKey[ 2 ] == '.' )
1040 {
1041 aTransLocale.Language = aUniKey.copy( 0, 2 );
1042 aUniKey = aUniKey.copy( 3 );
1043 }
1044 else
1045 {
1046 aTransLocale.Language = aUniKey.copy( 0, 2 );
1047 aTransLocale.Country = aUniKey.copy( 3, 2 );
1048 aUniKey = aUniKey.copy( 6 );
1049 }
1050 bIsGlobalizedLine = true;
1051 }
1052
1053 OUString aOption;
1054 nPos = aCurrentLine.indexOf(':');
1055 if( nPos != -1 )
1056 {
1057 aOption = OStringToOUString(
1058 aCurrentLine.subView( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 );
1059 aOption = GetCommandLineToken( 1, aOption );
1060 sal_Int32 nTransPos = aOption.indexOf( '/' );
1061 if( nTransPos != -1 )
1062 aOption = aOption.copy(0, nTransPos);
1063 }
1064
1065 PPDValueType eType = eNo;
1066 OUString aValue;
1067 OUString aOptionTranslation;
1068 OUString aValueTranslation;
1069 if( nPos != -1 )
1070 {
1071 // found a colon, there may be an option
1072 OString aLine = aCurrentLine.copy( 1, nPos-1 );
1073 aLine = WhitespaceToSpace( aLine );
1074 sal_Int32 nTransPos = aLine.indexOf('/');
1075 if (nTransPos != -1)
1076 aOptionTranslation = handleTranslation( aLine.copy(nTransPos+1), bIsGlobalizedLine );
1077
1078 // read in more lines if necessary for multiline values
1079 aLine = aCurrentLine.copy( nPos+1 );
1080 if (!aLine.isEmpty())
1081 {
1082 OStringBuffer aBuffer(aLine);
1083 while (line != rLines.end() && oddDoubleQuoteCount(aBuffer))
1084 {
1085 // copy the newlines also
1086 aBuffer.append("\n" + *line);
1087 ++line;
1088 }
1089 aLine = aBuffer.makeStringAndClear();
1090 }
1091 aLine = WhitespaceToSpace( aLine );
1092
1093 // #i100644# handle a missing value (actually a broken PPD)
1094 if( aLine.isEmpty() )
1095 {
1096 if( !aOption.isEmpty() &&
1097 !aUniKey.startsWith( "JCL" ) )
1098 eType = eInvocation;
1099 else
1100 eType = eQuoted;
1101 }
1102 // check for invocation or quoted value
1103 else if(aLine[0] == '"')
1104 {
1105 aLine = aLine.copy(1);
1106 nTransPos = aLine.indexOf('"');
1107 if (nTransPos == -1)
1108 nTransPos = aLine.getLength();
1109 aValue = OStringToOUString(aLine.subView(0, nTransPos), RTL_TEXTENCODING_MS_1252);
1110 // after the second doublequote can follow a / and a translation
1111 if (nTransPos < aLine.getLength() - 2)
1112 {
1113 aValueTranslation = handleTranslation( aLine.copy( nTransPos+2 ), bIsGlobalizedLine );
1114 }
1115 // check for quoted value
1116 if( !aOption.isEmpty() &&
1117 !aUniKey.startsWith( "JCL" ) )
1118 eType = eInvocation;
1119 else
1120 eType = eQuoted;
1121 }
1122 // check for symbol value
1123 else if(aLine[0] == '^')
1124 {
1125 aLine = aLine.copy(1);
1126 aValue = OStringToOUString(aLine, RTL_TEXTENCODING_MS_1252);
1127 eType = eSymbol;
1128 }
1129 else
1130 {
1131 // must be a string value then
1132 // strictly this is false because string values
1133 // can contain any whitespace which is reduced
1134 // to one space by now
1135 // who cares ...
1136 nTransPos = aLine.indexOf('/');
1137 if (nTransPos == -1)
1138 nTransPos = aLine.getLength();
1139 aValue = OStringToOUString(aLine.subView(0, nTransPos), RTL_TEXTENCODING_MS_1252);
1140 if (nTransPos+1 < aLine.getLength())
1141 aValueTranslation = handleTranslation( aLine.copy( nTransPos+1 ), bIsGlobalizedLine );
1142 eType = eString;
1143 }
1144 }
1145
1146 // handle globalized PPD entries
1147 if( bIsGlobalizedLine )
1148 {
1149 // handle main key translations of form:
1150 // *ll_CC.Translation MainKeyword/translated text: ""
1151 if( aUniKey == "Translation" )
1152 {
1153 m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale );
1154 }
1155 // handle options translations of for:
1156 // *ll_CC.MainKeyword OptionKeyword/translated text: ""
1157 else
1158 {
1159 m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1160 }
1161 continue;
1162 }
1163
1164 PPDKey* pKey = nullptr;
1165 keyit = m_aKeys.find( aUniKey );
1166 if( keyit == m_aKeys.end() )
1167 {
1168 pKey = new PPDKey( aUniKey );
1169 insertKey( std::unique_ptr<PPDKey>(pKey) );
1170 }
1171 else
1172 pKey = keyit->second.get();
1173
1174 if( eType == eNo && bQuery )
1175 continue;
1176
1177 PPDValue* pValue = pKey->insertValue( aOption, eType );
1178 if( ! pValue )
1179 continue;
1180 pValue->m_aValue = aValue;
1181
1182 if( !aOptionTranslation.isEmpty() )
1183 m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1184 if( !aValueTranslation.isEmpty() )
1185 m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale );
1186
1187 // eventually update query and remove from option list
1188 if( bQuery && !pKey->m_bQueryValue )
1189 {
1190 pKey->m_bQueryValue = true;
1191 pKey->eraseValue( pValue->m_aOption );
1192 }
1193 }
1194
1195 // second pass: fill in defaults
1196 for( const auto& aLine : rLines )
1197 {
1198 if (aLine.startsWith("*Default"))
1199 {
1200 SAL_INFO("vcl.unx.print", "Found a default: '" << aLine << "'");
1201 OUString aKey(OStringToOUString(aLine.subView(8), RTL_TEXTENCODING_MS_1252));
1202 sal_Int32 nPos = aKey.indexOf( ':' );
1203 if( nPos != -1 )
1204 {
1205 aKey = aKey.copy(0, nPos);
1206 OUString aOption(OStringToOUString(
1207 WhitespaceToSpace(aLine.subView(nPos+9)),
1208 RTL_TEXTENCODING_MS_1252));
1209 keyit = m_aKeys.find( aKey );
1210 if( keyit != m_aKeys.end() )
1211 {
1212 PPDKey* pKey = keyit->second.get();
1213 const PPDValue* pDefValue = pKey->getValue( aOption );
1214 if( pKey->m_pDefaultValue == nullptr )
1215 pKey->m_pDefaultValue = pDefValue;
1216 }
1217 else
1218 {
1219 // some PPDs contain defaults for keys that
1220 // do not exist otherwise
1221 // (example: DefaultResolution)
1222 // so invent that key here and have a default value
1223 std::unique_ptr<PPDKey> pKey(new PPDKey( aKey ));
1224 pKey->insertValue( aOption, eInvocation /*or what ?*/ );
1225 pKey->m_pDefaultValue = pKey->getValue( aOption );
1226 insertKey( std::move(pKey) );
1227 }
1228 }
1229 }
1230 else if (aLine.startsWith("*UIConstraints") ||
1231 aLine.startsWith("*NonUIConstraints"))
1232 {
1233 parseConstraint( aLine );
1234 }
1235 }
1236 }
1237
parseOpenUI(const OString & rLine,std::string_view rPPDGroup)1238 void PPDParser::parseOpenUI(const OString& rLine, std::string_view rPPDGroup)
1239 {
1240 OUString aTranslation;
1241 OString aKey = rLine;
1242
1243 sal_Int32 nPos = aKey.indexOf(':');
1244 if( nPos != -1 )
1245 aKey = aKey.copy(0, nPos);
1246 nPos = aKey.indexOf('/');
1247 if( nPos != -1 )
1248 {
1249 aTranslation = handleTranslation( aKey.copy( nPos + 1 ), false );
1250 aKey = aKey.copy(0, nPos);
1251 }
1252 aKey = GetCommandLineToken( 1, aKey );
1253 aKey = aKey.copy(1);
1254
1255 OUString aUniKey(OStringToOUString(aKey, RTL_TEXTENCODING_MS_1252));
1256 PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey );
1257 PPDKey* pKey;
1258 if( keyit == m_aKeys.end() )
1259 {
1260 pKey = new PPDKey( aUniKey );
1261 insertKey( std::unique_ptr<PPDKey>(pKey) );
1262 }
1263 else
1264 pKey = keyit->second.get();
1265
1266 pKey->m_bUIOption = true;
1267 m_pTranslator->insertKey( pKey->getKey(), aTranslation );
1268
1269 pKey->m_aGroup = OStringToOUString(rPPDGroup, RTL_TEXTENCODING_MS_1252);
1270 }
1271
parseOrderDependency(const OString & rLine)1272 void PPDParser::parseOrderDependency(const OString& rLine)
1273 {
1274 OString aLine(rLine);
1275 sal_Int32 nPos = aLine.indexOf(':');
1276 if( nPos != -1 )
1277 aLine = aLine.copy( nPos+1 );
1278
1279 sal_Int32 nOrder = GetCommandLineToken( 0, aLine ).toInt32();
1280 OUString aKey(OStringToOUString(GetCommandLineToken(2, aLine), RTL_TEXTENCODING_MS_1252));
1281 if( aKey[ 0 ] != '*' )
1282 return; // invalid order dependency
1283 aKey = aKey.replaceAt( 0, 1, u"" );
1284
1285 PPDKey* pKey;
1286 PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey );
1287 if( keyit == m_aKeys.end() )
1288 {
1289 pKey = new PPDKey( aKey );
1290 insertKey( std::unique_ptr<PPDKey>(pKey) );
1291 }
1292 else
1293 pKey = keyit->second.get();
1294
1295 pKey->m_nOrderDependency = nOrder;
1296 }
1297
parseConstraint(const OString & rLine)1298 void PPDParser::parseConstraint( const OString& rLine )
1299 {
1300 bool bFailed = false;
1301
1302 OUString aLine(OStringToOUString(rLine, RTL_TEXTENCODING_MS_1252));
1303 sal_Int32 nIdx = rLine.indexOf(':');
1304 if (nIdx != -1)
1305 aLine = aLine.replaceAt(0, nIdx + 1, u"");
1306 PPDConstraint aConstraint;
1307 int nTokens = GetCommandLineTokenCount( aLine );
1308 for( int i = 0; i < nTokens; i++ )
1309 {
1310 OUString aToken = GetCommandLineToken( i, aLine );
1311 if( !aToken.isEmpty() && aToken[ 0 ] == '*' )
1312 {
1313 aToken = aToken.replaceAt( 0, 1, u"" );
1314 if( aConstraint.m_pKey1 )
1315 aConstraint.m_pKey2 = getKey( aToken );
1316 else
1317 aConstraint.m_pKey1 = getKey( aToken );
1318 }
1319 else
1320 {
1321 if( aConstraint.m_pKey2 )
1322 {
1323 if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1324 bFailed = true;
1325 }
1326 else if( aConstraint.m_pKey1 )
1327 {
1328 if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1329 bFailed = true;
1330 }
1331 else
1332 // constraint for nonexistent keys; this happens
1333 // e.g. in HP4PLUS3
1334 bFailed = true;
1335 }
1336 }
1337 // there must be two keywords
1338 if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed )
1339 {
1340 SAL_INFO("vcl.unx.print",
1341 "Warning: constraint \"" << rLine << "\" is invalid");
1342 }
1343 else
1344 m_aConstraints.push_back( aConstraint );
1345 }
1346
getDefaultPaperDimension() const1347 OUString PPDParser::getDefaultPaperDimension() const
1348 {
1349 if( m_pDefaultPaperDimension )
1350 return m_pDefaultPaperDimension->m_aOption;
1351
1352 return OUString();
1353 }
1354
getMargins(std::u16string_view rPaperName,int & rLeft,int & rRight,int & rUpper,int & rLower) const1355 bool PPDParser::getMargins(
1356 std::u16string_view rPaperName,
1357 int& rLeft, int& rRight,
1358 int& rUpper, int& rLower ) const
1359 {
1360 if( ! m_pImageableAreas || ! m_pPaperDimensions )
1361 return false;
1362
1363 int nPDim=-1, nImArea=-1, i;
1364 for( i = 0; i < m_pImageableAreas->countValues(); i++ )
1365 if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption )
1366 nImArea = i;
1367 for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1368 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1369 nPDim = i;
1370 if( nPDim == -1 || nImArea == -1 )
1371 return false;
1372
1373 double ImLLx, ImLLy, ImURx, ImURy;
1374 double PDWidth, PDHeight;
1375 OUString aArea = m_pImageableAreas->getValue( nImArea )->m_aValue;
1376 ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) );
1377 ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) );
1378 ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) );
1379 ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) );
1380 aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1381 PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1382 PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1383 rLeft = static_cast<int>(ImLLx + 0.5);
1384 rLower = static_cast<int>(ImLLy + 0.5);
1385 rUpper = static_cast<int>(PDHeight - ImURy + 0.5);
1386 rRight = static_cast<int>(PDWidth - ImURx + 0.5);
1387
1388 return true;
1389 }
1390
getPaperDimension(std::u16string_view rPaperName,int & rWidth,int & rHeight) const1391 bool PPDParser::getPaperDimension(
1392 std::u16string_view rPaperName,
1393 int& rWidth, int& rHeight ) const
1394 {
1395 if( ! m_pPaperDimensions )
1396 return false;
1397
1398 int nPDim=-1;
1399 for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1400 if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1401 nPDim = i;
1402 if( nPDim == -1 )
1403 return false;
1404
1405 double PDWidth, PDHeight;
1406 OUString aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1407 PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1408 PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1409 rHeight = static_cast<int>(PDHeight + 0.5);
1410 rWidth = static_cast<int>(PDWidth + 0.5);
1411
1412 return true;
1413 }
1414
matchPaperImpl(int nWidth,int nHeight,psp::orientation * pOrientation) const1415 OUString PPDParser::matchPaperImpl(int nWidth, int nHeight, psp::orientation* pOrientation) const
1416 {
1417 if( ! m_pPaperDimensions )
1418 return OUString();
1419
1420 int nPDim = -1;
1421 double fSort = 2e36, fNewSort;
1422
1423 for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1424 {
1425 OUString aArea = m_pPaperDimensions->getValue( i )->m_aValue;
1426 double PDWidth = StringToDouble( GetCommandLineToken( 0, aArea ) );
1427 double PDHeight = StringToDouble( GetCommandLineToken( 1, aArea ) );
1428 PDWidth /= static_cast<double>(nWidth);
1429 PDHeight /= static_cast<double>(nHeight);
1430 if( PDWidth >= 0.9 && PDWidth <= 1.1 &&
1431 PDHeight >= 0.9 && PDHeight <= 1.1 )
1432 {
1433 fNewSort =
1434 (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight);
1435 if( fNewSort == 0.0 ) // perfect match
1436 return m_pPaperDimensions->getValue( i )->m_aOption;
1437
1438 if( fNewSort < fSort )
1439 {
1440 fSort = fNewSort;
1441 nPDim = i;
1442 }
1443 }
1444 }
1445
1446 if (nPDim == -1)
1447 return OUString();
1448
1449 if (pOrientation)
1450 {
1451 switch (*pOrientation)
1452 {
1453 case psp::orientation::Portrait:
1454 *pOrientation = psp::orientation::Landscape;
1455 break;
1456 case psp::orientation::Landscape:
1457 *pOrientation = psp::orientation::Portrait;
1458 break;
1459 }
1460 }
1461
1462 return m_pPaperDimensions->getValue( nPDim )->m_aOption;
1463 }
1464
matchPaper(int nWidth,int nHeight,psp::orientation * pOrientation) const1465 OUString PPDParser::matchPaper(int nWidth, int nHeight, psp::orientation* pOrientation) const
1466 {
1467 return matchPaperImpl(nHeight, nWidth, pOrientation);
1468 }
1469
getDefaultInputSlot() const1470 OUString PPDParser::getDefaultInputSlot() const
1471 {
1472 if( m_pDefaultInputSlot )
1473 return m_pDefaultInputSlot->m_aValue;
1474 return OUString();
1475 }
1476
getResolutionFromString(std::u16string_view rString,int & rXRes,int & rYRes)1477 void PPDParser::getResolutionFromString(std::u16string_view rString,
1478 int& rXRes, int& rYRes )
1479 {
1480 rXRes = rYRes = 300;
1481
1482 const size_t nDPIPos {rString.find( u"dpi" )};
1483 if( nDPIPos != std::u16string_view::npos )
1484 {
1485 const size_t nPos {rString.find( 'x' )};
1486 if( nPos != std::u16string_view::npos )
1487 {
1488 rXRes = o3tl::toInt32(rString.substr( 0, nPos ));
1489 rYRes = o3tl::toInt32(rString.substr(nPos+1, nDPIPos - nPos - 1));
1490 }
1491 else
1492 rXRes = rYRes = o3tl::toInt32(rString.substr( 0, nDPIPos ));
1493 }
1494 }
1495
getDefaultResolution(int & rXRes,int & rYRes) const1496 void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const
1497 {
1498 if( m_pDefaultResolution )
1499 {
1500 getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes );
1501 return;
1502 }
1503
1504 rXRes = 300;
1505 rYRes = 300;
1506 }
1507
translateKey(const OUString & i_rKey) const1508 OUString PPDParser::translateKey( const OUString& i_rKey ) const
1509 {
1510 OUString aResult( m_pTranslator->translateKey( i_rKey ) );
1511 if( aResult.isEmpty() )
1512 aResult = i_rKey;
1513 return aResult;
1514 }
1515
translateOption(std::u16string_view i_rKey,const OUString & i_rOption) const1516 OUString PPDParser::translateOption( std::u16string_view i_rKey,
1517 const OUString& i_rOption ) const
1518 {
1519 OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption ) );
1520 if( aResult.isEmpty() )
1521 aResult = i_rOption;
1522 return aResult;
1523 }
1524
1525 /*
1526 * PPDKey
1527 */
1528
PPDKey(OUString aKey)1529 PPDKey::PPDKey( OUString aKey ) :
1530 m_aKey(std::move( aKey )),
1531 m_pDefaultValue( nullptr ),
1532 m_bQueryValue( false ),
1533 m_bUIOption( false ),
1534 m_nOrderDependency( 100 )
1535 {
1536 }
1537
~PPDKey()1538 PPDKey::~PPDKey()
1539 {
1540 }
1541
getValue(int n) const1542 const PPDValue* PPDKey::getValue( int n ) const
1543 {
1544 return (n >= 0 && o3tl::make_unsigned(n) < m_aOrderedValues.size()) ? m_aOrderedValues[n] : nullptr;
1545 }
1546
getValue(const OUString & rOption) const1547 const PPDValue* PPDKey::getValue( const OUString& rOption ) const
1548 {
1549 PPDKey::hash_type::const_iterator it = m_aValues.find( rOption );
1550 return it != m_aValues.end() ? &it->second : nullptr;
1551 }
1552
getValueCaseInsensitive(const OUString & rOption) const1553 const PPDValue* PPDKey::getValueCaseInsensitive( const OUString& rOption ) const
1554 {
1555 const PPDValue* pValue = getValue( rOption );
1556 if( ! pValue )
1557 {
1558 for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ )
1559 if( m_aOrderedValues[n]->m_aOption.equalsIgnoreAsciiCase( rOption ) )
1560 pValue = m_aOrderedValues[n];
1561 }
1562
1563 return pValue;
1564 }
1565
eraseValue(const OUString & rOption)1566 void PPDKey::eraseValue( const OUString& rOption )
1567 {
1568 PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1569 if( it == m_aValues.end() )
1570 return;
1571
1572 auto vit = std::find(m_aOrderedValues.begin(), m_aOrderedValues.end(), &(it->second ));
1573 if( vit != m_aOrderedValues.end() )
1574 m_aOrderedValues.erase( vit );
1575
1576 m_aValues.erase( it );
1577 }
1578
insertValue(const OUString & rOption,PPDValueType eType,bool bCustomOption)1579 PPDValue* PPDKey::insertValue(const OUString& rOption, PPDValueType eType, bool bCustomOption)
1580 {
1581 if( m_aValues.find( rOption ) != m_aValues.end() )
1582 return nullptr;
1583
1584 PPDValue aValue;
1585 aValue.m_aOption = rOption;
1586 aValue.m_bCustomOption = bCustomOption;
1587 aValue.m_bCustomOptionSetViaApp = false;
1588 aValue.m_eType = eType;
1589 m_aValues[ rOption ] = aValue;
1590 PPDValue* pValue = &m_aValues[rOption];
1591 m_aOrderedValues.push_back( pValue );
1592 return pValue;
1593 }
1594
1595 /*
1596 * PPDContext
1597 */
1598
PPDContext()1599 PPDContext::PPDContext() :
1600 m_pParser( nullptr )
1601 {
1602 }
1603
operator =(PPDContext && rCopy)1604 PPDContext& PPDContext::operator=( PPDContext&& rCopy )
1605 {
1606 std::swap(m_pParser, rCopy.m_pParser);
1607 std::swap(m_aCurrentValues, rCopy.m_aCurrentValues);
1608 return *this;
1609 }
1610
getModifiedKey(std::size_t n) const1611 const PPDKey* PPDContext::getModifiedKey( std::size_t n ) const
1612 {
1613 if( m_aCurrentValues.size() <= n )
1614 return nullptr;
1615
1616 hash_type::const_iterator it = m_aCurrentValues.begin();
1617 std::advance(it, n);
1618 return it->first;
1619 }
1620
setParser(const PPDParser * pParser)1621 void PPDContext::setParser( const PPDParser* pParser )
1622 {
1623 if( pParser != m_pParser )
1624 {
1625 m_aCurrentValues.clear();
1626 m_pParser = pParser;
1627 }
1628 }
1629
getValue(const PPDKey * pKey) const1630 const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const
1631 {
1632 if( ! m_pParser )
1633 return nullptr;
1634
1635 hash_type::const_iterator it = m_aCurrentValues.find( pKey );
1636 if( it != m_aCurrentValues.end() )
1637 return it->second;
1638
1639 if( ! m_pParser->hasKey( pKey ) )
1640 return nullptr;
1641
1642 const PPDValue* pValue = pKey->getDefaultValue();
1643 if( ! pValue )
1644 pValue = pKey->getValue( 0 );
1645
1646 return pValue;
1647 }
1648
setValue(const PPDKey * pKey,const PPDValue * pValue,bool bDontCareForConstraints)1649 const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1650 {
1651 if( ! m_pParser || ! pKey )
1652 return nullptr;
1653
1654 // pValue can be NULL - it means ignore this option
1655
1656 if( ! m_pParser->hasKey( pKey ) )
1657 return nullptr;
1658
1659 // check constraints
1660 if( pValue )
1661 {
1662 if( bDontCareForConstraints )
1663 {
1664 m_aCurrentValues[ pKey ] = pValue;
1665 }
1666 else if( checkConstraints( pKey, pValue, true ) )
1667 {
1668 m_aCurrentValues[ pKey ] = pValue;
1669
1670 // after setting this value, check all constraints !
1671 hash_type::iterator it = m_aCurrentValues.begin();
1672 while( it != m_aCurrentValues.end() )
1673 {
1674 if( it->first != pKey &&
1675 ! checkConstraints( it->first, it->second, false ) )
1676 {
1677 SAL_INFO("vcl.unx.print", "PPDContext::setValue: option "
1678 << it->first->getKey()
1679 << " (" << it->second->m_aOption
1680 << ") is constrained after setting "
1681 << pKey->getKey()
1682 << " to " << pValue->m_aOption);
1683 resetValue( it->first, true );
1684 it = m_aCurrentValues.begin();
1685 }
1686 else
1687 ++it;
1688 }
1689 }
1690 }
1691 else
1692 m_aCurrentValues[ pKey ] = nullptr;
1693
1694 return pValue;
1695 }
1696
checkConstraints(const PPDKey * pKey,const PPDValue * pValue)1697 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1698 {
1699 if( ! m_pParser || ! pKey || ! pValue )
1700 return false;
1701
1702 // ensure that this key is already in the list if it exists at all
1703 if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() )
1704 return checkConstraints( pKey, pValue, false );
1705
1706 // it is not in the list, insert it temporarily
1707 bool bRet = false;
1708 if( m_pParser->hasKey( pKey ) )
1709 {
1710 const PPDValue* pDefValue = pKey->getDefaultValue();
1711 m_aCurrentValues[ pKey ] = pDefValue;
1712 bRet = checkConstraints( pKey, pValue, false );
1713 m_aCurrentValues.erase( pKey );
1714 }
1715
1716 return bRet;
1717 }
1718
resetValue(const PPDKey * pKey,bool bDefaultable)1719 bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1720 {
1721 if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1722 return false;
1723
1724 const PPDValue* pResetValue = pKey->getValue( u"None"_ustr );
1725 if( ! pResetValue )
1726 pResetValue = pKey->getValue( u"False"_ustr );
1727 if( ! pResetValue && bDefaultable )
1728 pResetValue = pKey->getDefaultValue();
1729
1730 bool bRet = pResetValue && ( setValue( pKey, pResetValue ) == pResetValue );
1731
1732 return bRet;
1733 }
1734
checkConstraints(const PPDKey * pKey,const PPDValue * pNewValue,bool bDoReset)1735 bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1736 {
1737 if( ! pNewValue )
1738 return true;
1739
1740 // sanity checks
1741 if( ! m_pParser )
1742 return false;
1743
1744 if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1745 return false;
1746
1747 // None / False and the default can always be set, but be careful !
1748 // setting them might influence constrained values
1749 if( pNewValue->m_aOption == "None" || pNewValue->m_aOption == "False" ||
1750 pNewValue == pKey->getDefaultValue() )
1751 return true;
1752
1753 const ::std::vector< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() );
1754 for (auto const& constraint : rConstraints)
1755 {
1756 const PPDKey* pLeft = constraint.m_pKey1;
1757 const PPDKey* pRight = constraint.m_pKey2;
1758 if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) )
1759 continue;
1760
1761 const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft;
1762 const PPDValue* pOtherKeyOption = pKey == pLeft ? constraint.m_pOption2 : constraint.m_pOption1;
1763 const PPDValue* pKeyOption = pKey == pLeft ? constraint.m_pOption1 : constraint.m_pOption2;
1764
1765 // syntax *Key1 option1 *Key2 option2
1766 if( pKeyOption && pOtherKeyOption )
1767 {
1768 if( pNewValue != pKeyOption )
1769 continue;
1770 if( pOtherKeyOption == getValue( pOtherKey ) )
1771 {
1772 return false;
1773 }
1774 }
1775 // syntax *Key1 option *Key2 or *Key1 *Key2 option
1776 else if( pOtherKeyOption || pKeyOption )
1777 {
1778 if( pKeyOption )
1779 {
1780 if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) )
1781 continue; // this should not happen, PPD broken
1782
1783 if( pKeyOption == pNewValue &&
1784 pOtherKeyOption->m_aOption != "None" &&
1785 pOtherKeyOption->m_aOption != "False" )
1786 {
1787 // check if the other value can be reset and
1788 // do so if possible
1789 if( bDoReset && resetValue( pOtherKey ) )
1790 continue;
1791
1792 return false;
1793 }
1794 }
1795 else if( pOtherKeyOption )
1796 {
1797 if( getValue( pOtherKey ) == pOtherKeyOption &&
1798 pNewValue->m_aOption != "None" &&
1799 pNewValue->m_aOption != "False" )
1800 return false;
1801 }
1802 else
1803 {
1804 // this should not happen, PPD is broken
1805 }
1806 }
1807 // syntax *Key1 *Key2
1808 else
1809 {
1810 const PPDValue* pOtherValue = getValue( pOtherKey );
1811 if( pOtherValue->m_aOption != "None" &&
1812 pOtherValue->m_aOption != "False" &&
1813 pNewValue->m_aOption != "None" &&
1814 pNewValue->m_aOption != "False" )
1815 return false;
1816 }
1817 }
1818 return true;
1819 }
1820
getStreamableBuffer(sal_uLong & rBytes) const1821 char* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const
1822 {
1823 rBytes = 0;
1824 if( m_aCurrentValues.empty() )
1825 return nullptr;
1826 for (auto const& elem : m_aCurrentValues)
1827 {
1828 OString aCopy(OUStringToOString(elem.first->getKey(), RTL_TEXTENCODING_MS_1252));
1829 rBytes += aCopy.getLength();
1830 rBytes += 1; // for ':'
1831 if( elem.second )
1832 {
1833 aCopy = OUStringToOString(elem.second->m_aOption, RTL_TEXTENCODING_MS_1252);
1834 rBytes += aCopy.getLength();
1835 }
1836 else
1837 rBytes += 4;
1838 rBytes += 1; // for '\0'
1839 }
1840 rBytes += 1;
1841 char* pBuffer = new char[ rBytes ];
1842 memset( pBuffer, 0, rBytes );
1843 char* pRun = pBuffer;
1844 for (auto const& elem : m_aCurrentValues)
1845 {
1846 OString aCopy(OUStringToOString(elem.first->getKey(), RTL_TEXTENCODING_MS_1252));
1847 int nBytes = aCopy.getLength();
1848 memcpy( pRun, aCopy.getStr(), nBytes );
1849 pRun += nBytes;
1850 *pRun++ = ':';
1851 if( elem.second )
1852 aCopy = OUStringToOString(elem.second->m_aOption, RTL_TEXTENCODING_MS_1252);
1853 else
1854 aCopy = "*nil"_ostr;
1855 nBytes = aCopy.getLength();
1856 memcpy( pRun, aCopy.getStr(), nBytes );
1857 pRun += nBytes;
1858
1859 *pRun++ = 0;
1860 }
1861 return pBuffer;
1862 }
1863
rebuildFromStreamBuffer(const std::vector<char> & rBuffer)1864 void PPDContext::rebuildFromStreamBuffer(const std::vector<char> &rBuffer)
1865 {
1866 if( ! m_pParser )
1867 return;
1868
1869 m_aCurrentValues.clear();
1870
1871 const size_t nBytes = rBuffer.size() - 1;
1872 size_t nRun = 0;
1873 while (nRun < nBytes && rBuffer[nRun])
1874 {
1875 OString aLine(rBuffer.data() + nRun);
1876 sal_Int32 nPos = aLine.indexOf(':');
1877 if( nPos != -1 )
1878 {
1879 const PPDKey* pKey = m_pParser->getKey( OStringToOUString( aLine.subView( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
1880 if( pKey )
1881 {
1882 const PPDValue* pValue = nullptr;
1883 OUString aOption(
1884 OStringToOUString(aLine.subView(nPos+1), RTL_TEXTENCODING_MS_1252));
1885 if (aOption != "*nil")
1886 pValue = pKey->getValue( aOption );
1887 m_aCurrentValues[ pKey ] = pValue;
1888 SAL_INFO("vcl.unx.print",
1889 "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { "
1890 << pKey->getKey() << " , "
1891 << (pValue ? aOption : u"<nil>"_ustr)
1892 << " }");
1893 }
1894 }
1895 nRun += aLine.getLength()+1;
1896 }
1897 }
1898
getRenderResolution() const1899 int PPDContext::getRenderResolution() const
1900 {
1901 // initialize to reasonable default, if parser is not set
1902 int nDPI = 300;
1903 if( m_pParser )
1904 {
1905 int nDPIx = 300, nDPIy = 300;
1906 const PPDKey* pKey = m_pParser->getKey( u"Resolution"_ustr );
1907 if( pKey )
1908 {
1909 const PPDValue* pValue = getValue( pKey );
1910 if( pValue )
1911 PPDParser::getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
1912 else
1913 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1914 }
1915 else
1916 m_pParser->getDefaultResolution( nDPIx, nDPIy );
1917
1918 nDPI = std::max(nDPIx, nDPIy);
1919 }
1920 return nDPI;
1921 }
1922
getPageSize(OUString & rPaper,int & rWidth,int & rHeight) const1923 void PPDContext::getPageSize( OUString& rPaper, int& rWidth, int& rHeight ) const
1924 {
1925 // initialize to reasonable default, if parser is not set
1926 rPaper = "A4";
1927 rWidth = 595;
1928 rHeight = 842;
1929 if( !m_pParser )
1930 return;
1931
1932 const PPDKey* pKey = m_pParser->getKey( u"PageSize"_ustr );
1933 if( !pKey )
1934 return;
1935
1936 const PPDValue* pValue = getValue( pKey );
1937 if( pValue )
1938 {
1939 rPaper = pValue->m_aOption;
1940 m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
1941 }
1942 else
1943 {
1944 rPaper = m_pParser->getDefaultPaperDimension();
1945 m_pParser->getDefaultPaperDimension( rWidth, rHeight );
1946 }
1947 }
1948
1949 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1950