Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/unx/generic/print/genprnpsp.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
/**
21
  this file implements the sal printer interface (SalPrinter, SalInfoPrinter
22
  and some printer relevant methods of SalInstance and SalGraphicsData)
23
24
  as underlying library the printer features of psprint are used.
25
26
  The query methods of a SalInfoPrinter are implemented by querying psprint
27
28
  The job methods of a SalPrinter are implemented by calling psprint
29
  printer job functions.
30
 */
31
32
#include <sal/config.h>
33
34
#include <string_view>
35
36
// For spawning PDF and FAX generation
37
38
#include <comphelper/fileurl.hxx>
39
#include <o3tl/safeint.hxx>
40
#include <o3tl/unit_conversion.hxx>
41
#include <tools/mapunit.hxx>
42
#include <rtl/ustrbuf.hxx>
43
#include <rtl/ustring.hxx>
44
#include <sal/log.hxx>
45
#include <osl/diagnose.h>
46
#include <osl/file.hxx>
47
48
#include <utility>
49
#include <vcl/gdimtf.hxx>
50
#include <vcl/idle.hxx>
51
#include <vcl/printer/Options.hxx>
52
#include <vcl/print.hxx>
53
#include <vcl/PrinterSupport.hxx>
54
#include <vcl/QueueInfo.hxx>
55
#include <vcl/pdfwriter.hxx>
56
#include <vcl/svapp.hxx>
57
#include <vcl/settings.hxx>
58
#include <vcl/weld/Dialog.hxx>
59
60
#include <strings.hrc>
61
#include <unx/genprn.h>
62
#include <unx/geninst.h>
63
#include <unx/genpspgraphics.h>
64
65
#include <jobset.h>
66
#include <print.h>
67
#include "prtsetup.hxx"
68
#include <salptype.hxx>
69
70
#include <com/sun/star/beans/PropertyValue.hpp>
71
#include <com/sun/star/view/PrintableState.hpp>
72
73
using namespace psp;
74
using namespace com::sun::star;
75
76
namespace
77
{
78
    class QueryString : public weld::GenericDialogController
79
    {
80
    private:
81
        OUString&           m_rReturnValue;
82
83
        std::unique_ptr<weld::Button> m_xOKButton;
84
        std::unique_ptr<weld::Label> m_xFixedText;
85
        std::unique_ptr<weld::Entry> m_xEdit;
86
87
        DECL_LINK( ClickBtnHdl, weld::Button&, void );
88
89
    public:
90
        // parent window, Query text, initial value
91
        QueryString(weld::Window*, OUString const &, OUString &);
92
    };
93
94
    /*
95
     *  QueryString
96
     */
97
    QueryString::QueryString(weld::Window* pParent, OUString const & rQuery, OUString& rRet)
98
0
        : GenericDialogController(pParent, u"vcl/ui/querydialog.ui"_ustr, u"QueryDialog"_ustr)
99
0
        , m_rReturnValue( rRet )
100
0
        , m_xOKButton(m_xBuilder->weld_button(u"ok"_ustr))
101
0
        , m_xFixedText(m_xBuilder->weld_label(u"label"_ustr))
102
0
        , m_xEdit(m_xBuilder->weld_entry(u"entry"_ustr))
103
0
    {
104
0
        m_xOKButton->connect_clicked(LINK(this, QueryString, ClickBtnHdl));
105
0
        m_xFixedText->set_label(rQuery);
106
0
        m_xEdit->set_text(m_rReturnValue);
107
0
        m_xDialog->set_title(rQuery);
108
0
    }
109
110
    IMPL_LINK(QueryString, ClickBtnHdl, weld::Button&, rButton, void)
111
0
    {
112
0
        if (&rButton == m_xOKButton.get())
113
0
        {
114
0
            m_rReturnValue = m_xEdit->get_text();
115
0
            m_xDialog->response(RET_OK);
116
0
        }
117
0
        else
118
0
            m_xDialog->response(RET_CANCEL);
119
0
    }
120
121
    int QueryFaxNumber(OUString& rNumber)
122
0
    {
123
0
        QueryString aQuery(Application::GetDefDialogParent(), VclResId(SV_PRINT_QUERYFAXNUMBER_TXT), rNumber);
124
0
        return aQuery.run();
125
0
    }
126
}
127
128
0
static int PtTo10Mu( int nPoints ) { return o3tl::convert(nPoints, o3tl::Length::pt, o3tl::Length::mm100); }
129
130
0
static int TenMuToPt( int nUnits ) { return o3tl::convert(nUnits, o3tl::Length::mm100, o3tl::Length::pt); }
131
132
static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
133
0
{
134
0
    pJobSetup->SetOrientation( rData.m_eOrientation == orientation::Landscape ?
135
0
        Orientation::Landscape : Orientation::Portrait );
136
137
    // copy page size
138
0
    OUString aPaper;
139
0
    int width, height;
140
141
0
    rData.m_aContext.getPageSize( aPaper, width, height );
142
0
    pJobSetup->SetPaperFormat( PaperInfo::fromPSName(
143
0
        OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 )));
144
145
0
    pJobSetup->SetPaperWidth( 0 );
146
0
    pJobSetup->SetPaperHeight( 0 );
147
0
    if( pJobSetup->GetPaperFormat() == PAPER_USER )
148
0
    {
149
        // transform to 100dth mm
150
0
        width               = PtTo10Mu( width );
151
0
        height              = PtTo10Mu( height );
152
153
0
        if( rData.m_eOrientation == psp::orientation::Portrait )
154
0
        {
155
0
            pJobSetup->SetPaperWidth( width );
156
0
            pJobSetup->SetPaperHeight( height );
157
0
        }
158
0
        else
159
0
        {
160
0
            pJobSetup->SetPaperWidth( height );
161
0
            pJobSetup->SetPaperHeight( width );
162
0
        }
163
0
    }
164
165
    // copy input slot
166
0
    const PPDKey* pKey = nullptr;
167
0
    const PPDValue* pValue = nullptr;
168
169
0
    pJobSetup->SetPaperBin( 0 );
170
0
    if( rData.m_pParser )
171
0
        pKey                    = rData.m_pParser->getKey( u"InputSlot"_ustr );
172
0
    if( pKey )
173
0
        pValue                  = rData.m_aContext.getValue( pKey );
174
0
    if( pKey && pValue )
175
0
    {
176
0
        int nPaperBin;
177
0
        for( nPaperBin = 0;
178
0
             pValue != pKey->getValue( nPaperBin ) &&
179
0
                 nPaperBin < pKey->countValues();
180
0
             nPaperBin++);
181
0
        pJobSetup->SetPaperBin(
182
0
            nPaperBin == pKey->countValues() ? 0 : nPaperBin);
183
0
    }
184
185
    // copy duplex
186
0
    pKey = nullptr;
187
0
    pValue = nullptr;
188
189
0
    pJobSetup->SetDuplexMode( DuplexMode::Unknown );
190
0
    if( rData.m_pParser )
191
0
        pKey = rData.m_pParser->getKey( u"Duplex"_ustr );
192
0
    if( pKey )
193
0
        pValue = rData.m_aContext.getValue( pKey );
194
0
    if( pKey && pValue )
195
0
    {
196
0
        if( pValue->m_aOption.equalsIgnoreAsciiCase( "None" ) ||
197
0
            pValue->m_aOption.startsWithIgnoreAsciiCase( "Simplex" )
198
0
           )
199
0
        {
200
0
            pJobSetup->SetDuplexMode( DuplexMode::Off);
201
0
        }
202
0
        else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
203
0
        {
204
0
            pJobSetup->SetDuplexMode( DuplexMode::LongEdge );
205
0
        }
206
0
        else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexTumble" ) )
207
0
        {
208
0
            pJobSetup->SetDuplexMode( DuplexMode::ShortEdge );
209
0
        }
210
0
    }
211
212
    // copy the whole context
213
214
0
    sal_uInt32 nBytes;
215
0
    std::unique_ptr<sal_uInt8[]> pBuffer;
216
0
    if( rData.getStreamBuffer( pBuffer, nBytes ) )
217
0
    {
218
0
        pJobSetup->SetDriverData( std::move(pBuffer), nBytes );
219
0
    }
220
0
    else
221
0
    {
222
0
        pJobSetup->SetDriverData( nullptr, 0 );
223
0
    }
224
0
    pJobSetup->SetPapersizeFromSetup( rData.m_bPapersizeFromSetup );
225
0
}
226
227
static std::vector<OUString> getFaxNumbers()
228
0
{
229
0
    std::vector<OUString> aFaxNumbers;
230
231
0
    OUString aNewNr;
232
0
    if (QueryFaxNumber(aNewNr))
233
0
    {
234
0
        for (sal_Int32 nIndex {0}; nIndex >= 0; )
235
0
            aFaxNumbers.push_back(aNewNr.getToken( 0, ';', nIndex ));
236
0
    }
237
238
0
    return aFaxNumbers;
239
0
}
240
241
/*
242
 *  SalInstance
243
 */
244
245
void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter& rPrinter,
246
                                                 const SalPrinterQueueInfo& rQueueInfo,
247
                                                 ImplJobSetup& rJobSetup)
248
0
{
249
0
    PrinterInfoManager& rManager( PrinterInfoManager::get() );
250
0
    PrinterInfo aInfo(rManager.getPrinterInfo(rQueueInfo.maPrinterName));
251
0
    rPrinter.m_aJobData = aInfo;
252
253
0
    if (rJobSetup.GetDriverData())
254
0
        JobData::constructFromStreamBuffer(rJobSetup.GetDriverData(), rJobSetup.GetDriverDataLen(),
255
0
                                           aInfo);
256
257
0
    rJobSetup.SetSystem(JOBSETUP_SYSTEM_UNIX);
258
0
    rJobSetup.SetPrinterName(rQueueInfo.maPrinterName);
259
0
    rJobSetup.SetDriver(aInfo.m_aDriverName);
260
0
    copyJobDataToJobSetup(&rJobSetup, aInfo);
261
0
}
262
263
bool SalGenericInstance::getPdfDir(const PrinterInfo& rInfo, OUString& rDir)
264
0
{
265
0
    sal_Int32 nIndex = 0;
266
0
    while (nIndex != -1)
267
0
    {
268
0
        OUString aToken(rInfo.m_aFeatures.getToken(0, ',', nIndex));
269
0
        if (aToken.startsWith("pdf="))
270
0
        {
271
0
            sal_Int32 nPos = 0;
272
0
            rDir = aToken.getToken(1, '=', nPos);
273
0
            if (rDir.isEmpty() && getenv("HOME"))
274
0
                rDir
275
0
                    = OUString(getenv("HOME"), strlen(getenv("HOME")), osl_getThreadTextEncoding());
276
0
            return true;
277
0
        }
278
0
    }
279
0
    return false;
280
0
}
281
282
SalInfoPrinter* SalGenericInstance::CreateInfoPrinter(SalPrinterQueueInfo& rQueueInfo,
283
                                                      ImplJobSetup& rJobSetup)
