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