Coverage Report

Created: 2026-02-14 09:37

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