284
0
{
285
0
    mbPrinterInit = true;
286
    // create and initialize SalInfoPrinter
287
0
    PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter();
288
0
    configurePspInfoPrinter(*pPrinter, rQueueInfo, rJobSetup);
289
0
    return pPrinter;
290
0
}
291
292
std::unique_ptr<SalPrinter> SalGenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
293
0
{
294
0
    mbPrinterInit = true;
295
    // create and initialize SalPrinter
296
0
    PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
297
0
    pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
298
299
0
    return std::unique_ptr<SalPrinter>(pPrinter);
300
0
}
301
302
void SalGenericInstance::GetPrinterQueueInfo(ImplPrnQueueList& rList)
303
0
{
304
0
    mbPrinterInit = true;
305
0
    PrinterInfoManager& rManager( PrinterInfoManager::get() );
306
0
    static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
307
0
    if( ! pNoSyncDetection || ! *pNoSyncDetection )
308
0
    {
309
        // #i62663# synchronize possible asynchronouse printer detection now
310
0
        rManager.checkPrintersChanged( true );
311
0
    }
312
313
0
    for (const auto& [rPrinterName, rInfo] : rManager.getPrinters())
314
0
    {
315
        // create new entry
316
0
        std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
317
0
        pInfo->maPrinterName = rPrinterName;
318
0
        pInfo->maDriver         = rInfo.m_aDriverName;
319
0
        pInfo->maLocation       = rInfo.m_aLocation;
320
0
        pInfo->maComment        = rInfo.m_aComment;
321
322
0
        OUString sPdfDir;
323
0
        if (getPdfDir(rInfo, sPdfDir))
324
0
            pInfo->maLocation = sPdfDir;
325
326
0
        rList.Add(std::move(pInfo));
327
0
    }
328
0
}
329
330
OUString SalGenericInstance::GetDefaultPrinter()
331
0
{
332
0
    mbPrinterInit = true;
333
0
    PrinterInfoManager& rManager( PrinterInfoManager::get() );
334
0
    return rManager.getDefaultPrinter();
335
0
}
336
337
PspSalInfoPrinter::PspSalInfoPrinter()
338
0
{
339
0
}
340
341
PspSalInfoPrinter::~PspSalInfoPrinter()
342
0
{
343
0
}
344
345
void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
346
0
{
347
0
    m_aPaperFormats.clear();
348
0
    m_bPapersInit = true;
349
350
0
    if( !m_aJobData.m_pParser )
351
0
        return;
352
353
0
    const PPDKey* pKey = m_aJobData.m_pParser->getKey( u"PageSize"_ustr );
354
0
    if( pKey )
355
0
    {
356
0
        int nValues = pKey->countValues();
357
0
        for( int i = 0; i < nValues; i++ )
358
0
        {
359
0
            const PPDValue* pValue = pKey->getValue( i );
360
0
            int nWidth = 0, nHeight = 0;
361
0
            m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
362
0
            PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
363
0
            m_aPaperFormats.push_back( aInfo );
364
0
        }
365
0
    }
366
0
}
367
368
int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
369
0
{
370
0
    return 900;
371
0
}
372
373
SalGraphics* PspSalInfoPrinter::AcquireGraphics()
374
0
{
375
    // return a valid pointer only once
376
    // the reasoning behind this is that we could have different
377
    // SalGraphics that can run in multiple threads
378
    // (future plans)
379
0
    SalGraphics* pRet = nullptr;
380
0
    if( ! m_pGraphics )
381
0
    {
382
0
        m_pGraphics = GetGenericInstance()->CreatePrintGraphics();
383
0
        m_pGraphics->Init(&m_aJobData);
384
0
        pRet = m_pGraphics.get();
385
0
    }
386
0
    return pRet;
387
0
}
388
389
void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
390
0
{
391
0
    if( m_pGraphics.get() == pGraphics )
392
0
    {
393
0
        m_pGraphics.reset();
394
0
    }
395
0
}
396
397
bool PspSalInfoPrinter::Setup(weld::Window& rFrame, ImplJobSetup& rJobSetup)
398
0
{
399
0
    PrinterInfoManager& rManager = PrinterInfoManager::get();
400
401
0
    PrinterInfo aInfo(rManager.getPrinterInfo(rJobSetup.GetPrinterName()));
402
0
    if (rJobSetup.GetDriverData())
403
0
    {
404
0
        SetData(JobSetFlags::ALL, rJobSetup);
405
0
        JobData::constructFromStreamBuffer(rJobSetup.GetDriverData(), rJobSetup.GetDriverDataLen(),
406
0
                                           aInfo);
407
0
    }
408
0
    aInfo.m_bPapersizeFromSetup = rJobSetup.GetPapersizeFromSetup();
409
0
    aInfo.meSetupMode = rJobSetup.GetPrinterSetupMode();
410
411
0
    if (SetupPrinterDriver(&rFrame, aInfo))
412
0
    {
413
0
        rJobSetup.SetDriverData(nullptr, 0);
414
415
0
        sal_uInt32 nBytes;
416
0
        std::unique_ptr<sal_uInt8[]> pBuffer;
417
0
        aInfo.getStreamBuffer( pBuffer, nBytes );
418
0
        rJobSetup.SetDriverData(std::move(pBuffer), nBytes);
419
420
        // copy everything to job setup
421
0
        copyJobDataToJobSetup(&rJobSetup, aInfo);
422
0
        JobData::constructFromStreamBuffer(rJobSetup.GetDriverData(), rJobSetup.GetDriverDataLen(),
423
0
                                           m_aJobData);
424
0
        return true;
425
0
    }
426
0
    return false;
427
0
}
428
429
// This function gets the driver data and puts it into rJobSetup
430
// If rJobSetup.GetDriverData() is NOT NULL, then the independent
431
// data should be merged into the driver data
432
// If rJobSetup.GetDriverData() IS NULL, then the driver defaults
433
// should be merged into the independent data
434
bool PspSalInfoPrinter::SetPrinterData(ImplJobSetup& rJobSetup)
435
0
{
436
0
    if (rJobSetup.GetDriverData())
437
0
        return SetData(JobSetFlags::ALL, rJobSetup);
438
439
0
    copyJobDataToJobSetup(&rJobSetup, m_aJobData);
440
441
0
    return true;
442
0
}
443
444
bool PspSalInfoPrinter::SetData(JobSetFlags nSetDataFlags, ImplJobSetup& rJobSetup)
445
0
{
446
0
    JobData aData;
447
0
    JobData::constructFromStreamBuffer(rJobSetup.GetDriverData(), rJobSetup.GetDriverDataLen(),
448
0
                                       aData);
449
450
0
    if( aData.m_pParser )
451
0
    {
452
0
        const PPDKey* pKey;
453
0
        const PPDValue* pValue;
454
455
        // merge orientation if necessary
456
0
        if( nSetDataFlags & JobSetFlags::ORIENTATION )
457
0
            aData.m_eOrientation = rJobSetup.GetOrientation() == Orientation::Landscape
458
0
                                       ? orientation::Landscape
459
0
                                       : orientation::Portrait;
460
461
        // merge papersize if necessary
462
0
        if( nSetDataFlags & JobSetFlags::PAPERSIZE )
463
0
        {
464
0
            OUString aPaper;
465
466
0
            if (rJobSetup.GetPaperFormat() == PAPER_USER)
467
0
                aPaper = aData.m_pParser->matchPaper(TenMuToPt(rJobSetup.GetPaperWidth()),
468
0
                                                     TenMuToPt(rJobSetup.GetPaperHeight()),
469
0
                                                     &aData.m_eOrientation);
470
0
            else
471
0
                aPaper = OStringToOUString(PaperInfo::toPSName(rJobSetup.GetPaperFormat()),
472
0
                                           RTL_TEXTENCODING_ISO_8859_1);
473
474
0
            pKey = aData.m_pParser->getKey( u"PageSize"_ustr );
475
0
            pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : nullptr;
476
477
            // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
478
            // try to find the correct paper anyway using the size
479
0
            if (pKey && !pValue && rJobSetup.GetPaperFormat() != PAPER_USER)
480
0
            {
481
0
                PaperInfo aInfo(rJobSetup.GetPaperFormat());
482
0
                aPaper = aData.m_pParser->matchPaper(
483
0
                    TenMuToPt( aInfo.getWidth() ),
484
0
                    TenMuToPt( aInfo.getHeight() ),
485
0
                    &aData.m_eOrientation );
486
0
                pValue = pKey->getValueCaseInsensitive( aPaper );
487
0
            }
488
489
0
            if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue ) == pValue ) )
490
0
                return false;
491
0
        }
492
493
        // merge paperbin if necessary
494
0
        if( nSetDataFlags & JobSetFlags::PAPERBIN )
495
0
        {
496
0
            pKey = aData.m_pParser->getKey( u"InputSlot"_ustr );
497
0
            if( pKey )
498
0
            {
499
0
                int nPaperBin = rJobSetup.GetPaperBin();
500
0
                if( nPaperBin >= pKey->countValues() )
501
0
                    pValue = pKey->getDefaultValue();
502
0
                else
503
0
                    pValue = pKey->getValue(rJobSetup.GetPaperBin());
504
505
                // may fail due to constraints;
506
                // real paper bin is copied back to jobsetup in that case
507
0
                aData.m_aContext.setValue( pKey, pValue );
508
0
            }
509
            // if printer has no InputSlot key simply ignore this setting
510
            // (e.g. SGENPRT has no InputSlot)
511
0
        }
512
513
        // merge duplex if necessary
514
0
        if( nSetDataFlags & JobSetFlags::DUPLEXMODE )
515
0
        {
516
0
            pKey = aData.m_pParser->getKey( u"Duplex"_ustr );
517
0
            if( pKey )
518
0
            {
519
0
                pValue = nullptr;
520
0
                switch (rJobSetup.GetDuplexMode())
521
0
                {
522
0
                case DuplexMode::Off:
523
0
                    pValue = pKey->getValue( u"None"_ustr );
524
0
                    if( pValue == nullptr )
525
0
                        pValue = pKey->getValue( u"SimplexNoTumble"_ustr );
526
0
                    break;
527
0
                case DuplexMode::ShortEdge:
528
0
                    pValue = pKey->getValue( u"DuplexTumble"_ustr );
529
0
                    break;
530
0
                case DuplexMode::LongEdge:
531
0
                    pValue = pKey->getValue( u"DuplexNoTumble"_ustr );
532
0
                    break;
533
0
                case DuplexMode::Unknown:
534
0
                default:
535
0
                    pValue = nullptr;
536
0
                    break;
537
0
                }
538
0
                if( ! pValue )
539
0
                    pValue = pKey->getDefaultValue();
540
0
                aData.m_aContext.setValue( pKey, pValue );
541
0
            }
542
0
        }
543
0
        aData.m_bPapersizeFromSetup = rJobSetup.GetPapersizeFromSetup();
544
545
0
        m_aJobData = aData;
546
0
        copyJobDataToJobSetup(&rJobSetup, aData);
547
0
        return true;
548
0
    }
549
550
0
    return false;
551
0
}
552
553
void PspSalInfoPrinter::GetPageInfo(
554
    const ImplJobSetup* pJobSetup,
555
    tools::Long& rOutWidth, tools::Long& rOutHeight,
556
    Point& rPageOffset,
557
    Size& rPaperSize )
558
0
{
559
0
    if( ! pJobSetup )
560
0
        return;
561
562
0
    JobData aData;
563
0
    JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
564
565
    // get the selected page size
566
0
    if( !aData.m_pParser )
567
0
        return;
568
569
570
0
    OUString aPaper;
571
0
    int width, height;
572
0
    int left = 0, top = 0, right = 0, bottom = 0;
573
0
    int nDPI = aData.m_aContext.getRenderResolution();
574
575
0
    if( aData.m_eOrientation == psp::orientation::Portrait )
576
0
    {
577
0
        aData.m_aContext.getPageSize( aPaper, width, height );
578
0
        aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
579
0
    }
580
0
    else
581
0
    {
582
0
        aData.m_aContext.getPageSize( aPaper, height, width );
583
0
        aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
584
0
    }
585
586
0
    rPaperSize.setWidth( width * nDPI / 72 );
587
0
    rPaperSize.setHeight( height * nDPI / 72 );
588
0
    rPageOffset.setX( left * nDPI / 72 );
589
0
    rPageOffset.setY( top * nDPI / 72 );
590
0
    rOutWidth   = ( width  - left - right ) * nDPI / 72;
591
0
    rOutHeight  = ( height - top  - bottom ) * nDPI / 72;
592
593
0
}
594
595
sal_uInt16 PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
596
0
{
597
0
    if( ! pJobSetup )
598
0
        return 0;
599
600
0
    JobData aData;
601
0
    JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
602
603
0
    const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( u"InputSlot"_ustr ): nullptr;
604
0
    return pKey ? pKey->countValues() : 0;
605
0
}
606
607
OUString PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uInt16 nPaperBin )
608
0
{
609
0
    JobData aData;
610
0
    JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
611
612
0
    if( aData.m_pParser )
613
0
    {
614
0
        const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( u"InputSlot"_ustr ): nullptr;
615
0
        if( ! pKey || nPaperBin >= o3tl::make_unsigned(pKey->countValues()) )
616
0
            return aData.m_pParser->getDefaultInputSlot();
617
0
        const PPDValue* pValue = pKey->getValue( nPaperBin );
618
0
        if( pValue )
619
0
            return aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
620
0
    }
621
622
0
    return OUString();
623
0
}
624
625
sal_uInt16 PspSalInfoPrinter::GetPaperBinBySourceIndex( const ImplJobSetup*, sal_uInt16 )
626
0
{
627
0
    return 0xffff;
628
0
}
629
630
sal_uInt16  PspSalInfoPrinter::GetSourceIndexByPaperBin(const ImplJobSetup*, sal_uInt16)
631
0
{
632
0
    return 0;
633
0
}
634
635
sal_uInt32 PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, PrinterCapType nType )
636
0
{
637
0
    switch( nType )
638
0
    {
639
0
        case PrinterCapType::SupportDialog:
640
0
            return 1;
641
0
        case PrinterCapType::Copies:
642
0
            return 0xffff;
643
0
        case PrinterCapType::CollateCopies:
644
0
        {
645
            // PPDs don't mention the number of possible collated copies.
646
            // so let's guess as many as we want ?
647
0
            return 0xffff;
648
0
        }
649
0
        case PrinterCapType::SetOrientation:
650
0
            return 1;
651
0
        case PrinterCapType::SetPaperSize:
652
0
            return 1;
653
0
        case PrinterCapType::SetPaper:
654
0
            return 0;
655
0
        case PrinterCapType::Fax:
656
0
            {
657
                // see if the PPD contains the fax4CUPS "Dial" option and that it's not set
658
                // to "manually"
659
0
                JobData aData = PrinterInfoManager::get().getPrinterInfo(pJobSetup->GetPrinterName());
660
0
                if( pJobSetup->GetDriverData() )
661
0
                    JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
662
0
                const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey(u"Dial"_ustr) : nullptr;
663
0
                const PPDValue* pValue = pKey ? aData.m_aContext.getValue(pKey) : nullptr;
664
0
                if (pValue && !pValue->m_aOption.equalsIgnoreAsciiCase("Manually"))
665
0
                    return 1;
666
0
                return 0;
667
0
            }
668
669
0
        case PrinterCapType::PDF:
670
0
            return 1;
671
0
        case PrinterCapType::ExternalDialog:
672
0
            return PrinterInfoManager::get().checkFeatureToken( pJobSetup->GetPrinterName(), "external_dialog" ) ? 1 : 0;
673
0
        case PrinterCapType::UsePullModel:
674
0
            return 1;
675
0
        default: break;
676
0
    }
677
0
    return 0;
678
0
}
679
680
/*
681
 *  SalPrinter
682
 */
683
PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
684
0
    : m_pInfoPrinter( pInfoPrinter )
685
0
{
686
0
}
687
688
PspSalPrinter::~PspSalPrinter()
689
0
{
690
0
}
691
692
bool PspSalPrinter::StartJob(
693
    const OUString* /*pFileName*/,
694
    const OUString& /*rJobName*/,
695
    const OUString& /*rAppName*/,
696
    sal_uInt32 /*nCopies*/,
697
    bool /*bCollate*/,
698
    bool /*bDirect*/,
699
    ImplJobSetup* /*pJobSetup*/ )
700
0
{
701
0
    OSL_FAIL( "should never be called" );
702
0
    return false;
703
0
}
704
705
bool PspSalPrinter::EndJob()
706
0
{
707
0
    return true;
708
0
}
709
710
SalGraphics* PspSalPrinter::StartPage( ImplJobSetup*, bool )
711
0
{
712
0
    OSL_FAIL( "should never be called" );
713
0
    return nullptr;
714
0
}
715
716
void PspSalPrinter::EndPage()
717
0
{
718
0
    OSL_FAIL( "should never be called" );
719
0
}
720
721
namespace {
722
723
struct PDFNewJobParameters
724
{
725
    Size        maPageSize;
726
    sal_uInt16      mnPaperBin;
727
728
    PDFNewJobParameters( const Size& i_rSize = Size(),
729
                         sal_uInt16 i_nPaperBin = 0xffff )
730
0
    : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
731
732
    bool operator==(const PDFNewJobParameters& rComp ) const
733
0
    {
734
0
        const tools::Long nRotatedWidth = rComp.maPageSize.Height();
735
0
        const tools::Long nRotatedHeight = rComp.maPageSize.Width();
736
0
        Size aCompLSSize(nRotatedWidth, nRotatedHeight);
737
0
        return
738
0
            (maPageSize == rComp.maPageSize || maPageSize == aCompLSSize)
739
0
        &&  mnPaperBin == rComp.mnPaperBin
740
0
        ;
741
0
    }
742
743
    bool operator!=(const PDFNewJobParameters& rComp) const
744
0
    {
745
0
        return ! operator==(rComp);
746
0
    }
747
};
748
749
struct PDFPrintFile
750
{
751
    OUString       maTmpURL;
752
    PDFNewJobParameters maParameters;
753
754
    PDFPrintFile( OUString i_URL, const PDFNewJobParameters& i_rNewParameters )
755
0
    : maTmpURL(std::move( i_URL ))
756
0
    , maParameters( i_rNewParameters ) {}
757
};
758
759
}
760
761
bool PspSalPrinter::StartJob( const OUString* i_pFileName, const OUString& i_rJobName, const OUString& i_rAppName,
762
                              ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
763
0
{
764
0
    SAL_INFO( "vcl.unx.print", "StartJob with controller: pFilename = " << (i_pFileName ? *i_pFileName : u"<nil>"_ustr) );
765
    // reset IsLastPage
766
0
    i_rController.setLastPage( false );
767
    // is this a fax device
768
0
    bool bFax = m_pInfoPrinter->GetCapabilities(i_pSetupData, PrinterCapType::Fax) == 1;
769
770
    // update job data
771
0
    if( i_pSetupData )
772
0
        JobData::constructFromStreamBuffer( i_pSetupData->GetDriverData(), i_pSetupData->GetDriverDataLen(), m_aJobData );
773
774
    // possibly create one job for collated output
775
0
    int nCopies = i_rController.getPrinter()->GetCopyCount();
776
0
    bool bCollate = i_rController.getPrinter()->IsCollateCopy();
777
0
    bool bSinglePrintJobs = i_rController.getPrinter()->IsSinglePrintJobs();
778
779
    // notify start of real print job
780
0
    i_rController.jobStarted();
781
782
    // setup PDFWriter context
783
0
    vcl::pdf::PDFWriter::PDFWriterContext aContext;
784
0
    aContext.Version            = vcl::pdf::PDFWriter::PDFVersion::PDF_1_4;
785
0
    aContext.Tagged             = false;
786
0
    aContext.DocumentLocale     = Application::GetSettings().GetLanguageTag().getLocale();
787
0
    aContext.ColorMode          = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
788
0
    ? vcl::pdf::PDFWriter::DrawGreyscale : vcl::pdf::PDFWriter::DrawColor;
789
790
    // prepare doc info
791
0
    aContext.DocumentInfo.Title              = i_rJobName;
792
0
    aContext.DocumentInfo.Creator            = i_rAppName;
793
0
    aContext.DocumentInfo.Producer           = i_rAppName;
794
795
    // define how we handle metafiles in PDFWriter
796
0
    vcl::pdf::PDFWriter::PlayMetafileContext aMtfContext;
797
0
    aMtfContext.m_bOnlyLosslessCompression = true;
798
799
0
    std::shared_ptr<vcl::pdf::PDFWriter> xWriter;
800
0
    std::vector< PDFPrintFile > aPDFFiles;
801
0
    VclPtr<Printer> xPrinter( i_rController.getPrinter() );
802
0
    int nAllPages = i_rController.getFilteredPageCount();
803
0
    i_rController.createProgressDialog();
804
0
    bool bAborted = false;
805
0
    PDFNewJobParameters aLastParm;
806
807
0
    aContext.DPIx = xPrinter->GetDPIX();
808
0
    aContext.DPIy = xPrinter->GetDPIY();
809
0
    for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ )
810
0
    {
811
0
        if( nPage == nAllPages-1 )
812
0
            i_rController.setLastPage( true );
813
814
        // get the page's metafile
815
0
        GDIMetaFile aPageFile;
816
0
        vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
817
0
        if( i_rController.isProgressCanceled() )
818
0
        {
819
0
            bAborted = true;
820
0
            if( nPage != nAllPages-1 )
821
0
            {
822
0
                i_rController.createProgressDialog();
823
0
                i_rController.setLastPage( true );
824
0
                i_rController.getFilteredPageFile( nPage, aPageFile );
825
0
            }
826
0
        }
827
0
        else
828
0
        {
829
0
            xPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
830
0
            xPrinter->SetPaperSizeUser( aPageSize.aSize );
831
0
            PDFNewJobParameters aNewParm(xPrinter->GetPaperSize(), xPrinter->GetPaperBin());
832
833
            // create PDF writer on demand
834
            // either on first page
835
            // or on paper format change - cups does not support multiple paper formats per job (yet?)
836
            // so we need to start a new job to get a new paper format from the printer
837
            // orientation switches (that is switch of height and width) is handled transparently by CUPS
838
0
            if( ! xWriter ||
839
0
                (aNewParm != aLastParm && ! i_pFileName ) )
840
0
            {
841
0
                if( xWriter )
842
0
                {
843
0
                    xWriter->Emit();
844
0
                }
845
                // produce PDF file
846
0
                OUString aPDFUrl;
847
0
                if( i_pFileName )
848
0
                    aPDFUrl = *i_pFileName;
849
0
                else
850
0
                    osl_createTempFile( nullptr, nullptr, &aPDFUrl.pData );
851
                // normalize to file URL
852
0
                if( !comphelper::isFileUrl(aPDFUrl) )
853
0
                {
854
                    // this is not a file URL, but it should
855
                    // form it into an osl friendly file URL
856
0
                    OUString aTmp;
857
0
                    osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
858
0
                    aPDFUrl = aTmp;
859
0
                }
860
                // save current file and paper format
861
0
                aLastParm = aNewParm;
862
0
                aPDFFiles.emplace_back( aPDFUrl, aNewParm );
863
                // update context
864
0
                aContext.URL = aPDFUrl;
865
866
                // create and initialize PDFWriter
867
0
                xWriter = std::make_shared<vcl::pdf::PDFWriter>( aContext, uno::Reference< beans::XMaterialHolder >() );
868
0
            }
869
870
0
            xWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
871
0
                              TenMuToPt( aNewParm.maPageSize.Height() ),
872
0
                              vcl::pdf::PDFWriter::Orientation::Portrait );
873
874
0
            xWriter->PlayMetafile( aPageFile, aMtfContext );
875
0
        }
876
0
    }
877
878
    // emit the last file
879
0
    if( xWriter )
880
0
        xWriter->Emit();
881
882
    // handle collate, copy count and multiple jobs correctly
883
0
    int nOuterJobs = 1;
884
0
    if( bSinglePrintJobs )
885
0
    {
886
0
        nOuterJobs = nCopies;
887
0
        m_aJobData.m_nCopies = 1;
888
0
    }
889
0
    else
890
0
    {
891
0
        if( bCollate )
892
0
        {
893
0
            if (aPDFFiles.size() == 1 && xPrinter->HasSupport(PrinterSupport::CollateCopy))
894
0
            {
895
0
                m_aJobData.setCollate( true );
896
0
                m_aJobData.m_nCopies = nCopies;
897
0
            }
898
0
            else
899
0
            {
900
0
                nOuterJobs = nCopies;
901
0
                m_aJobData.m_nCopies = 1;
902
0
            }
903
0
        }
904
0
        else
905
0
        {
906
0
            m_aJobData.setCollate( false );
907
0
            m_aJobData.m_nCopies = nCopies;
908
0
        }
909
0
    }
910
911
0
    std::vector<OUString> aFaxNumbers;
912
913
    // check for fax numbers
914
0
    if (!bAborted && bFax)
915
0
    {
916
0
        aFaxNumbers = getFaxNumbers();
917
0
        bAborted = aFaxNumbers.empty();
918
0
    }
919
920
0
    bool bSuccess(true);
921
    // spool files
922
0
    if( ! i_pFileName && ! bAborted )
923
0
    {
924
0
        do
925
0
        {
926
0
            OUString sFaxNumber;
927
0
            if (!aFaxNumbers.empty())
928
0
            {
929
0
                sFaxNumber = aFaxNumbers.back();
930
0
                aFaxNumbers.pop_back();
931
0
            }
932
933
0
            bool bFirstJob = true;
934
0
            for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ )
935
0
            {
936
0
                for( size_t i = 0; i < aPDFFiles.size(); i++ )
937
0
                {
938
0
                    oslFileHandle pFile = nullptr;
939
0
                    osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
940
0
                    if (pFile && (osl_setFilePos(pFile, osl_Pos_Absolut, 0) == osl_File_E_None))
941
0
                    {
942
0
                        std::vector< char > buffer( 0x10000, 0 );
943
                        // update job data with current page size
944
0
                        Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
945
0
                        m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
946
                        // update job data with current paperbin
947
0
                        m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
948
949
                        // spool current file
950
0
                        FILE* fp = PrinterInfoManager::get().startSpool(xPrinter->GetName(), i_rController.isDirectPrint());
951
0
                        if( fp )
952
0
                        {
953
0
                            sal_uInt64 nBytesRead = 0;
954
0
                            do
955
0
                            {
956
0
                                osl_readFile( pFile, buffer.data(), buffer.size(), &nBytesRead );
957
0
                                if( nBytesRead > 0 )
958
0
                                {
959
0
                                    size_t nBytesWritten = fwrite(buffer.data(), 1, nBytesRead, fp);
960
0
                                    OSL_ENSURE(nBytesRead == nBytesWritten, "short write");
961
0
                                    if (nBytesRead != nBytesWritten)
962
0
                                        break;
963
0
                                }
964
0
                            } while( nBytesRead == buffer.size() );
965
0
                            OUStringBuffer aBuf( i_rJobName.getLength() + 8 );
966
0
                            aBuf.append( i_rJobName );
967
0
                            if( i > 0 || nCurJob > 0 )
968
0
                            {
969
0
                                aBuf.append( ' ' );
970
0
                                aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
971
0
                            }
972
0
                            bSuccess &=
973
0
                            PrinterInfoManager::get().endSpool(xPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob, sFaxNumber);
974
0
                            bFirstJob = false;
975
0
                        }
976
0
                    }
977
0
                    osl_closeFile( pFile );
978
0
                }
979
0
            }
980
0
        }
981
0
        while (!aFaxNumbers.empty());
982
0
    }
983
984
    // job has been spooled
985
0
    i_rController.setJobState( bAborted
986
0
            ? view::PrintableState_JOB_ABORTED
987
0
            : (bSuccess ? view::PrintableState_JOB_SPOOLED
988
0
                          : view::PrintableState_JOB_SPOOLING_FAILED));
989
990
    // clean up the temporary PDF files
991
0
    if( ! i_pFileName || bAborted )
992
0
    {
993
0
        for(PDFPrintFile & rPDFFile : aPDFFiles)
994
0
        {
995
0
            osl_removeFile( rPDFFile.maTmpURL.pData );
996
0
            SAL_INFO( "vcl.unx.print", "removed print PDF file " << rPDFFile.maTmpURL );
997
0
        }
998
0
    }
999
1000
0
    return true;
1001
0
}
1002
1003
void SalGenericInstance::updatePrinterUpdate()
1004
0
{
1005
0
    if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1006
0
        return;
1007
1008
0
    if (!isPrinterInit())
1009
0
    {
1010
        // #i45389# start background printer detection
1011
0
        psp::PrinterInfoManager::get();
1012
0
        return;
1013
0
    }
1014
1015
0
    ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
1016
0
    if (rManager.checkPrintersChanged(false))
1017
0
        PostPrintersChanged();
1018
0
}
1019
1020
std::unique_ptr<GenPspGraphics> SalGenericInstance::CreatePrintGraphics()
1021
0
{
1022
0
    return std::make_unique<GenPspGraphics>();
1023
0
}
1024
1025
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */