Coverage Report

Created: 2026-04-09 11:41

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 = psp::getPrinterPathList(PRINTER_PPDDIR);
457
0
    for (auto const& path : aPathList)
458
0
    {
459
0
        INetURLObject aPPDDir( path, INetProtocol::File, INetURLObject::EncodeMechanism::All );
460
0
        scanPPDDir( aPPDDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
461
0
    }
462
0
    if( rPPDCache.xAllPPDFiles->find( u"SGENPRT"_ustr ) != rPPDCache.xAllPPDFiles->end() )
463
0
        return;
464
465
    // last try: search in directory of executable (mainly for setup)
466
0
    OUString aExe;
467
0
    if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
468
0
    {
469
0
        INetURLObject aDir( aExe );
470
0
        aDir.removeSegment();
471
0
        SAL_INFO("vcl.unx.print", "scanning last chance dir: "
472
0
                << aDir.GetMainURL(INetURLObject::DecodeMechanism::NONE));
473
0
        scanPPDDir( aDir.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
474
0
        SAL_INFO("vcl.unx.print", "SGENPRT "
475
0
                << (rPPDCache.xAllPPDFiles->find(u"SGENPRT"_ustr) ==
476
0
                    rPPDCache.xAllPPDFiles->end() ? "not found" : "found"));
477
0
    }
478
0
}
479
480
OUString PPDParser::getPPDFile( const OUString& rFile )
481
0
{
482
0
    INetURLObject aPPD( rFile, INetProtocol::File, INetURLObject::EncodeMechanism::All );
483
    // someone might enter a full qualified name here
484
0
    PPDDecompressStream aStream( aPPD.PathToFileName() );
485
0
    if( ! aStream.IsOpen() )
486
0
    {
487
0
        std::unordered_map< OUString, OUString >::const_iterator it;
488
0
        PPDCache &rPPDCache = getPPDCache();
489
490
0
        bool bRetry = true;
491
0
        do
492
0
        {
493
0
            initPPDFiles(rPPDCache);
494
            // some PPD files contain dots beside the extension, so try name first
495
            // and cut of points after that
496
0
            OUString aBase( rFile );
497
0
            sal_Int32 nLastIndex = aBase.lastIndexOf( '/' );
498
0
            if( nLastIndex >= 0 )
499
0
                aBase = aBase.copy( nLastIndex+1 );
500
0
            do
501
0
            {
502
0
                it = rPPDCache.xAllPPDFiles->find( aBase );
503
0
                nLastIndex = aBase.lastIndexOf( '.' );
504
0
                if( nLastIndex > 0 )
505
0
                    aBase = aBase.copy( 0, nLastIndex );
506
0
            } while( it == rPPDCache.xAllPPDFiles->end() && nLastIndex > 0 );
507
508
0
            if( it == rPPDCache.xAllPPDFiles->end() && bRetry )
509
0
            {
510
                // a new file ? rehash
511
0
                rPPDCache.xAllPPDFiles.reset();
512
0
                bRetry = false;
513
                // note this is optimized for office start where
514
                // no new files occur and initPPDFiles is called only once
515
0
            }
516
0
        } while( ! rPPDCache.xAllPPDFiles );
517
518
0
        if( it != rPPDCache.xAllPPDFiles->end() )
519
0
            aStream.Open( it->second );
520
0
    }
521
522
0
    OUString aRet;
523
0
    if( aStream.IsOpen() )
524
0
    {
525
0
        OString aLine = aStream.ReadLine();
526
0
        if (aLine.startsWith("*PPD-Adobe"))
527
0
            aRet = aStream.GetFileName();
528
0
        else
529
0
        {
530
            // our *Include hack does usually not begin
531
            // with *PPD-Adobe, so try some lines for *Include
532
0
            int nLines = 10;
533
0
            while (aLine.indexOf("*Include") != 0 && --nLines)
534
0
                aLine = aStream.ReadLine();
535
0
            if( nLines )
536
0
                aRet = aStream.GetFileName();
537
0
        }
538
0
    }
539
540
0
    return aRet;
541
0
}
542
543
const PPDParser* PPDParser::getParser( const OUString& rFile )
544
0
{
545
    // Recursive because we can get re-entered via CUPSManager::createCUPSParser
546
0
    static std::recursive_mutex aMutex;
547
0
    std::scoped_lock aGuard( aMutex );
548
549
0
    OUString aFile = rFile;
550
0
    if( !rFile.startsWith( "CUPS:" ) && !rFile.startsWith( "CPD:" ) )
551
0
        aFile = getPPDFile( rFile );
552
0
    if( aFile.isEmpty() )
553
0
    {
554
0
        SAL_INFO("vcl.unx.print", "Could not get printer PPD file \""
555
0
                << rFile << "\" !");
556
0
        return nullptr;
557
0
    }
558
0
    else
559
0
        SAL_INFO("vcl.unx.print", "Parsing printer info from \""
560
0
                 << rFile << "\" !");
561
562
563
0
    PPDCache &rPPDCache = getPPDCache();
564
0
    for( auto const & i : rPPDCache.aAllParsers )
565
0
        if( i->m_aFile == aFile )
566
0
            return i.get();
567
568
0
    PPDParser* pNewParser = nullptr;
569
0
    if( !aFile.startsWith( "CUPS:" ) && !aFile.startsWith( "CPD:" ) )
570
0
        pNewParser = new PPDParser( aFile );
571
0
    else
572
0
    {
573
0
        PrinterInfoManager& rMgr = PrinterInfoManager::get();
574
0
        if( rMgr.getType() == PrinterInfoManager::Type::CUPS )
575
0
        {
576
#if ENABLE_CUPS
577
            pNewParser = const_cast<PPDParser*>(static_cast<CUPSManager&>(rMgr).createCUPSParser( aFile ));
578
#endif
579
0
        } else if ( rMgr.getType() == PrinterInfoManager::Type::CPD )
580
0
        {
581
#if ENABLE_CPDB
582
            pNewParser = const_cast<PPDParser*>(static_cast<CPDManager&>(rMgr).createCPDParser( aFile ));
583
#endif
584
0
        }
585
0
    }
586
0
    if( pNewParser )
587
0
    {
588
        // this may actually be the SGENPRT parser,
589
        // so ensure uniqueness here (but don't remove last we delete us!)
590
0
        if (std::none_of(
591
0
                    rPPDCache.aAllParsers.begin(),
592
0
                    rPPDCache.aAllParsers.end(),
593
0
                    [pNewParser] (std::unique_ptr<PPDParser> const & x) { return x.get() == pNewParser; } ))
594
0
        {
595
            // insert new parser to vector
596
0
            rPPDCache.aAllParsers.emplace_back(pNewParser);
597
0
        }
598
0
    }
599
0
    return pNewParser;
600
0
}
601
602
PPDParser::PPDParser(OUString aFile, const std::vector<PPDKey*>& keys)
603
0
    : m_aFile(std::move(aFile))
604
0
    , m_aFileEncoding(RTL_TEXTENCODING_MS_1252)
605
0
    , m_pImageableAreas(nullptr)
606
0
    , m_pDefaultPaperDimension(nullptr)
607
0
    , m_pPaperDimensions(nullptr)
608
0
    , m_pDefaultInputSlot(nullptr)
609
0
    , m_pDefaultResolution(nullptr)
610
0
    , m_pTranslator(new PPDTranslator())
611
0
{
612
0
    for (auto & key: keys)
613
0
    {
614
0
        insertKey( std::unique_ptr<PPDKey>(key) );
615
0
    }
616
617
    // fill in shortcuts
618
0
    const PPDKey* pKey;
619
620
0
    pKey = getKey( u"PageSize"_ustr );
621
622
0
    if ( pKey ) {
623
0
        std::unique_ptr<PPDKey> pImageableAreas(new PPDKey(u"ImageableArea"_ustr));
624
0
        std::unique_ptr<PPDKey> pPaperDimensions(new PPDKey(u"PaperDimension"_ustr));
625
#if ENABLE_CUPS
626
        for (int i = 0; i < pKey->countValues(); i++) {
627
            const PPDValue* pValue = pKey -> getValue(i);
628
            OUString aValueName = pValue -> m_aOption;
629
            PPDValue* pImageableAreaValue
630
                = pImageableAreas->insertValue(aValueName, PPDValueType::Quoted);
631
            PPDValue* pPaperDimensionValue
632
                = pPaperDimensions->insertValue(aValueName, PPDValueType::Quoted);
633
            rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
634
            OString o = OUStringToOString( aValueName, aEncoding );
635
            pwg_media_t *pPWGMedia = pwgMediaForPWG(o.pData->buffer);
636
            if (pPWGMedia != nullptr) {
637
                if ( pImageableAreaValue )
638
                    pImageableAreaValue->m_aValue =
639
                        "0 0 " +
640
                        OUString::number(PWG_TO_POINTS(pPWGMedia -> width)) +
641
                        " " +
642
                        OUString::number(PWG_TO_POINTS(pPWGMedia -> length));
643
                if ( pPaperDimensionValue )
644
                    pPaperDimensionValue->m_aValue =
645
                        OUString::number(PWG_TO_POINTS(pPWGMedia -> width))
646
                        + " "
647
                        + OUString::number(PWG_TO_POINTS(pPWGMedia -> length));
648
                if (aValueName.equals(pKey -> getDefaultValue() -> m_aOption)) {
649
                    pImageableAreas -> m_pDefaultValue = pImageableAreaValue;
650
                    pPaperDimensions -> m_pDefaultValue = pPaperDimensionValue;
651
                }
652
            }
653
        }
654
#endif
655
0
        insertKey(std::move(pImageableAreas));
656
0
        insertKey(std::move(pPaperDimensions));
657
0
    }
658
659
0
    m_pImageableAreas = getKey( u"ImageableArea"_ustr );
660
0
    const PPDValue* pDefaultImageableArea = nullptr;
661
0
    if( m_pImageableAreas )
662
0
        pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
663
0
    if (m_pImageableAreas == nullptr) {
664
0
        SAL_WARN( "vcl.unx.print", "no ImageableArea in " << m_aFile);
665
0
    }
666
0
    if (pDefaultImageableArea == nullptr) {
667
0
        SAL_WARN( "vcl.unx.print", "no DefaultImageableArea in " << m_aFile);
668
0
    }
669
670
0
    m_pPaperDimensions = getKey( u"PaperDimension"_ustr );
671
0
    if( m_pPaperDimensions )
672
0
        m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
673
0
    if (m_pPaperDimensions == nullptr) {
674
0
        SAL_WARN( "vcl.unx.print", "no PaperDimensions in " << m_aFile);
675
0
    }
676
0
    if (m_pDefaultPaperDimension == nullptr) {
677
0
        SAL_WARN( "vcl.unx.print", "no DefaultPaperDimensions in " << m_aFile);
678
0
    }
679
680
0
    auto pResolutions = getKey( u"Resolution"_ustr );
681
0
    if( pResolutions )
682
0
        m_pDefaultResolution = pResolutions->getDefaultValue();
683
0
    if (pResolutions == nullptr) {
684
0
        SAL_INFO( "vcl.unx.print", "no Resolution in " << m_aFile);
685
0
    }
686
0
    SAL_INFO_IF(!m_pDefaultResolution, "vcl.unx.print", "no DefaultResolution in " + m_aFile);
687
688
0
    auto pInputSlots = getKey( u"InputSlot"_ustr );
689
0
    if( pInputSlots )
690
0
        m_pDefaultInputSlot = pInputSlots->getDefaultValue();
691
0
    SAL_INFO_IF(!pInputSlots, "vcl.unx.print", "no InputSlot in " << m_aFile);
692
0
    SAL_INFO_IF(!m_pDefaultInputSlot, "vcl.unx.print", "no DefaultInputSlot in " << m_aFile);
693
0
}
694
695
PPDParser::PPDParser( OUString aFile ) :
696
0
        m_aFile(std::move( aFile )),
697
0
        m_aFileEncoding( RTL_TEXTENCODING_MS_1252 ),
698
0
        m_pImageableAreas( nullptr ),
699
0
        m_pDefaultPaperDimension( nullptr ),
700
0
        m_pPaperDimensions( nullptr ),
701
0
        m_pDefaultInputSlot( nullptr ),
702
0
        m_pDefaultResolution( nullptr ),
703
0
        m_pTranslator( new PPDTranslator() )
704
0
{
705
    // read in the file
706
0
    std::vector< OString > aLines;
707
0
    PPDDecompressStream aStream( m_aFile );
708
0
    if( aStream.IsOpen() )
709
0
    {
710
0
        bool bLanguageEncoding = false;
711
0
        while( ! aStream.eof() )
712
0
        {
713
0
            OString aCurLine = aStream.ReadLine();
714
0
            if( aCurLine.startsWith("*") )
715
0
            {
716
0
                if (aCurLine.matchIgnoreAsciiCase("*include:"))
717
0
                {
718
0
                    aCurLine = aCurLine.copy(9);
719
0
                    aCurLine = comphelper::string::strip(aCurLine, ' ');
720
0
                    aCurLine = comphelper::string::strip(aCurLine, '\t');
721
0
                    aCurLine = comphelper::string::stripEnd(aCurLine, '\r');
722
0
                    aCurLine = comphelper::string::stripEnd(aCurLine, '\n');
723
0
                    aCurLine = comphelper::string::strip(aCurLine, '"');
724
0
                    aStream.Close();
725
0
                    aStream.Open(getPPDFile(OStringToOUString(aCurLine, m_aFileEncoding)));
726
0
                    continue;
727
0
                }
728
0
                else if( ! bLanguageEncoding &&
729
0
                         aCurLine.matchIgnoreAsciiCase("*languageencoding") )
730
0
                {
731
0
                    bLanguageEncoding = true; // generally only the first one counts
732
0
                    OString aLower = aCurLine.toAsciiLowerCase();
733
0
                    if( aLower.indexOf("isolatin1", 17 ) != -1 ||
734
0
                        aLower.indexOf("windowsansi", 17 ) != -1 )
735
0
                        m_aFileEncoding = RTL_TEXTENCODING_MS_1252;
736
0
                    else if( aLower.indexOf("isolatin2", 17 ) != -1 )
737
0
                        m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_2;
738
0
                    else if( aLower.indexOf("isolatin5", 17 ) != -1 )
739
0
                        m_aFileEncoding = RTL_TEXTENCODING_ISO_8859_5;
740
0
                    else if( aLower.indexOf("jis83-rksj", 17 ) != -1 )
741
0
                        m_aFileEncoding = RTL_TEXTENCODING_SHIFT_JIS;
742
0
                    else if( aLower.indexOf("macstandard", 17 ) != -1 )
743
0
                        m_aFileEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
744
0
                    else if( aLower.indexOf("utf-8", 17 ) != -1 )
745
0
                        m_aFileEncoding = RTL_TEXTENCODING_UTF8;
746
0
                }
747
0
            }
748
0
            aLines.push_back( aCurLine );
749
0
        }
750
0
    }
751
0
    aStream.Close();
752
753
    // now get the Values
754
0
    parse( aLines );
755
#if OSL_DEBUG_LEVEL > 1
756
    SAL_INFO("vcl.unx.print", "acquired " << m_aKeys.size()
757
            << " Keys from PPD " << m_aFile << ":");
758
    for (auto const& key : m_aKeys)
759
    {
760
        const PPDKey* pKey = key.second.get();
761
        char const* pSetupType = "<unknown>";
762
        switch( pKey->m_eSetupType )
763
        {
764
            case PPDKey::SetupType::ExitServer:        pSetupType = "ExitServer";break;
765
            case PPDKey::SetupType::Prolog:            pSetupType = "Prolog";break;
766
            case PPDKey::SetupType::DocumentSetup:     pSetupType = "DocumentSetup";break;
767
            case PPDKey::SetupType::PageSetup:         pSetupType = "PageSetup";break;
768
            case PPDKey::SetupType::JCLSetup:          pSetupType = "JCLSetup";break;
769
            case PPDKey::SetupType::AnySetup:          pSetupType = "AnySetup";break;
770
            default: break;
771
        }
772
        SAL_INFO("vcl.unx.print", "\t\"" << pKey->getKey() << "\" ("
773
                << pKey->countValues() << "values) OrderDependency: "
774
                << pKey->m_nOrderDependency << pSetupType );
775
        for( int j = 0; j < pKey->countValues(); j++ )
776
        {
777
            const PPDValue* pValue = pKey->getValue( j );
778
            char const* pVType = "<unknown>";
779
            switch( pValue->m_eType )
780
            {
781
                case PPDValueType::Invocation:
782
                    pVType = "invocation";
783
                    break;
784
                case PPDValueType::Quoted:
785
                    pVType = "quoted";
786
                    break;
787
                case PPDValueType::String:
788
                    pVType = "string";
789
                    break;
790
                case PPDValueType::Symbol:
791
                    pVType = "symbol";
792
                    break;
793
                case PPDValueType::No:
794
                    pVType = "no";
795
                    break;
796
                default: break;
797
            }
798
            SAL_INFO("vcl.unx.print", "\t\t"
799
                << (pValue == pKey->m_pDefaultValue ? "(Default:) " : "")
800
                << "option: \"" << pValue->m_aOption
801
                << "\", value: type " << pVType << " \""
802
                << pValue->m_aValue << "\"");
803
        }
804
    }
805
    SAL_INFO("vcl.unx.print",
806
            "constraints: (" << m_aConstraints.size() << " found)");
807
    for (auto const& constraint : m_aConstraints)
808
    {
809
        SAL_INFO("vcl.unx.print", "*\"" << constraint.m_pKey1->getKey() << "\" \""
810
                << (constraint.m_pOption1 ? constraint.m_pOption1->m_aOption : "<nil>")
811
                << "\" *\"" << constraint.m_pKey2->getKey() << "\" \""
812
                << (constraint.m_pOption2 ? constraint.m_pOption2->m_aOption : "<nil>")
813
                << "\"");
814
    }
815
#endif
816
817
0
    m_pImageableAreas = getKey( u"ImageableArea"_ustr );
818
0
    const PPDValue * pDefaultImageableArea = nullptr;
819
0
    if( m_pImageableAreas )
820
0
        pDefaultImageableArea = m_pImageableAreas->getDefaultValue();
821
0
    if (m_pImageableAreas == nullptr) {
822
0
        SAL_WARN( "vcl.unx.print", "no ImageableArea in " << m_aFile);
823
0
    }
824
0
    if (pDefaultImageableArea == nullptr) {
825
0
        SAL_WARN( "vcl.unx.print", "no DefaultImageableArea in " << m_aFile);
826
0
    }
827
828
0
    m_pPaperDimensions = getKey( u"PaperDimension"_ustr );
829
0
    if( m_pPaperDimensions )
830
0
        m_pDefaultPaperDimension = m_pPaperDimensions->getDefaultValue();
831
0
    if (m_pPaperDimensions == nullptr) {
832
0
        SAL_WARN( "vcl.unx.print", "no PaperDimensions in " << m_aFile);
833
0
    }
834
0
    if (m_pDefaultPaperDimension == nullptr) {
835
0
        SAL_WARN( "vcl.unx.print", "no DefaultPaperDimensions in " << m_aFile);
836
0
    }
837
838
0
    auto pResolutions = getKey( u"Resolution"_ustr );
839
0
    if( pResolutions )
840
0
        m_pDefaultResolution = pResolutions->getDefaultValue();
841
0
    if (pResolutions == nullptr) {
842
0
        SAL_INFO( "vcl.unx.print", "no Resolution in " << m_aFile);
843
0
    }
844
0
    SAL_INFO_IF(!m_pDefaultResolution, "vcl.unx.print", "no DefaultResolution in " + m_aFile);
845
846
0
    auto pInputSlots = getKey( u"InputSlot"_ustr );
847
0
    if( pInputSlots )
848
0
        m_pDefaultInputSlot = pInputSlots->getDefaultValue();
849
0
    SAL_INFO_IF(!pInputSlots, "vcl.unx.print", "no InputSlot in " << m_aFile);
850
0
    SAL_INFO_IF(!m_pDefaultInputSlot, "vcl.unx.print", "no DefaultInputSlot in " << m_aFile);
851
0
}
852
853
PPDParser::~PPDParser()
854
0
{
855
0
    m_pTranslator.reset();
856
0
}
857
858
void PPDParser::insertKey( std::unique_ptr<PPDKey> pKey )
859
0
{
860
0
    m_aOrderedKeys.push_back( pKey.get() );
861
0
    m_aKeys[ pKey->getKey() ] = std::move(pKey);
862
0
}
863
864
const PPDKey* PPDParser::getKey( int n ) const
865
0
{
866
0
    return (n >= 0 && o3tl::make_unsigned(n) < m_aOrderedKeys.size()) ? m_aOrderedKeys[n] : nullptr;
867
0
}
868
869
const PPDKey* PPDParser::getKey( const OUString& rKey ) const
870
0
{
871
0
    PPDParser::hash_type::const_iterator it = m_aKeys.find( rKey );
872
0
    return it != m_aKeys.end() ? it->second.get() : nullptr;
873
0
}
874
875
bool PPDParser::hasKey( const PPDKey* pKey ) const
876
0
{
877
0
    return pKey && ( m_aKeys.find( pKey->getKey() ) != m_aKeys.end() );
878
0
}
879
880
static sal_uInt8 getNibble( char cChar )
881
0
{
882
0
    sal_uInt8 nRet = 0;
883
0
    if( cChar >= '0' && cChar <= '9' )
884
0
        nRet = sal_uInt8( cChar - '0' );
885
0
    else if( cChar >= 'A' && cChar <= 'F' )
886
0
        nRet = 10 + sal_uInt8( cChar - 'A' );
887
0
    else if( cChar >= 'a' && cChar <= 'f' )
888
0
        nRet = 10 + sal_uInt8( cChar - 'a' );
889
0
    return nRet;
890
0
}
891
892
OUString PPDParser::handleTranslation(const OString& i_rString, bool bIsGlobalized)
893
0
{
894
0
    sal_Int32 nOrigLen = i_rString.getLength();
895
0
    OStringBuffer aTrans( nOrigLen );
896
0
    const char* pStr = i_rString.getStr();
897
0
    const char* pEnd = pStr + nOrigLen;
898
0
    while( pStr < pEnd )
899
0
    {
900
0
        if( *pStr == '<' )
901
0
        {
902
0
            pStr++;
903
0
            char cChar;
904
0
            while( *pStr != '>' && pStr < pEnd-1 )
905
0
            {
906
0
                cChar = getNibble( *pStr++ ) << 4;
907
0
                cChar |= getNibble( *pStr++ );
908
0
                aTrans.append( cChar );
909
0
            }
910
0
            pStr++;
911
0
        }
912
0
        else
913
0
            aTrans.append( *pStr++ );
914
0
    }
915
0
    return OStringToOUString( aTrans, bIsGlobalized ? RTL_TEXTENCODING_UTF8 : m_aFileEncoding );
916
0
}
917
918
namespace
919
{
920
    bool oddDoubleQuoteCount(OStringBuffer &rBuffer)
921
0
    {
922
0
        bool bHasOddCount = false;
923
0
        for (sal_Int32 i = 0; i < rBuffer.getLength(); ++i)
924
0
        {
925
0
            if (rBuffer[i] == '"')
926
0
                bHasOddCount = !bHasOddCount;
927
0
        }
928
0
        return bHasOddCount;
929
0
    }
930
}
931
932
void PPDParser::parse( ::std::vector< OString >& rLines )
933
0
{
934
    // Name for PPD group into which all options are put for which the PPD
935
    // does not explicitly define a group.
936
    // This is similar to how CUPS handles it,
937
    // s. Sweet, Michael R. (2001): Common UNIX Printing System, p. 251:
938
    // "Each option in turn is associated with a group stored in the
939
    // ppd_group_t structure. Groups can be specified in the PPD file; if an
940
    // option is not associated with a group, it is put in a "General" or
941
    // "Extra" group depending on the option.
942
0
    static constexpr OString aDefaultPPDGroupName("General"_ostr);
943
944
0
    std::vector< OString >::iterator line = rLines.begin();
945
0
    PPDParser::hash_type::const_iterator keyit;
946
947
    // name of the PPD group that is currently being processed
948
0
    OString aCurrentGroup = aDefaultPPDGroupName;
949
950
0
    while( line != rLines.end() )
951
0
    {
952
0
        OString aCurrentLine( *line );
953
0
        ++line;
954
955
0
        SAL_INFO("vcl.unx.print", "Parse line '" << aCurrentLine << "'");
956
957
0
        if (aCurrentLine.getLength() < 2 || aCurrentLine[0] != '*')
958
0
            continue;
959
0
        if( aCurrentLine[1] == '%' )
960
0
            continue;
961
962
0
        OString aKey = GetCommandLineToken( 0, aCurrentLine.getToken(0, ':') );
963
0
        sal_Int32 nPos = aKey.indexOf('/');
964
0
        if (nPos != -1)
965
0
            aKey = aKey.copy(0, nPos);
966
0
        if(!aKey.isEmpty())
967
0
        {
968
0
            aKey = aKey.copy(1); // remove the '*'
969
0
        }
970
0
        if(aKey.isEmpty())
971
0
        {
972
0
            continue;
973
0
        }
974
975
0
        if (aKey == "CloseGroup")
976
0
        {
977
0
            aCurrentGroup = aDefaultPPDGroupName;
978
0
            continue;
979
0
        }
980
0
        if (aKey == "OpenGroup")
981
0
        {
982
0
            OString aGroupName = aCurrentLine;
983
0
            sal_Int32 nPosition = aGroupName.indexOf('/');
984
0
            if (nPosition != -1)
985
0
            {
986
0
                aGroupName = aGroupName.copy(0, nPosition);
987
0
            }
988
989
0
            aCurrentGroup = GetCommandLineToken(1, aGroupName);
990
0
            continue;
991
0
        }
992
0
        if ((aKey == "CloseUI") ||
993
0
            (aKey == "JCLCloseUI") ||
994
0
            (aKey == "End") ||
995
0
            (aKey == "JCLEnd") ||
996
0
            (aKey == "OpenSubGroup") ||
997
0
            (aKey == "CloseSubGroup"))
998
0
        {
999
0
            continue;
1000
0
        }
1001
1002
0
        if ((aKey == "OpenUI") || (aKey == "JCLOpenUI"))
1003
0
        {
1004
0
            parseOpenUI( aCurrentLine, aCurrentGroup);
1005
0
            continue;
1006
0
        }
1007
0
        else if (aKey == "OrderDependency")
1008
0
        {
1009
0
            parseOrderDependency( aCurrentLine );
1010
0
            continue;
1011
0
        }
1012
0
        else if (aKey == "UIConstraints" ||
1013
0
                 aKey == "NonUIConstraints")
1014
0
        {
1015
0
            continue; // parsed in pass 2
1016
0
        }
1017
0
        else if( aKey == "CustomPageSize" ) // currently not handled
1018
0
            continue;
1019
0
        else if ( std::string_view rest; aKey.startsWith("Custom", &rest) )
1020
0
        {
1021
            //fdo#43049 very basic support for Custom entries, we ignore the
1022
            //validation params and types
1023
0
            OUString aUniKey(OStringToOUString(rest, RTL_TEXTENCODING_MS_1252));
1024
0
            keyit = m_aKeys.find( aUniKey );
1025
0
            if(keyit != m_aKeys.end())
1026
0
            {
1027
0
                PPDKey* pKey = keyit->second.get();
1028
0
                pKey->insertValue(u"Custom"_ustr, PPDValueType::Invocation, true);
1029
0
            }
1030
0
            continue;
1031
0
        }
1032
1033
        // default values are parsed in pass 2
1034
0
        if (aKey.startsWith("Default"))
1035
0
            continue;
1036
1037
0
        bool bQuery     = false;
1038
0
        if (aKey[0] == '?')
1039
0
        {
1040
0
            aKey = aKey.copy(1);
1041
0
            bQuery = true;
1042
0
        }
1043
1044
0
        OUString aUniKey(OStringToOUString(aKey, RTL_TEXTENCODING_MS_1252));
1045
        // handle CUPS extension for globalized PPDs
1046
        /* FIXME-BCP47: really only ISO 639-1 two character language codes?
1047
         * goodnight... */
1048
0
        bool bIsGlobalizedLine = false;
1049
0
        css::lang::Locale aTransLocale;
1050
0
        if( ( aUniKey.getLength() > 3 && aUniKey[ 2 ] == '.' ) ||
1051
0
            ( aUniKey.getLength() > 5 && aUniKey[ 2 ] == '_' && aUniKey[ 5 ] == '.' ) )
1052
0
        {
1053
0
            if( aUniKey[ 2 ] == '.' )
1054
0
            {
1055
0
                aTransLocale.Language = aUniKey.copy( 0, 2 );
1056
0
                aUniKey = aUniKey.copy( 3 );
1057
0
            }
1058
0
            else
1059
0
            {
1060
0
                aTransLocale.Language = aUniKey.copy( 0, 2 );
1061
0
                aTransLocale.Country = aUniKey.copy( 3, 2 );
1062
0
                aUniKey = aUniKey.copy( 6 );
1063
0
            }
1064
0
            bIsGlobalizedLine = true;
1065
0
        }
1066
1067
0
        OUString aOption;
1068
0
        nPos = aCurrentLine.indexOf(':');
1069
0
        if( nPos != -1 )
1070
0
        {
1071
0
            aOption = OStringToOUString(
1072
0
                aCurrentLine.subView( 1, nPos-1 ), RTL_TEXTENCODING_MS_1252 );
1073
0
            aOption = GetCommandLineToken( 1, aOption );
1074
0
            sal_Int32 nTransPos = aOption.indexOf( '/' );
1075
0
            if( nTransPos != -1 )
1076
0
                aOption = aOption.copy(0,  nTransPos);
1077
0
        }
1078
1079
0
        PPDValueType eType = PPDValueType::No;
1080
0
        OUString aValue;
1081
0
        OUString aOptionTranslation;
1082
0
        OUString aValueTranslation;
1083
0
        if( nPos != -1 )
1084
0
        {
1085
            // found a colon, there may be an option
1086
0
            OString aLine = aCurrentLine.copy( 1, nPos-1 );
1087
0
            aLine = WhitespaceToSpace( aLine );
1088
0
            sal_Int32 nTransPos = aLine.indexOf('/');
1089
0
            if (nTransPos != -1)
1090
0
                aOptionTranslation = handleTranslation( aLine.copy(nTransPos+1), bIsGlobalizedLine );
1091
1092
            // read in more lines if necessary for multiline values
1093
0
            aLine = aCurrentLine.copy( nPos+1 );
1094
0
            if (!aLine.isEmpty())
1095
0
            {
1096
0
                OStringBuffer aBuffer(aLine);
1097
0
                while (line != rLines.end() && oddDoubleQuoteCount(aBuffer))
1098
0
                {
1099
                    // copy the newlines also
1100
0
                    aBuffer.append("\n" + *line);
1101
0
                    ++line;
1102
0
                }
1103
0
                aLine = aBuffer.makeStringAndClear();
1104
0
            }
1105
0
            aLine = WhitespaceToSpace( aLine );
1106
1107
            // #i100644# handle a missing value (actually a broken PPD)
1108
0
            if( aLine.isEmpty() )
1109
0
            {
1110
0
                if( !aOption.isEmpty() &&
1111
0
                    !aUniKey.startsWith( "JCL" ) )
1112
0
                    eType = PPDValueType::Invocation;
1113
0
                else
1114
0
                    eType = PPDValueType::Quoted;
1115
0
            }
1116
            // check for invocation or quoted value
1117
0
            else if(aLine[0] == '"')
1118
0
            {
1119
0
                aLine = aLine.copy(1);
1120
0
                nTransPos = aLine.indexOf('"');
1121
0
                if (nTransPos == -1)
1122
0
                    nTransPos = aLine.getLength();
1123
0
                aValue = OStringToOUString(aLine.subView(0, nTransPos), RTL_TEXTENCODING_MS_1252);
1124
                // after the second doublequote can follow a / and a translation
1125
0
                if (nTransPos < aLine.getLength() - 2)
1126
0
                {
1127
0
                    aValueTranslation = handleTranslation( aLine.copy( nTransPos+2 ), bIsGlobalizedLine );
1128
0
                }
1129
                // check for quoted value
1130
0
                if( !aOption.isEmpty() &&
1131
0
                    !aUniKey.startsWith( "JCL" ) )
1132
0
                    eType = PPDValueType::Invocation;
1133
0
                else
1134
0
                    eType = PPDValueType::Quoted;
1135
0
            }
1136
            // check for symbol value
1137
0
            else if(aLine[0] == '^')
1138
0
            {
1139
0
                aLine = aLine.copy(1);
1140
0
                aValue = OStringToOUString(aLine, RTL_TEXTENCODING_MS_1252);
1141
0
                eType = PPDValueType::Symbol;
1142
0
            }
1143
0
            else
1144
0
            {
1145
                // must be a string value then
1146
                // strictly this is false because string values
1147
                // can contain any whitespace which is reduced
1148
                // to one space by now
1149
                // who cares ...
1150
0
                nTransPos = aLine.indexOf('/');
1151
0
                if (nTransPos == -1)
1152
0
                    nTransPos = aLine.getLength();
1153
0
                aValue = OStringToOUString(aLine.subView(0, nTransPos), RTL_TEXTENCODING_MS_1252);
1154
0
                if (nTransPos+1 < aLine.getLength())
1155
0
                    aValueTranslation = handleTranslation( aLine.copy( nTransPos+1 ), bIsGlobalizedLine );
1156
0
                eType = PPDValueType::String;
1157
0
            }
1158
0
        }
1159
1160
        // handle globalized PPD entries
1161
0
        if( bIsGlobalizedLine )
1162
0
        {
1163
            // handle main key translations of form:
1164
            // *ll_CC.Translation MainKeyword/translated text: ""
1165
0
            if( aUniKey == "Translation" )
1166
0
            {
1167
0
                m_pTranslator->insertKey( aOption, aOptionTranslation, aTransLocale );
1168
0
            }
1169
            // handle options translations of for:
1170
            // *ll_CC.MainKeyword OptionKeyword/translated text: ""
1171
0
            else
1172
0
            {
1173
0
                m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1174
0
            }
1175
0
            continue;
1176
0
        }
1177
1178
0
        PPDKey* pKey = nullptr;
1179
0
        keyit = m_aKeys.find( aUniKey );
1180
0
        if( keyit == m_aKeys.end() )
1181
0
        {
1182
0
            pKey = new PPDKey( aUniKey );
1183
0
            insertKey( std::unique_ptr<PPDKey>(pKey) );
1184
0
        }
1185
0
        else
1186
0
            pKey = keyit->second.get();
1187
1188
0
        if (eType == PPDValueType::No && bQuery)
1189
0
            continue;
1190
1191
0
        PPDValue* pValue = pKey->insertValue( aOption, eType );
1192
0
        if( ! pValue )
1193
0
            continue;
1194
0
        pValue->m_aValue = aValue;
1195
1196
0
        if( !aOptionTranslation.isEmpty() )
1197
0
            m_pTranslator->insertOption( aUniKey, aOption, aOptionTranslation, aTransLocale );
1198
0
        if( !aValueTranslation.isEmpty() )
1199
0
            m_pTranslator->insertValue( aUniKey, aOption, aValue, aValueTranslation, aTransLocale );
1200
1201
        // eventually update query and remove from option list
1202
0
        if( bQuery && !pKey->m_bQueryValue )
1203
0
        {
1204
0
            pKey->m_bQueryValue = true;
1205
0
            pKey->eraseValue( pValue->m_aOption );
1206
0
        }
1207
0
    }
1208
1209
    // second pass: fill in defaults
1210
0
    for( const auto& aLine : rLines )
1211
0
    {
1212
0
        if (aLine.startsWith("*Default"))
1213
0
        {
1214
0
            SAL_INFO("vcl.unx.print", "Found a default: '" << aLine << "'");
1215
0
            OUString aKey(OStringToOUString(aLine.subView(8), RTL_TEXTENCODING_MS_1252));
1216
0
            sal_Int32 nPos = aKey.indexOf( ':' );
1217
0
            if( nPos != -1 )
1218
0
            {
1219
0
                aKey = aKey.copy(0, nPos);
1220
0
                OUString aOption(OStringToOUString(
1221
0
                    WhitespaceToSpace(aLine.subView(nPos+9)),
1222
0
                    RTL_TEXTENCODING_MS_1252));
1223
0
                keyit = m_aKeys.find( aKey );
1224
0
                if( keyit != m_aKeys.end() )
1225
0
                {
1226
0
                    PPDKey* pKey = keyit->second.get();
1227
0
                    const PPDValue* pDefValue = pKey->getValue( aOption );
1228
0
                    if( pKey->m_pDefaultValue == nullptr )
1229
0
                        pKey->m_pDefaultValue = pDefValue;
1230
0
                }
1231
0
                else
1232
0
                {
1233
                    // some PPDs contain defaults for keys that
1234
                    // do not exist otherwise
1235
                    // (example: DefaultResolution)
1236
                    // so invent that key here and have a default value
1237
0
                    std::unique_ptr<PPDKey> pKey(new PPDKey( aKey ));
1238
0
                    pKey->insertValue(aOption, PPDValueType::Invocation /*or what ?*/);
1239
0
                    pKey->m_pDefaultValue = pKey->getValue( aOption );
1240
0
                    insertKey( std::move(pKey) );
1241
0
                }
1242
0
            }
1243
0
        }
1244
0
        else if (aLine.startsWith("*UIConstraints") ||
1245
0
                 aLine.startsWith("*NonUIConstraints"))
1246
0
        {
1247
0
            parseConstraint( aLine );
1248
0
        }
1249
0
    }
1250
0
}
1251
1252
void PPDParser::parseOpenUI(const OString& rLine, std::string_view rPPDGroup)
1253
0
{
1254
0
    OUString aTranslation;
1255
0
    OString aKey = rLine;
1256
1257
0
    sal_Int32 nPos = aKey.indexOf(':');
1258
0
    if( nPos != -1 )
1259
0
        aKey = aKey.copy(0, nPos);
1260
0
    nPos = aKey.indexOf('/');
1261
0
    if( nPos != -1 )
1262
0
    {
1263
0
        aTranslation = handleTranslation( aKey.copy( nPos + 1 ), false );
1264
0
        aKey = aKey.copy(0, nPos);
1265
0
    }
1266
0
    aKey = GetCommandLineToken( 1, aKey );
1267
0
    aKey = aKey.copy(1);
1268
1269
0
    OUString aUniKey(OStringToOUString(aKey, RTL_TEXTENCODING_MS_1252));
1270
0
    PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aUniKey );
1271
0
    PPDKey* pKey;
1272
0
    if( keyit == m_aKeys.end() )
1273
0
    {
1274
0
        pKey = new PPDKey( aUniKey );
1275
0
        insertKey( std::unique_ptr<PPDKey>(pKey) );
1276
0
    }
1277
0
    else
1278
0
        pKey = keyit->second.get();
1279
1280
0
    pKey->m_bUIOption = true;
1281
0
    m_pTranslator->insertKey( pKey->getKey(), aTranslation );
1282
1283
0
    pKey->m_aGroup = OStringToOUString(rPPDGroup, RTL_TEXTENCODING_MS_1252);
1284
0
}
1285
1286
void PPDParser::parseOrderDependency(const OString& rLine)
1287
0
{
1288
0
    OString aLine(rLine);
1289
0
    sal_Int32 nPos = aLine.indexOf(':');
1290
0
    if( nPos != -1 )
1291
0
        aLine = aLine.copy( nPos+1 );
1292
1293
0
    sal_Int32 nOrder = GetCommandLineToken( 0, aLine ).toInt32();
1294
0
    OUString aKey(OStringToOUString(GetCommandLineToken(2, aLine), RTL_TEXTENCODING_MS_1252));
1295
0
    if( aKey[ 0 ] != '*' )
1296
0
        return; // invalid order dependency
1297
0
    aKey = aKey.replaceAt( 0, 1, u"" );
1298
1299
0
    PPDKey* pKey;
1300
0
    PPDParser::hash_type::const_iterator keyit = m_aKeys.find( aKey );
1301
0
    if( keyit == m_aKeys.end() )
1302
0
    {
1303
0
        pKey = new PPDKey( aKey );
1304
0
        insertKey( std::unique_ptr<PPDKey>(pKey) );
1305
0
    }
1306
0
    else
1307
0
        pKey = keyit->second.get();
1308
1309
0
    pKey->m_nOrderDependency = nOrder;
1310
0
}
1311
1312
void PPDParser::parseConstraint( const OString& rLine )
1313
0
{
1314
0
    bool bFailed = false;
1315
1316
0
    OUString aLine(OStringToOUString(rLine, RTL_TEXTENCODING_MS_1252));
1317
0
    sal_Int32 nIdx = rLine.indexOf(':');
1318
0
    if (nIdx != -1)
1319
0
        aLine = aLine.replaceAt(0, nIdx + 1, u"");
1320
0
    PPDConstraint aConstraint;
1321
0
    int nTokens = GetCommandLineTokenCount( aLine );
1322
0
    for( int i = 0; i < nTokens; i++ )
1323
0
    {
1324
0
        OUString aToken = GetCommandLineToken( i, aLine );
1325
0
        if( !aToken.isEmpty() && aToken[ 0 ] == '*' )
1326
0
        {
1327
0
            aToken = aToken.replaceAt( 0, 1, u"" );
1328
0
            if( aConstraint.m_pKey1 )
1329
0
                aConstraint.m_pKey2 = getKey( aToken );
1330
0
            else
1331
0
                aConstraint.m_pKey1 = getKey( aToken );
1332
0
        }
1333
0
        else
1334
0
        {
1335
0
            if( aConstraint.m_pKey2 )
1336
0
            {
1337
0
                if( ! ( aConstraint.m_pOption2 = aConstraint.m_pKey2->getValue( aToken ) ) )
1338
0
                    bFailed = true;
1339
0
            }
1340
0
            else if( aConstraint.m_pKey1 )
1341
0
            {
1342
0
                if( ! ( aConstraint.m_pOption1 = aConstraint.m_pKey1->getValue( aToken ) ) )
1343
0
                    bFailed = true;
1344
0
            }
1345
0
            else
1346
                // constraint for nonexistent keys; this happens
1347
                // e.g. in HP4PLUS3
1348
0
                bFailed = true;
1349
0
        }
1350
0
    }
1351
    // there must be two keywords
1352
0
    if( ! aConstraint.m_pKey1 || ! aConstraint.m_pKey2 || bFailed )
1353
0
    {
1354
0
        SAL_INFO("vcl.unx.print",
1355
0
                "Warning: constraint \"" << rLine << "\" is invalid");
1356
0
    }
1357
0
    else
1358
0
        m_aConstraints.push_back( aConstraint );
1359
0
}
1360
1361
const OUString & PPDParser::getDefaultPaperDimension() const
1362
0
{
1363
0
    if( m_pDefaultPaperDimension )
1364
0
        return m_pDefaultPaperDimension->m_aOption;
1365
1366
0
    return EMPTY_OUSTRING;
1367
0
}
1368
1369
bool PPDParser::getMargins(
1370
                           std::u16string_view rPaperName,
1371
                           int& rLeft, int& rRight,
1372
                           int& rUpper, int& rLower ) const
1373
0
{
1374
0
    if( ! m_pImageableAreas || ! m_pPaperDimensions )
1375
0
        return false;
1376
1377
0
    int nPDim=-1, nImArea=-1, i;
1378
0
    for( i = 0; i < m_pImageableAreas->countValues(); i++ )
1379
0
        if( rPaperName == m_pImageableAreas->getValue( i )->m_aOption )
1380
0
            nImArea = i;
1381
0
    for( i = 0; i < m_pPaperDimensions->countValues(); i++ )
1382
0
        if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1383
0
            nPDim = i;
1384
0
    if( nPDim == -1 || nImArea == -1 )
1385
0
        return false;
1386
1387
0
    double ImLLx, ImLLy, ImURx, ImURy;
1388
0
    double PDWidth, PDHeight;
1389
0
    OUString aArea = m_pImageableAreas->getValue( nImArea )->m_aValue;
1390
0
    ImLLx = StringToDouble( GetCommandLineToken( 0, aArea ) );
1391
0
    ImLLy = StringToDouble( GetCommandLineToken( 1, aArea ) );
1392
0
    ImURx = StringToDouble( GetCommandLineToken( 2, aArea ) );
1393
0
    ImURy = StringToDouble( GetCommandLineToken( 3, aArea ) );
1394
0
    aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1395
0
    PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1396
0
    PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1397
0
    rLeft  = static_cast<int>(ImLLx + 0.5);
1398
0
    rLower = static_cast<int>(ImLLy + 0.5);
1399
0
    rUpper = static_cast<int>(PDHeight - ImURy + 0.5);
1400
0
    rRight = static_cast<int>(PDWidth - ImURx + 0.5);
1401
1402
0
    return true;
1403
0
}
1404
1405
bool PPDParser::getPaperDimension(
1406
                                  std::u16string_view rPaperName,
1407
                                  int& rWidth, int& rHeight ) const
1408
0
{
1409
0
    if( ! m_pPaperDimensions )
1410
0
        return false;
1411
1412
0
    int nPDim=-1;
1413
0
    for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1414
0
        if( rPaperName == m_pPaperDimensions->getValue( i )->m_aOption )
1415
0
            nPDim = i;
1416
0
    if( nPDim == -1 )
1417
0
        return false;
1418
1419
0
    double PDWidth, PDHeight;
1420
0
    OUString aArea = m_pPaperDimensions->getValue( nPDim )->m_aValue;
1421
0
    PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1422
0
    PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1423
0
    rHeight = static_cast<int>(PDHeight + 0.5);
1424
0
    rWidth  = static_cast<int>(PDWidth + 0.5);
1425
1426
0
    return true;
1427
0
}
1428
1429
OUString PPDParser::matchPaperImpl(int nWidth, int nHeight, bool bSwapped, psp::orientation* pOrientation) const
1430
0
{
1431
0
    if( ! m_pPaperDimensions )
1432
0
        return OUString();
1433
1434
0
    int nPDim = -1;
1435
0
    double fSort = 2e36, fNewSort;
1436
1437
0
    for( int i = 0; i < m_pPaperDimensions->countValues(); i++ )
1438
0
    {
1439
0
        OUString aArea =  m_pPaperDimensions->getValue( i )->m_aValue;
1440
0
        double PDWidth     = StringToDouble( GetCommandLineToken( 0, aArea ) );
1441
0
        double PDHeight    = StringToDouble( GetCommandLineToken( 1, aArea ) );
1442
0
        PDWidth     /= static_cast<double>(nWidth);
1443
0
        PDHeight    /= static_cast<double>(nHeight);
1444
0
        if( PDWidth >= 0.9      &&  PDWidth <= 1.1      &&
1445
0
            PDHeight >= 0.9     &&  PDHeight <= 1.1         )
1446
0
        {
1447
0
            fNewSort =
1448
0
                (1.0-PDWidth)*(1.0-PDWidth) + (1.0-PDHeight)*(1.0-PDHeight);
1449
0
            if( fNewSort == 0.0 ) // perfect match
1450
0
                return m_pPaperDimensions->getValue( i )->m_aOption;
1451
1452
0
            if( fNewSort < fSort )
1453
0
            {
1454
0
                fSort = fNewSort;
1455
0
                nPDim = i;
1456
0
            }
1457
0
        }
1458
0
    }
1459
1460
0
    if (nPDim == -1 && !bSwapped)
1461
0
    {
1462
        // swap portrait/landscape and try again
1463
0
        return matchPaperImpl(nHeight, nWidth, true, pOrientation);
1464
0
    }
1465
1466
0
    if (nPDim == -1)
1467
0
        return OUString();
1468
1469
0
    if (bSwapped && pOrientation)
1470
0
    {
1471
0
        switch (*pOrientation)
1472
0
        {
1473
0
            case psp::orientation::Portrait:
1474
0
                *pOrientation = psp::orientation::Landscape;
1475
0
            break;
1476
0
            case psp::orientation::Landscape:
1477
0
                *pOrientation = psp::orientation::Portrait;
1478
0
            break;
1479
0
        }
1480
0
    }
1481
1482
0
    return m_pPaperDimensions->getValue( nPDim )->m_aOption;
1483
0
}
1484
1485
OUString PPDParser::matchPaper(int nWidth, int nHeight, psp::orientation* pOrientation) const
1486
0
{
1487
0
    return matchPaperImpl(nHeight, nWidth, false, pOrientation);
1488
0
}
1489
1490
const OUString & PPDParser::getDefaultInputSlot() const
1491
0
{
1492
0
    if( m_pDefaultInputSlot )
1493
0
        return m_pDefaultInputSlot->m_aValue;
1494
0
    return EMPTY_OUSTRING;
1495
0
}
1496
1497
void PPDParser::getResolutionFromString(std::u16string_view rString,
1498
                                        int& rXRes, int& rYRes )
1499
0
{
1500
0
    rXRes = rYRes = 300;
1501
1502
0
    const size_t nDPIPos {rString.find( u"dpi" )};
1503
0
    if( nDPIPos != std::u16string_view::npos )
1504
0
    {
1505
0
        const size_t nPos {rString.find( 'x' )};
1506
0
        if( nPos != std::u16string_view::npos )
1507
0
        {
1508
0
            rXRes = o3tl::toInt32(rString.substr( 0, nPos ));
1509
0
            rYRes = o3tl::toInt32(rString.substr(nPos+1, nDPIPos - nPos - 1));
1510
0
        }
1511
0
        else
1512
0
            rXRes = rYRes = o3tl::toInt32(rString.substr( 0, nDPIPos ));
1513
0
    }
1514
0
}
1515
1516
void PPDParser::getDefaultResolution( int& rXRes, int& rYRes ) const
1517
0
{
1518
0
    if( m_pDefaultResolution )
1519
0
    {
1520
0
        getResolutionFromString( m_pDefaultResolution->m_aValue, rXRes, rYRes );
1521
0
        return;
1522
0
    }
1523
1524
0
    rXRes = 300;
1525
0
    rYRes = 300;
1526
0
}
1527
1528
OUString PPDParser::translateKey( const OUString& i_rKey ) const
1529
0
{
1530
0
    OUString aResult( m_pTranslator->translateKey( i_rKey ) );
1531
0
    if( aResult.isEmpty() )
1532
0
        aResult = i_rKey;
1533
0
    return aResult;
1534
0
}
1535
1536
OUString PPDParser::translateOption( std::u16string_view i_rKey,
1537
                                          const OUString& i_rOption ) const
1538
0
{
1539
0
    OUString aResult( m_pTranslator->translateOption( i_rKey, i_rOption ) );
1540
0
    if( aResult.isEmpty() )
1541
0
        aResult = i_rOption;
1542
0
    return aResult;
1543
0
}
1544
1545
/*
1546
 *  PPDKey
1547
 */
1548
1549
PPDKey::PPDKey( OUString aKey ) :
1550
0
        m_aKey(std::move( aKey )),
1551
0
        m_pDefaultValue( nullptr ),
1552
0
        m_bQueryValue( false ),
1553
0
        m_bUIOption( false ),
1554
0
        m_nOrderDependency( 100 )
1555
0
{
1556
0
}
1557
1558
PPDKey::~PPDKey()
1559
0
{
1560
0
}
1561
1562
const PPDValue* PPDKey::getValue( int n ) const
1563
0
{
1564
0
    return (n >= 0 && o3tl::make_unsigned(n) < m_aOrderedValues.size()) ? m_aOrderedValues[n] : nullptr;
1565
0
}
1566
1567
const PPDValue* PPDKey::getValue( const OUString& rOption ) const
1568
0
{
1569
0
    PPDKey::hash_type::const_iterator it = m_aValues.find( rOption );
1570
0
    return it != m_aValues.end() ? &it->second : nullptr;
1571
0
}
1572
1573
const PPDValue* PPDKey::getValueCaseInsensitive( const OUString& rOption ) const
1574
0
{
1575
0
    const PPDValue* pValue = getValue( rOption );
1576
0
    if( ! pValue )
1577
0
    {
1578
0
        for( size_t n = 0; n < m_aOrderedValues.size() && ! pValue; n++ )
1579
0
            if( m_aOrderedValues[n]->m_aOption.equalsIgnoreAsciiCase( rOption ) )
1580
0
                pValue = m_aOrderedValues[n];
1581
0
    }
1582
1583
0
    return pValue;
1584
0
}
1585
1586
void PPDKey::eraseValue( const OUString& rOption )
1587
0
{
1588
0
    PPDKey::hash_type::iterator it = m_aValues.find( rOption );
1589
0
    if( it == m_aValues.end() )
1590
0
        return;
1591
1592
0
    auto vit = std::find(m_aOrderedValues.begin(), m_aOrderedValues.end(), &(it->second ));
1593
0
    if( vit != m_aOrderedValues.end() )
1594
0
        m_aOrderedValues.erase( vit );
1595
1596
0
    m_aValues.erase( it );
1597
0
}
1598
1599
PPDValue* PPDKey::insertValue(const OUString& rOption, PPDValueType eType, bool bCustomOption)
1600
0
{
1601
0
    if( m_aValues.find( rOption ) != m_aValues.end() )
1602
0
        return nullptr;
1603
1604
0
    PPDValue aValue;
1605
0
    aValue.m_aOption = rOption;
1606
0
    aValue.m_bCustomOption = bCustomOption;
1607
0
    aValue.m_bCustomOptionSetViaApp = false;
1608
0
    aValue.m_eType = eType;
1609
0
    m_aValues[rOption] = std::move(aValue);
1610
0
    PPDValue* pValue = &m_aValues[rOption];
1611
0
    m_aOrderedValues.push_back( pValue );
1612
0
    return pValue;
1613
0
}
1614
1615
/*
1616
 * PPDContext
1617
 */
1618
1619
PPDContext::PPDContext() :
1620
0
        m_pParser( nullptr )
1621
0
{
1622
0
}
1623
1624
PPDContext& PPDContext::operator=( PPDContext&& rCopy )
1625
0
{
1626
0
    std::swap(m_pParser, rCopy.m_pParser);
1627
0
    std::swap(m_aCurrentValues, rCopy.m_aCurrentValues);
1628
0
    return *this;
1629
0
}
1630
1631
const PPDKey* PPDContext::getModifiedKey( std::size_t n ) const
1632
0
{
1633
0
    if( m_aCurrentValues.size() <= n )
1634
0
        return nullptr;
1635
1636
0
    hash_type::const_iterator it = m_aCurrentValues.begin();
1637
0
    std::advance(it, n);
1638
0
    return it->first;
1639
0
}
1640
1641
void PPDContext::setParser( const PPDParser* pParser )
1642
0
{
1643
0
    if( pParser != m_pParser )
1644
0
    {
1645
0
        m_aCurrentValues.clear();
1646
0
        m_pParser = pParser;
1647
0
    }
1648
0
}
1649
1650
const PPDValue* PPDContext::getValue( const PPDKey* pKey ) const
1651
0
{
1652
0
    if( ! m_pParser )
1653
0
        return nullptr;
1654
1655
0
    hash_type::const_iterator it = m_aCurrentValues.find( pKey );
1656
0
    if( it != m_aCurrentValues.end() )
1657
0
        return it->second;
1658
1659
0
    if( ! m_pParser->hasKey( pKey ) )
1660
0
        return nullptr;
1661
1662
0
    const PPDValue* pValue = pKey->getDefaultValue();
1663
0
    if( ! pValue )
1664
0
        pValue = pKey->getValue( 0 );
1665
1666
0
    return pValue;
1667
0
}
1668
1669
const PPDValue* PPDContext::setValue( const PPDKey* pKey, const PPDValue* pValue, bool bDontCareForConstraints )
1670
0
{
1671
0
    if( ! m_pParser || ! pKey )
1672
0
        return nullptr;
1673
1674
    // pValue can be NULL - it means ignore this option
1675
1676
0
    if( ! m_pParser->hasKey( pKey ) )
1677
0
        return nullptr;
1678
1679
    // check constraints
1680
0
    if( pValue )
1681
0
    {
1682
0
        if( bDontCareForConstraints )
1683
0
        {
1684
0
            m_aCurrentValues[ pKey ] = pValue;
1685
0
        }
1686
0
        else if( checkConstraints( pKey, pValue, true ) )
1687
0
        {
1688
0
            m_aCurrentValues[ pKey ] = pValue;
1689
1690
            // after setting this value, check all constraints !
1691
0
            hash_type::iterator it = m_aCurrentValues.begin();
1692
0
            while(  it != m_aCurrentValues.end() )
1693
0
            {
1694
0
                if( it->first != pKey &&
1695
0
                    ! checkConstraints( it->first, it->second, false ) )
1696
0
                {
1697
0
                    SAL_INFO("vcl.unx.print", "PPDContext::setValue: option "
1698
0
                         << it->first->getKey()
1699
0
                         << " (" << it->second->m_aOption
1700
0
                         << ") is constrained after setting "
1701
0
                         << pKey->getKey()
1702
0
                         << " to " << pValue->m_aOption);
1703
0
                    resetValue( it->first, true );
1704
0
                    it = m_aCurrentValues.begin();
1705
0
                }
1706
0
                else
1707
0
                    ++it;
1708
0
            }
1709
0
        }
1710
0
    }
1711
0
    else
1712
0
        m_aCurrentValues[ pKey ] = nullptr;
1713
1714
0
    return pValue;
1715
0
}
1716
1717
bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pValue )
1718
0
{
1719
0
    if( ! m_pParser || ! pKey || ! pValue )
1720
0
        return false;
1721
1722
    // ensure that this key is already in the list if it exists at all
1723
0
    if( m_aCurrentValues.find( pKey ) != m_aCurrentValues.end() )
1724
0
        return checkConstraints( pKey, pValue, false );
1725
1726
    // it is not in the list, insert it temporarily
1727
0
    bool bRet = false;
1728
0
    if( m_pParser->hasKey( pKey ) )
1729
0
    {
1730
0
        const PPDValue* pDefValue = pKey->getDefaultValue();
1731
0
        m_aCurrentValues[ pKey ] = pDefValue;
1732
0
        bRet = checkConstraints( pKey, pValue, false );
1733
0
        m_aCurrentValues.erase( pKey );
1734
0
    }
1735
1736
0
    return bRet;
1737
0
}
1738
1739
bool PPDContext::resetValue( const PPDKey* pKey, bool bDefaultable )
1740
0
{
1741
0
    if( ! pKey || ! m_pParser || ! m_pParser->hasKey( pKey ) )
1742
0
        return false;
1743
1744
0
    const PPDValue* pResetValue = pKey->getValue( u"None"_ustr );
1745
0
    if( ! pResetValue )
1746
0
        pResetValue = pKey->getValue( u"False"_ustr );
1747
0
    if( ! pResetValue && bDefaultable )
1748
0
        pResetValue = pKey->getDefaultValue();
1749
1750
0
    bool bRet = pResetValue && ( setValue( pKey, pResetValue ) == pResetValue );
1751
1752
0
    return bRet;
1753
0
}
1754
1755
bool PPDContext::checkConstraints( const PPDKey* pKey, const PPDValue* pNewValue, bool bDoReset )
1756
0
{
1757
0
    if( ! pNewValue )
1758
0
        return true;
1759
1760
    // sanity checks
1761
0
    if( ! m_pParser )
1762
0
        return false;
1763
1764
0
    if( pKey->getValue( pNewValue->m_aOption ) != pNewValue )
1765
0
        return false;
1766
1767
    // None / False and the default can always be set, but be careful !
1768
    // setting them might influence constrained values
1769
0
    if( pNewValue->m_aOption == "None" || pNewValue->m_aOption == "False" ||
1770
0
        pNewValue == pKey->getDefaultValue() )
1771
0
        return true;
1772
1773
0
    const ::std::vector< PPDParser::PPDConstraint >& rConstraints( m_pParser->getConstraints() );
1774
0
    for (auto const& constraint : rConstraints)
1775
0
    {
1776
0
        const PPDKey* pLeft     = constraint.m_pKey1;
1777
0
        const PPDKey* pRight    = constraint.m_pKey2;
1778
0
        if( ! pLeft || ! pRight || ( pKey != pLeft && pKey != pRight ) )
1779
0
            continue;
1780
1781
0
        const PPDKey* pOtherKey = pKey == pLeft ? pRight : pLeft;
1782
0
        const PPDValue* pOtherKeyOption = pKey == pLeft ? constraint.m_pOption2 : constraint.m_pOption1;
1783
0
        const PPDValue* pKeyOption = pKey == pLeft ? constraint.m_pOption1 : constraint.m_pOption2;
1784
1785
        // syntax *Key1 option1 *Key2 option2
1786
0
        if( pKeyOption && pOtherKeyOption )
1787
0
        {
1788
0
            if( pNewValue != pKeyOption )
1789
0
                continue;
1790
0
            if( pOtherKeyOption == getValue( pOtherKey ) )
1791
0
            {
1792
0
                return false;
1793
0
            }
1794
0
        }
1795
        // syntax *Key1 option *Key2  or  *Key1 *Key2 option
1796
0
        else if( pOtherKeyOption || pKeyOption )
1797
0
        {
1798
0
            if( pKeyOption )
1799
0
            {
1800
0
                if( ! ( pOtherKeyOption = getValue( pOtherKey ) ) )
1801
0
                    continue; // this should not happen, PPD broken
1802
1803
0
                if( pKeyOption == pNewValue &&
1804
0
                    pOtherKeyOption->m_aOption != "None" &&
1805
0
                    pOtherKeyOption->m_aOption != "False" )
1806
0
                {
1807
                    // check if the other value can be reset and
1808
                    // do so if possible
1809
0
                    if( bDoReset && resetValue( pOtherKey ) )
1810
0
                        continue;
1811
1812
0
                    return false;
1813
0
                }
1814
0
            }
1815
0
            else if( pOtherKeyOption )
1816
0
            {
1817
0
                if( getValue( pOtherKey ) == pOtherKeyOption &&
1818
0
                    pNewValue->m_aOption != "None" &&
1819
0
                    pNewValue->m_aOption != "False" )
1820
0
                    return false;
1821
0
            }
1822
0
            else
1823
0
            {
1824
                // this should not happen, PPD is broken
1825
0
            }
1826
0
        }
1827
        // syntax *Key1 *Key2
1828
0
        else
1829
0
        {
1830
0
            const PPDValue* pOtherValue = getValue( pOtherKey );
1831
0
            if( pOtherValue->m_aOption != "None"  &&
1832
0
                pOtherValue->m_aOption != "False" &&
1833
0
                pNewValue->m_aOption != "None"    &&
1834
0
                pNewValue->m_aOption != "False" )
1835
0
                return false;
1836
0
        }
1837
0
    }
1838
0
    return true;
1839
0
}
1840
1841
char* PPDContext::getStreamableBuffer( sal_uLong& rBytes ) const
1842
0
{
1843
0
    rBytes = 0;
1844
0
    if( m_aCurrentValues.empty() )
1845
0
        return nullptr;
1846
0
    for (auto const& elem : m_aCurrentValues)
1847
0
    {
1848
0
        OString aCopy(OUStringToOString(elem.first->getKey(), RTL_TEXTENCODING_MS_1252));
1849
0
        rBytes += aCopy.getLength();
1850
0
        rBytes += 1; // for ':'
1851
0
        if( elem.second )
1852
0
        {
1853
0
            aCopy = OUStringToOString(elem.second->m_aOption, RTL_TEXTENCODING_MS_1252);
1854
0
            rBytes += aCopy.getLength();
1855
0
        }
1856
0
        else
1857
0
            rBytes += 4;
1858
0
        rBytes += 1; // for '\0'
1859
0
    }
1860
0
    rBytes += 1;
1861
0
    char* pBuffer = new char[ rBytes ];
1862
0
    memset( pBuffer, 0, rBytes );
1863
0
    char* pRun = pBuffer;
1864
0
    for (auto const& elem : m_aCurrentValues)
1865
0
    {
1866
0
        OString aCopy(OUStringToOString(elem.first->getKey(), RTL_TEXTENCODING_MS_1252));
1867
0
        int nBytes = aCopy.getLength();
1868
0
        memcpy( pRun, aCopy.getStr(), nBytes );
1869
0
        pRun += nBytes;
1870
0
        *pRun++ = ':';
1871
0
        if( elem.second )
1872
0
            aCopy = OUStringToOString(elem.second->m_aOption, RTL_TEXTENCODING_MS_1252);
1873
0
        else
1874
0
            aCopy = "*nil"_ostr;
1875
0
        nBytes = aCopy.getLength();
1876
0
        memcpy( pRun, aCopy.getStr(), nBytes );
1877
0
        pRun += nBytes;
1878
1879
0
        *pRun++ = 0;
1880
0
    }
1881
0
    return pBuffer;
1882
0
}
1883
1884
void PPDContext::rebuildFromStreamBuffer(const std::vector<char> &rBuffer)
1885
0
{
1886
0
    if( ! m_pParser )
1887
0
        return;
1888
1889
0
    m_aCurrentValues.clear();
1890
1891
0
    const size_t nBytes = rBuffer.size() - 1;
1892
0
    size_t nRun = 0;
1893
0
    while (nRun < nBytes && rBuffer[nRun])
1894
0
    {
1895
0
        OString aLine(rBuffer.data() + nRun);
1896
0
        sal_Int32 nPos = aLine.indexOf(':');
1897
0
        if( nPos != -1 )
1898
0
        {
1899
0
            const PPDKey* pKey = m_pParser->getKey( OStringToOUString( aLine.subView( 0, nPos ), RTL_TEXTENCODING_MS_1252 ) );
1900
0
            if( pKey )
1901
0
            {
1902
0
                const PPDValue* pValue = nullptr;
1903
0
                OUString aOption(
1904
0
                    OStringToOUString(aLine.subView(nPos+1), RTL_TEXTENCODING_MS_1252));
1905
0
                if (aOption != "*nil")
1906
0
                    pValue = pKey->getValue( aOption );
1907
0
                m_aCurrentValues[ pKey ] = pValue;
1908
0
                SAL_INFO("vcl.unx.print",
1909
0
                    "PPDContext::rebuildFromStreamBuffer: read PPDKeyValue { "
1910
0
                    << pKey->getKey() << " , "
1911
0
                    << (pValue ? aOption : u"<nil>"_ustr)
1912
0
                    << " }");
1913
0
            }
1914
0
        }
1915
0
        nRun += aLine.getLength()+1;
1916
0
    }
1917
0
}
1918
1919
int PPDContext::getRenderResolution() const
1920
0
{
1921
    // initialize to reasonable default, if parser is not set
1922
0
    int nDPI = 300;
1923
0
    if( m_pParser )
1924
0
    {
1925
0
        int nDPIx = 300, nDPIy = 300;
1926
0
        const PPDKey* pKey = m_pParser->getKey( u"Resolution"_ustr );
1927
0
        if( pKey )
1928
0
        {
1929
0
            const PPDValue* pValue = getValue( pKey );
1930
0
            if( pValue )
1931
0
                PPDParser::getResolutionFromString( pValue->m_aOption, nDPIx, nDPIy );
1932
0
            else
1933
0
                m_pParser->getDefaultResolution( nDPIx, nDPIy );
1934
0
        }
1935
0
        else
1936
0
            m_pParser->getDefaultResolution( nDPIx, nDPIy );
1937
1938
0
        nDPI = std::max(nDPIx, nDPIy);
1939
0
    }
1940
0
    return  nDPI;
1941
0
}
1942
1943
void PPDContext::getPageSize( OUString& rPaper, int& rWidth, int& rHeight ) const
1944
0
{
1945
    // initialize to reasonable default, if parser is not set
1946
0
    rPaper  = "A4";
1947
0
    rWidth  = 595;
1948
0
    rHeight = 842;
1949
0
    if( !m_pParser )
1950
0
        return;
1951
1952
0
    const PPDKey* pKey = m_pParser->getKey( u"PageSize"_ustr );
1953
0
    if( !pKey )
1954
0
        return;
1955
1956
0
    const PPDValue* pValue = getValue( pKey );
1957
0
    if( pValue )
1958
0
    {
1959
0
        rPaper = pValue->m_aOption;
1960
0
        m_pParser->getPaperDimension( rPaper, rWidth, rHeight );
1961
0
    }
1962
0
    else
1963
0
    {
1964
0
        rPaper = m_pParser->getDefaultPaperDimension();
1965
0
        m_pParser->getDefaultPaperDimension( rWidth, rHeight );
1966
0
    }
1967
0
}
1968
1969
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */