1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 this file implements the sal printer interface (SalPrinter, SalInfoPrinter
22 and some printer relevant methods of SalInstance and SalGraphicsData)
24 as underlying library the printer features of psprint are used.
26 The query methods of a SalInfoPrinter are implemented by querying psprint
28 The job methods of a SalPrinter are implemented by calling psprint
29 printer job functions.
32 // For spawning PDF and FAX generation
37 #include <comphelper/fileurl.hxx>
38 #include <rtl/ustring.hxx>
39 #include <sal/log.hxx>
41 #include <vcl/gdimtf.hxx>
42 #include <vcl/idle.hxx>
43 #include <vcl/print.hxx>
44 #include <vcl/pdfwriter.hxx>
45 #include <printerinfomanager.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/settings.hxx>
48 #include <vcl/weld.hxx>
49 #include <strings.hrc>
50 #include <saldatabasic.hxx>
51 #include <unx/genprn.h>
52 #include <unx/geninst.h>
53 #include <unx/genpspgraphics.h>
57 #include "prtsetup.hxx"
58 #include <salptype.hxx>
60 #include <com/sun/star/beans/PropertyValue.hpp>
63 using namespace com::sun::star
;
65 static bool getPdfDir( const PrinterInfo
& rInfo
, OUString
&rDir
)
70 OUString
aToken( rInfo
.m_aFeatures
.getToken( 0, ',', nIndex
) );
71 if( aToken
.startsWith( "pdf=" ) )
74 rDir
= aToken
.getToken( 1, '=', nPos
);
75 if( rDir
.isEmpty() && getenv( "HOME" ) )
76 rDir
= OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
85 class QueryString
: public weld::GenericDialogController
88 OUString
& m_rReturnValue
;
90 std::unique_ptr
<weld::Button
> m_xOKButton
;
91 std::unique_ptr
<weld::Label
> m_xFixedText
;
92 std::unique_ptr
<weld::Entry
> m_xEdit
;
94 DECL_LINK( ClickBtnHdl
, weld::Button
&, void );
97 // parent window, Query text, initial value
98 QueryString(weld::Window
*, OUString
const &, OUString
&);
104 QueryString::QueryString(weld::Window
* pParent
, OUString
const & rQuery
, OUString
& rRet
)
105 : GenericDialogController(pParent
, "vcl/ui/querydialog.ui", "QueryDialog")
106 , m_rReturnValue( rRet
)
107 , m_xOKButton(m_xBuilder
->weld_button("ok"))
108 , m_xFixedText(m_xBuilder
->weld_label("label"))
109 , m_xEdit(m_xBuilder
->weld_entry("entry"))
111 m_xOKButton
->connect_clicked(LINK(this, QueryString
, ClickBtnHdl
));
112 m_xFixedText
->set_label(rQuery
);
113 m_xEdit
->set_text(m_rReturnValue
);
114 m_xDialog
->set_title(rQuery
);
117 IMPL_LINK(QueryString
, ClickBtnHdl
, weld::Button
&, rButton
, void)
119 if (&rButton
== m_xOKButton
.get())
121 m_rReturnValue
= m_xEdit
->get_text();
122 m_xDialog
->response(RET_OK
);
125 m_xDialog
->response(RET_CANCEL
);
128 int QueryFaxNumber(OUString
& rNumber
)
130 vcl::Window
* pWin
= Application::GetDefDialogParent();
131 QueryString
aQuery(pWin
? pWin
->GetFrameWeld() : nullptr, VclResId(SV_PRINT_QUERYFAXNUMBER_TXT
), rNumber
);
136 static int PtTo10Mu( int nPoints
) { return static_cast<int>((static_cast<double>(nPoints
)*35.27777778)+0.5); }
138 static int TenMuToPt( int nUnits
) { return static_cast<int>((static_cast<double>(nUnits
)/35.27777778)+0.5); }
140 static void copyJobDataToJobSetup( ImplJobSetup
* pJobSetup
, JobData
& rData
)
142 pJobSetup
->SetOrientation( rData
.m_eOrientation
== orientation::Landscape
?
143 Orientation::Landscape
: Orientation::Portrait
);
149 rData
.m_aContext
.getPageSize( aPaper
, width
, height
);
150 pJobSetup
->SetPaperFormat( PaperInfo::fromPSName(
151 OUStringToOString( aPaper
, RTL_TEXTENCODING_ISO_8859_1
)));
153 pJobSetup
->SetPaperWidth( 0 );
154 pJobSetup
->SetPaperHeight( 0 );
155 if( pJobSetup
->GetPaperFormat() == PAPER_USER
)
157 // transform to 100dth mm
158 width
= PtTo10Mu( width
);
159 height
= PtTo10Mu( height
);
161 if( rData
.m_eOrientation
== psp::orientation::Portrait
)
163 pJobSetup
->SetPaperWidth( width
);
164 pJobSetup
->SetPaperHeight( height
);
168 pJobSetup
->SetPaperWidth( height
);
169 pJobSetup
->SetPaperHeight( width
);
174 const PPDKey
* pKey
= nullptr;
175 const PPDValue
* pValue
= nullptr;
177 pJobSetup
->SetPaperBin( 0 );
178 if( rData
.m_pParser
)
179 pKey
= rData
.m_pParser
->getKey( "InputSlot" );
181 pValue
= rData
.m_aContext
.getValue( pKey
);
186 pValue
!= pKey
->getValue( nPaperBin
) &&
187 nPaperBin
< pKey
->countValues();
189 pJobSetup
->SetPaperBin(
190 nPaperBin
== pKey
->countValues() ? 0 : nPaperBin
);
197 pJobSetup
->SetDuplexMode( DuplexMode::Unknown
);
198 if( rData
.m_pParser
)
199 pKey
= rData
.m_pParser
->getKey( "Duplex" );
201 pValue
= rData
.m_aContext
.getValue( pKey
);
204 if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "None" ) ||
205 pValue
->m_aOption
.startsWithIgnoreAsciiCase( "Simplex" )
208 pJobSetup
->SetDuplexMode( DuplexMode::Off
);
210 else if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
212 pJobSetup
->SetDuplexMode( DuplexMode::LongEdge
);
214 else if( pValue
->m_aOption
.equalsIgnoreAsciiCase( "DuplexTumble" ) )
216 pJobSetup
->SetDuplexMode( DuplexMode::ShortEdge
);
220 // copy the whole context
221 if( pJobSetup
->GetDriverData() )
222 std::free( const_cast<sal_uInt8
*>(pJobSetup
->GetDriverData()) );
225 void* pBuffer
= nullptr;
226 if( rData
.getStreamBuffer( pBuffer
, nBytes
) )
228 pJobSetup
->SetDriverDataLen( nBytes
);
229 pJobSetup
->SetDriverData( static_cast<sal_uInt8
*>(pBuffer
) );
233 pJobSetup
->SetDriverDataLen( 0 );
234 pJobSetup
->SetDriverData( nullptr );
236 pJobSetup
->SetPapersizeFromSetup( rData
.m_bPapersizeFromSetup
);
239 // Needs a cleaner abstraction ...
240 static bool passFileToCommandLine( const OUString
& rFilename
, const OUString
& rCommandLine
)
242 bool bSuccess
= false;
244 rtl_TextEncoding aEncoding
= osl_getThreadTextEncoding();
245 OString
aCmdLine(OUStringToOString(rCommandLine
, aEncoding
));
246 OString
aFilename(OUStringToOString(rFilename
, aEncoding
));
248 bool bPipe
= aCmdLine
.indexOf( "(TMP)" ) == -1;
250 // setup command line for exec
252 aCmdLine
= aCmdLine
.replaceAll("(TMP)", aFilename
);
254 #if OSL_DEBUG_LEVEL > 1
255 fprintf( stderr
, "%s commandline: \"%s\"\n",
256 bPipe
? "piping to" : "executing",
259 if( stat( aFilename
.getStr(), &aStat
) )
260 fprintf( stderr
, "stat( %s ) failed\n", aFilename
.getStr() );
261 fprintf( stderr
, "Tmp file %s has modes: 0%03lo\n", aFilename
.getStr(), (long)aStat
.st_mode
);
264 if( ! ( argv
[ 0 ] = getenv( "SHELL" ) ) )
265 argv
[ 0 ] = "/bin/sh";
267 argv
[ 2 ] = aCmdLine
.getStr();
270 bool bHavePipes
= false;
274 bHavePipes
= pipe( fd
) == 0;
275 if( ( pid
= fork() ) > 0 )
277 if( bPipe
&& bHavePipes
)
280 char aBuffer
[ 2048 ];
281 FILE* fp
= fopen( aFilename
.getStr(), "r" );
282 while (fp
&& !feof(fp
))
284 size_t nBytesRead
= fread(aBuffer
, 1, sizeof( aBuffer
), fp
);
287 size_t nBytesWritten
= write(fd
[1], aBuffer
, nBytesRead
);
288 OSL_ENSURE(nBytesWritten
== nBytesRead
, "short write");
289 if (nBytesWritten
!= nBytesRead
)
297 if(waitpid( pid
, &status
, 0 ) != -1)
305 if( bPipe
&& bHavePipes
)
308 if( fd
[0] != STDIN_FILENO
) // not probable, but who knows :)
309 dup2( fd
[0], STDIN_FILENO
);
311 execv( argv
[0], const_cast<char**>(argv
) );
312 fprintf( stderr
, "failed to execute \"%s\"\n", aCmdLine
.getStr() );
316 fprintf( stderr
, "failed to fork\n" );
319 unlink( aFilename
.getStr() );
324 static std::vector
<OUString
> getFaxNumbers()
326 std::vector
<OUString
> aFaxNumbers
;
329 if (QueryFaxNumber(aNewNr
))
331 for (sal_Int32 nIndex
{0}; nIndex
>= 0; )
332 aFaxNumbers
.push_back(aNewNr
.getToken( 0, ';', nIndex
));
338 static bool createPdf( const OUString
& rToFile
, const OUString
& rFromFile
, const OUString
& rCommandLine
)
340 return passFileToCommandLine( rFromFile
, rCommandLine
.replaceAll("(OUTFILE)", rToFile
) );
347 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter
*pPrinter
,
348 SalPrinterQueueInfo
const * pQueueInfo
, ImplJobSetup
* pJobSetup
)
352 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
353 PrinterInfo
aInfo( rManager
.getPrinterInfo( pQueueInfo
->maPrinterName
) );
354 pPrinter
->m_aJobData
= aInfo
;
355 pPrinter
->m_aPrinterGfx
.Init( pPrinter
->m_aJobData
);
357 if( pJobSetup
->GetDriverData() )
358 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(),
359 pJobSetup
->GetDriverDataLen(), aInfo
);
361 pJobSetup
->SetSystem( JOBSETUP_SYSTEM_UNIX
);
362 pJobSetup
->SetPrinterName( pQueueInfo
->maPrinterName
);
363 pJobSetup
->SetDriver( aInfo
.m_aDriverName
);
364 copyJobDataToJobSetup( pJobSetup
, aInfo
);
368 SalInfoPrinter
* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
369 ImplJobSetup
* pJobSetup
)
371 mbPrinterInit
= true;
372 // create and initialize SalInfoPrinter
373 PspSalInfoPrinter
* pPrinter
= new PspSalInfoPrinter();
374 configurePspInfoPrinter(pPrinter
, pQueueInfo
, pJobSetup
);
378 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
383 std::unique_ptr
<SalPrinter
> SalGenericInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
385 mbPrinterInit
= true;
386 // create and initialize SalPrinter
387 PspSalPrinter
* pPrinter
= new PspSalPrinter( pInfoPrinter
);
388 pPrinter
->m_aJobData
= static_cast<PspSalInfoPrinter
*>(pInfoPrinter
)->m_aJobData
;
390 return std::unique_ptr
<SalPrinter
>(pPrinter
);
393 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
395 mbPrinterInit
= true;
396 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
397 static const char* pNoSyncDetection
= getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
398 if( ! pNoSyncDetection
|| ! *pNoSyncDetection
)
400 // #i62663# synchronize possible asynchronouse printer detection now
401 rManager
.checkPrintersChanged( true );
403 ::std::vector
< OUString
> aPrinters
;
404 rManager
.listPrinters( aPrinters
);
406 for (auto const& printer
: aPrinters
)
408 const PrinterInfo
& rInfo( rManager
.getPrinterInfo(printer
) );
410 std::unique_ptr
<SalPrinterQueueInfo
> pInfo(new SalPrinterQueueInfo
);
411 pInfo
->maPrinterName
= printer
;
412 pInfo
->maDriver
= rInfo
.m_aDriverName
;
413 pInfo
->maLocation
= rInfo
.m_aLocation
;
414 pInfo
->maComment
= rInfo
.m_aComment
;
417 if (getPdfDir(rInfo
, sPdfDir
))
418 pInfo
->maLocation
= sPdfDir
;
420 pList
->Add( std::move(pInfo
) );
424 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
426 mbPrinterInit
= true;
429 OUString
SalGenericInstance::GetDefaultPrinter()
431 mbPrinterInit
= true;
432 PrinterInfoManager
& rManager( PrinterInfoManager::get() );
433 return rManager
.getDefaultPrinter();
436 PspSalInfoPrinter::PspSalInfoPrinter()
440 PspSalInfoPrinter::~PspSalInfoPrinter()
444 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup
* )
446 m_aPaperFormats
.clear();
447 m_bPapersInit
= true;
449 if( m_aJobData
.m_pParser
)
451 const PPDKey
* pKey
= m_aJobData
.m_pParser
->getKey( "PageSize" );
454 int nValues
= pKey
->countValues();
455 for( int i
= 0; i
< nValues
; i
++ )
457 const PPDValue
* pValue
= pKey
->getValue( i
);
458 int nWidth
= 0, nHeight
= 0;
459 m_aJobData
.m_pParser
->getPaperDimension( pValue
->m_aOption
, nWidth
, nHeight
);
460 PaperInfo
aInfo(PtTo10Mu( nWidth
), PtTo10Mu( nHeight
));
461 m_aPaperFormats
.push_back( aInfo
);
467 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup
* )
472 SalGraphics
* PspSalInfoPrinter::AcquireGraphics()
474 // return a valid pointer only once
475 // the reasoning behind this is that we could have different
476 // SalGraphics that can run in multiple threads
478 SalGraphics
* pRet
= nullptr;
481 m_pGraphics
= GetGenericInstance()->CreatePrintGraphics();
482 m_pGraphics
->Init(&m_aJobData
, &m_aPrinterGfx
);
483 pRet
= m_pGraphics
.get();
488 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics
* pGraphics
)
490 if( m_pGraphics
.get() == pGraphics
)
496 bool PspSalInfoPrinter::Setup( weld::Window
* pFrame
, ImplJobSetup
* pJobSetup
)
498 if( ! pFrame
|| ! pJobSetup
)
501 PrinterInfoManager
& rManager
= PrinterInfoManager::get();
503 PrinterInfo
aInfo( rManager
.getPrinterInfo( pJobSetup
->GetPrinterName() ) );
504 if ( pJobSetup
->GetDriverData() )
506 SetData( JobSetFlags::ALL
, pJobSetup
);
507 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aInfo
);
509 aInfo
.m_bPapersizeFromSetup
= pJobSetup
->GetPapersizeFromSetup();
510 aInfo
.meSetupMode
= pJobSetup
->GetPrinterSetupMode();
512 if (SetupPrinterDriver(pFrame
, aInfo
))
514 aInfo
.resolveDefaultBackend();
515 std::free( const_cast<sal_uInt8
*>(pJobSetup
->GetDriverData()) );
516 pJobSetup
->SetDriverData( nullptr );
519 void* pBuffer
= nullptr;
520 aInfo
.getStreamBuffer( pBuffer
, nBytes
);
521 pJobSetup
->SetDriverDataLen( nBytes
);
522 pJobSetup
->SetDriverData( static_cast<sal_uInt8
*>(pBuffer
) );
524 // copy everything to job setup
525 copyJobDataToJobSetup( pJobSetup
, aInfo
);
526 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), m_aJobData
);
532 // This function gets the driver data and puts it into pJobSetup
533 // If pJobSetup->GetDriverData() is NOT NULL, then the independent
534 // data should be merged into the driver data
535 // If pJobSetup->GetDriverData() IS NULL, then the driver defaults
536 // should be merged into the independent data
537 bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup
* pJobSetup
)
539 if( pJobSetup
->GetDriverData() )
540 return SetData( JobSetFlags::ALL
, pJobSetup
);
542 copyJobDataToJobSetup( pJobSetup
, m_aJobData
);
547 // This function merges the independent driver data
548 // and sets the new independent data in pJobSetup
549 // Only the data must be changed, where the bit
550 // in nGetDataFlags is set
551 bool PspSalInfoPrinter::SetData(
552 JobSetFlags nSetDataFlags
,
553 ImplJobSetup
* pJobSetup
)
556 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
558 if( aData
.m_pParser
)
561 const PPDValue
* pValue
;
563 // merge papersize if necessary
564 if( nSetDataFlags
& JobSetFlags::PAPERSIZE
)
568 if( pJobSetup
->GetPaperFormat() == PAPER_USER
)
569 aPaper
= aData
.m_pParser
->matchPaper(
570 TenMuToPt( pJobSetup
->GetPaperWidth() ),
571 TenMuToPt( pJobSetup
->GetPaperHeight() ) );
573 aPaper
= OStringToOUString(PaperInfo::toPSName(pJobSetup
->GetPaperFormat()), RTL_TEXTENCODING_ISO_8859_1
);
575 pKey
= aData
.m_pParser
->getKey( "PageSize" );
576 pValue
= pKey
? pKey
->getValueCaseInsensitive( aPaper
) : nullptr;
578 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
579 // try to find the correct paper anyway using the size
580 if( pKey
&& ! pValue
&& pJobSetup
->GetPaperFormat() != PAPER_USER
)
582 PaperInfo
aInfo( pJobSetup
->GetPaperFormat() );
583 aPaper
= aData
.m_pParser
->matchPaper(
584 TenMuToPt( aInfo
.getWidth() ),
585 TenMuToPt( aInfo
.getHeight() ) );
586 pValue
= pKey
->getValueCaseInsensitive( aPaper
);
589 if( ! ( pKey
&& pValue
&& aData
.m_aContext
.setValue( pKey
, pValue
) == pValue
) )
593 // merge paperbin if necessary
594 if( nSetDataFlags
& JobSetFlags::PAPERBIN
)
596 pKey
= aData
.m_pParser
->getKey( "InputSlot" );
599 int nPaperBin
= pJobSetup
->GetPaperBin();
600 if( nPaperBin
>= pKey
->countValues() )
601 pValue
= pKey
->getDefaultValue();
603 pValue
= pKey
->getValue( pJobSetup
->GetPaperBin() );
605 // may fail due to constraints;
606 // real paper bin is copied back to jobsetup in that case
607 aData
.m_aContext
.setValue( pKey
, pValue
);
609 // if printer has no InputSlot key simply ignore this setting
610 // (e.g. SGENPRT has no InputSlot)
613 // merge orientation if necessary
614 if( nSetDataFlags
& JobSetFlags::ORIENTATION
)
615 aData
.m_eOrientation
= pJobSetup
->GetOrientation() == Orientation::Landscape
? orientation::Landscape
: orientation::Portrait
;
617 // merge duplex if necessary
618 if( nSetDataFlags
& JobSetFlags::DUPLEXMODE
)
620 pKey
= aData
.m_pParser
->getKey( "Duplex" );
624 switch( pJobSetup
->GetDuplexMode() )
626 case DuplexMode::Off
:
627 pValue
= pKey
->getValue( "None" );
628 if( pValue
== nullptr )
629 pValue
= pKey
->getValue( "SimplexNoTumble" );
631 case DuplexMode::ShortEdge
:
632 pValue
= pKey
->getValue( "DuplexTumble" );
634 case DuplexMode::LongEdge
:
635 pValue
= pKey
->getValue( "DuplexNoTumble" );
637 case DuplexMode::Unknown
:
643 pValue
= pKey
->getDefaultValue();
644 aData
.m_aContext
.setValue( pKey
, pValue
);
647 aData
.m_bPapersizeFromSetup
= pJobSetup
->GetPapersizeFromSetup();
650 copyJobDataToJobSetup( pJobSetup
, aData
);
657 void PspSalInfoPrinter::GetPageInfo(
658 const ImplJobSetup
* pJobSetup
,
659 long& rOutWidth
, long& rOutHeight
,
667 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
669 // get the selected page size
670 if( !aData
.m_pParser
)
676 int left
= 0, top
= 0, right
= 0, bottom
= 0;
677 int nDPI
= aData
.m_aContext
.getRenderResolution();
679 if( aData
.m_eOrientation
== psp::orientation::Portrait
)
681 aData
.m_aContext
.getPageSize( aPaper
, width
, height
);
682 aData
.m_pParser
->getMargins( aPaper
, left
, right
, top
, bottom
);
686 aData
.m_aContext
.getPageSize( aPaper
, height
, width
);
687 aData
.m_pParser
->getMargins( aPaper
, top
, bottom
, right
, left
);
690 rPaperSize
.setWidth( width
* nDPI
/ 72 );
691 rPaperSize
.setHeight( height
* nDPI
/ 72 );
692 rPageOffset
.setX( left
* nDPI
/ 72 );
693 rPageOffset
.setY( top
* nDPI
/ 72 );
694 rOutWidth
= ( width
- left
- right
) * nDPI
/ 72;
695 rOutHeight
= ( height
- top
- bottom
) * nDPI
/ 72;
699 sal_uInt16
PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup
* pJobSetup
)
705 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
707 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( "InputSlot" ): nullptr;
708 return pKey
? pKey
->countValues() : 0;
711 OUString
PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup
* pJobSetup
, sal_uInt16 nPaperBin
)
714 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
716 if( aData
.m_pParser
)
718 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey( "InputSlot" ): nullptr;
719 if( ! pKey
|| nPaperBin
>= static_cast<sal_uInt16
>(pKey
->countValues()) )
720 return aData
.m_pParser
->getDefaultInputSlot();
721 const PPDValue
* pValue
= pKey
->getValue( nPaperBin
);
723 return aData
.m_pParser
->translateOption( pKey
->getKey(), pValue
->m_aOption
);
729 sal_uInt32
PspSalInfoPrinter::GetCapabilities( const ImplJobSetup
* pJobSetup
, PrinterCapType nType
)
733 case PrinterCapType::SupportDialog
:
735 case PrinterCapType::Copies
:
737 case PrinterCapType::CollateCopies
:
739 // PPDs don't mention the number of possible collated copies.
740 // so let's guess as many as we want ?
743 case PrinterCapType::SetOrientation
:
745 case PrinterCapType::SetPaperSize
:
747 case PrinterCapType::SetPaper
:
749 case PrinterCapType::Fax
:
751 // see if the PPD contains the fax4CUPS "Dial" option and that it's not set
753 JobData aData
= PrinterInfoManager::get().getPrinterInfo(pJobSetup
->GetPrinterName());
754 if( pJobSetup
->GetDriverData() )
755 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
756 const PPDKey
* pKey
= aData
.m_pParser
? aData
.m_pParser
->getKey("Dial") : nullptr;
757 const PPDValue
* pValue
= pKey
? aData
.m_aContext
.getValue(pKey
) : nullptr;
758 if (pValue
&& !pValue
->m_aOption
.equalsIgnoreAsciiCase("Manually"))
763 case PrinterCapType::PDF
:
764 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup
->GetPrinterName(), "pdf" ) )
768 // see if the PPD contains a value to set PDF device
769 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->GetPrinterName() );
770 if( pJobSetup
->GetDriverData() )
771 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
772 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
774 case PrinterCapType::ExternalDialog
:
775 return PrinterInfoManager::get().checkFeatureToken( pJobSetup
->GetPrinterName(), "external_dialog" ) ? 1 : 0;
776 case PrinterCapType::UsePullModel
:
778 // see if the PPD contains a value to set PDF device
779 JobData aData
= PrinterInfoManager::get().getPrinterInfo( pJobSetup
->GetPrinterName() );
780 if( pJobSetup
->GetDriverData() )
781 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), aData
);
782 return aData
.m_nPDFDevice
> 0 ? 1 : 0;
792 PspSalPrinter::PspSalPrinter( SalInfoPrinter
* pInfoPrinter
)
793 : m_pInfoPrinter( pInfoPrinter
)
795 , m_bCollate( false )
797 , m_bIsPDFWriterJob( false )
801 PspSalPrinter::~PspSalPrinter()
805 static OUString
getTmpName()
808 osl_createTempFile( nullptr, nullptr, &aTmp
.pData
);
809 osl_getSystemPathFromFileURL( aTmp
.pData
, &aSys
.pData
);
814 bool PspSalPrinter::StartJob(
815 const OUString
* pFileName
,
816 const OUString
& rJobName
,
817 const OUString
& rAppName
,
821 ImplJobSetup
* pJobSetup
)
823 SAL_INFO( "vcl.unx.print", "PspSalPrinter::StartJob");
824 GetSalData()->m_pInstance
->jobStartedPrinterUpdate();
827 m_aFileName
= *pFileName
;
832 m_bCollate
= bCollate
;
834 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), m_aJobData
);
837 // in case user did not do anything (m_nCopies=1)
838 // take the default from jobsetup
839 m_aJobData
.m_nCopies
= m_nCopies
;
840 m_aJobData
.setCollate( bCollate
);
844 // check whether this printer is configured as fax
845 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
847 if (getPdfDir(rInfo
, sPdfDir
))
850 m_aTmpFile
= getTmpName();
851 nMode
= S_IRUSR
| S_IWUSR
;
853 if( m_aFileName
.isEmpty() )
854 m_aFileName
= sPdfDir
+ "/" + rJobName
+ ".pdf";
856 m_aPrinterGfx
.Init( m_aJobData
);
858 return m_aPrintJob
.StartJob( ! m_aTmpFile
.isEmpty() ? m_aTmpFile
: m_aFileName
, nMode
, rJobName
, rAppName
, m_aJobData
, &m_aPrinterGfx
, bDirect
);
861 bool PspSalPrinter::EndJob()
863 bool bSuccess
= false;
864 if( m_bIsPDFWriterJob
)
868 bSuccess
= m_aPrintJob
.EndJob();
869 SAL_INFO( "vcl.unx.print", "PspSalPrinter::EndJob " << bSuccess
);
871 if( bSuccess
&& m_bPdf
)
873 const PrinterInfo
& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData
.m_aPrinterName
) );
874 bSuccess
= createPdf( m_aFileName
, m_aTmpFile
, rInfo
.m_aCommand
);
877 GetSalData()->m_pInstance
->jobEndedPrinterUpdate();
881 SalGraphics
* PspSalPrinter::StartPage( ImplJobSetup
* pJobSetup
, bool )
883 SAL_INFO( "vcl.unx.print", "PspSalPrinter::StartPage");
885 JobData::constructFromStreamBuffer( pJobSetup
->GetDriverData(), pJobSetup
->GetDriverDataLen(), m_aJobData
);
886 m_xGraphics
= GetGenericInstance()->CreatePrintGraphics();
887 m_xGraphics
->Init(&m_aJobData
, &m_aPrinterGfx
);
891 // in case user did not do anything (m_nCopies=1)
892 // take the default from jobsetup
893 m_aJobData
.m_nCopies
= m_nCopies
;
894 m_aJobData
.setCollate( m_nCopies
> 1 && m_bCollate
);
897 m_aPrintJob
.StartPage( m_aJobData
);
898 m_aPrinterGfx
.Init( m_aPrintJob
);
900 return m_xGraphics
.get();
903 void PspSalPrinter::EndPage()
905 m_aPrintJob
.EndPage();
906 m_aPrinterGfx
.Clear();
907 SAL_INFO( "vcl.unx.print", "PspSalPrinter::EndPage");
910 struct PDFNewJobParameters
913 sal_uInt16 mnPaperBin
;
915 PDFNewJobParameters( const Size
& i_rSize
= Size(),
916 sal_uInt16 i_nPaperBin
= 0xffff )
917 : maPageSize( i_rSize
), mnPaperBin( i_nPaperBin
) {}
919 bool operator==(const PDFNewJobParameters
& rComp
) const
921 const long nRotatedWidth
= rComp
.maPageSize
.Height();
922 const long nRotatedHeight
= rComp
.maPageSize
.Width();
923 Size
aCompLSSize(nRotatedWidth
, nRotatedHeight
);
925 (maPageSize
== rComp
.maPageSize
|| maPageSize
== aCompLSSize
)
926 && mnPaperBin
== rComp
.mnPaperBin
930 bool operator!=(const PDFNewJobParameters
& rComp
) const
932 return ! operator==(rComp
);
938 OUString
const maTmpURL
;
939 PDFNewJobParameters
const maParameters
;
941 PDFPrintFile( const OUString
& i_rURL
, const PDFNewJobParameters
& i_rNewParameters
)
943 , maParameters( i_rNewParameters
) {}
946 bool PspSalPrinter::StartJob( const OUString
* i_pFileName
, const OUString
& i_rJobName
, const OUString
& i_rAppName
,
947 ImplJobSetup
* i_pSetupData
, vcl::PrinterController
& i_rController
)
949 SAL_INFO( "vcl.unx.print", "StartJob with controller: pFilename = " << (i_pFileName
? *i_pFileName
: "<nil>") );
951 m_bIsPDFWriterJob
= true;
953 i_rController
.setLastPage( false );
954 // is this a fax device
955 bool bFax
= m_pInfoPrinter
->GetCapabilities(i_pSetupData
, PrinterCapType::Fax
) == 1;
959 JobData::constructFromStreamBuffer( i_pSetupData
->GetDriverData(), i_pSetupData
->GetDriverDataLen(), m_aJobData
);
961 OSL_ASSERT( m_aJobData
.m_nPDFDevice
> 0 );
962 m_aJobData
.m_nPDFDevice
= 1;
964 // possibly create one job for collated output
965 bool bSinglePrintJobs
= false;
966 beans::PropertyValue
* pSingleValue
= i_rController
.getValue( "PrintCollateAsSingleJobs" );
969 pSingleValue
->Value
>>= bSinglePrintJobs
;
972 int nCopies
= i_rController
.getPrinter()->GetCopyCount();
973 bool bCollate
= i_rController
.getPrinter()->IsCollateCopy();
975 // notify start of real print job
976 i_rController
.jobStarted();
978 // setup PDFWriter context
979 vcl::PDFWriter::PDFWriterContext aContext
;
980 aContext
.Version
= vcl::PDFWriter::PDFVersion::PDF_1_4
;
981 aContext
.Tagged
= false;
982 aContext
.DocumentLocale
= Application::GetSettings().GetLanguageTag().getLocale();
983 aContext
.ColorMode
= i_rController
.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
984 ? vcl::PDFWriter::DrawGreyscale
: vcl::PDFWriter::DrawColor
;
987 aContext
.DocumentInfo
.Title
= i_rJobName
;
988 aContext
.DocumentInfo
.Creator
= i_rAppName
;
989 aContext
.DocumentInfo
.Producer
= i_rAppName
;
991 // define how we handle metafiles in PDFWriter
992 vcl::PDFWriter::PlayMetafileContext aMtfContext
;
993 aMtfContext
.m_bOnlyLosslessCompression
= true;
995 std::shared_ptr
<vcl::PDFWriter
> xWriter
;
996 std::vector
< PDFPrintFile
> aPDFFiles
;
997 VclPtr
<Printer
> xPrinter( i_rController
.getPrinter() );
998 int nAllPages
= i_rController
.getFilteredPageCount();
999 i_rController
.createProgressDialog();
1000 bool bAborted
= false;
1001 PDFNewJobParameters aLastParm
;
1003 aContext
.DPIx
= xPrinter
->GetDPIX();
1004 aContext
.DPIy
= xPrinter
->GetDPIY();
1005 for( int nPage
= 0; nPage
< nAllPages
&& ! bAborted
; nPage
++ )
1007 if( nPage
== nAllPages
-1 )
1008 i_rController
.setLastPage( true );
1010 // get the page's metafile
1011 GDIMetaFile aPageFile
;
1012 vcl::PrinterController::PageSize aPageSize
= i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1013 if( i_rController
.isProgressCanceled() )
1016 if( nPage
!= nAllPages
-1 )
1018 i_rController
.createProgressDialog();
1019 i_rController
.setLastPage( true );
1020 i_rController
.getFilteredPageFile( nPage
, aPageFile
);
1025 xPrinter
->SetMapMode( MapMode( MapUnit::Map100thMM
) );
1026 xPrinter
->SetPaperSizeUser( aPageSize
.aSize
, true );
1027 PDFNewJobParameters
aNewParm(xPrinter
->GetPaperSize(), xPrinter
->GetPaperBin());
1029 // create PDF writer on demand
1030 // either on first page
1031 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1032 // so we need to start a new job to get a new paper format from the printer
1033 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1035 (aNewParm
!= aLastParm
&& ! i_pFileName
) )
1044 aPDFUrl
= *i_pFileName
;
1046 osl_createTempFile( nullptr, nullptr, &aPDFUrl
.pData
);
1047 // normalize to file URL
1048 if( !comphelper::isFileUrl(aPDFUrl
) )
1050 // this is not a file URL, but it should
1051 // form it into an osl friendly file URL
1053 osl_getFileURLFromSystemPath( aPDFUrl
.pData
, &aTmp
.pData
);
1056 // save current file and paper format
1057 aLastParm
= aNewParm
;
1058 aPDFFiles
.emplace_back( aPDFUrl
, aNewParm
);
1060 aContext
.URL
= aPDFUrl
;
1062 // create and initialize PDFWriter
1063 xWriter
.reset( new vcl::PDFWriter( aContext
, uno::Reference
< beans::XMaterialHolder
>() ) );
1066 xWriter
->NewPage( TenMuToPt( aNewParm
.maPageSize
.Width() ),
1067 TenMuToPt( aNewParm
.maPageSize
.Height() ),
1068 vcl::PDFWriter::Orientation::Portrait
);
1070 xWriter
->PlayMetafile( aPageFile
, aMtfContext
);
1074 // emit the last file
1078 // handle collate, copy count and multiple jobs correctly
1080 if( bSinglePrintJobs
)
1082 nOuterJobs
= nCopies
;
1083 m_aJobData
.m_nCopies
= 1;
1089 if (aPDFFiles
.size() == 1 && xPrinter
->HasSupport(PrinterSupport::CollateCopy
))
1091 m_aJobData
.setCollate( true );
1092 m_aJobData
.m_nCopies
= nCopies
;
1096 nOuterJobs
= nCopies
;
1097 m_aJobData
.m_nCopies
= 1;
1102 m_aJobData
.setCollate( false );
1103 m_aJobData
.m_nCopies
= nCopies
;
1107 std::vector
<OUString
> aFaxNumbers
;
1109 // check for fax numbers
1110 if (!bAborted
&& bFax
)
1112 aFaxNumbers
= getFaxNumbers();
1113 bAborted
= aFaxNumbers
.empty();
1116 bool bSuccess(true);
1118 if( ! i_pFileName
&& ! bAborted
)
1122 OUString sFaxNumber
;
1123 if (!aFaxNumbers
.empty())
1125 sFaxNumber
= aFaxNumbers
.back();
1126 aFaxNumbers
.pop_back();
1129 bool bFirstJob
= true;
1130 for( int nCurJob
= 0; nCurJob
< nOuterJobs
; nCurJob
++ )
1132 for( size_t i
= 0; i
< aPDFFiles
.size(); i
++ )
1134 oslFileHandle pFile
= nullptr;
1135 osl_openFile( aPDFFiles
[i
].maTmpURL
.pData
, &pFile
, osl_File_OpenFlag_Read
);
1136 if (pFile
&& (osl_setFilePos(pFile
, osl_Pos_Absolut
, 0) == osl_File_E_None
))
1138 std::vector
< char > buffer( 0x10000, 0 );
1139 // update job data with current page size
1140 Size
aPageSize( aPDFFiles
[i
].maParameters
.maPageSize
);
1141 m_aJobData
.setPaper( TenMuToPt( aPageSize
.Width() ), TenMuToPt( aPageSize
.Height() ) );
1142 // update job data with current paperbin
1143 m_aJobData
.setPaperBin( aPDFFiles
[i
].maParameters
.mnPaperBin
);
1145 // spool current file
1146 FILE* fp
= PrinterInfoManager::get().startSpool(xPrinter
->GetName(), i_rController
.isDirectPrint());
1149 sal_uInt64 nBytesRead
= 0;
1152 osl_readFile( pFile
, buffer
.data(), buffer
.size(), &nBytesRead
);
1153 if( nBytesRead
> 0 )
1155 size_t nBytesWritten
= fwrite(buffer
.data(), 1, nBytesRead
, fp
);
1156 OSL_ENSURE(nBytesRead
== nBytesWritten
, "short write");
1157 if (nBytesRead
!= nBytesWritten
)
1160 } while( nBytesRead
== buffer
.size() );
1161 OUStringBuffer
aBuf( i_rJobName
.getLength() + 8 );
1162 aBuf
.append( i_rJobName
);
1163 if( i
> 0 || nCurJob
> 0 )
1166 aBuf
.append( sal_Int32( i
+ nCurJob
* aPDFFiles
.size() ) );
1169 PrinterInfoManager::get().endSpool(xPrinter
->GetName(), aBuf
.makeStringAndClear(), fp
, m_aJobData
, bFirstJob
, sFaxNumber
);
1173 osl_closeFile( pFile
);
1177 while (!aFaxNumbers
.empty());
1180 // job has been spooled
1181 i_rController
.setJobState( bAborted
1182 ? view::PrintableState_JOB_ABORTED
1183 : (bSuccess
? view::PrintableState_JOB_SPOOLED
1184 : view::PrintableState_JOB_SPOOLING_FAILED
));
1186 // clean up the temporary PDF files
1187 if( ! i_pFileName
|| bAborted
)
1189 for(PDFPrintFile
& rPDFFile
: aPDFFiles
)
1191 osl_removeFile( rPDFFile
.maTmpURL
.pData
);
1192 SAL_INFO( "vcl.unx.print", "removed print PDF file " << rPDFFile
.maTmpURL
);
1201 static Idle
* pPrinterUpdateIdle
;
1202 static int nActiveJobs
;
1204 static void doUpdate();
1205 DECL_STATIC_LINK( PrinterUpdate
, UpdateTimerHdl
, Timer
*, void );
1207 static void update(SalGenericInstance
const &rInstance
);
1208 static void jobStarted() { nActiveJobs
++; }
1209 static void jobEnded();
1212 Idle
* PrinterUpdate::pPrinterUpdateIdle
= nullptr;
1213 int PrinterUpdate::nActiveJobs
= 0;
1215 void PrinterUpdate::doUpdate()
1217 ::psp::PrinterInfoManager
& rManager( ::psp::PrinterInfoManager::get() );
1218 SalGenericInstance
*pInst
= static_cast<SalGenericInstance
*>( GetSalData()->m_pInstance
);
1219 if( pInst
&& rManager
.checkPrintersChanged( false ) )
1220 pInst
->PostPrintersChanged();
1223 IMPL_STATIC_LINK_NOARG( PrinterUpdate
, UpdateTimerHdl
, Timer
*, void )
1225 if( nActiveJobs
< 1 )
1228 delete pPrinterUpdateIdle
;
1229 pPrinterUpdateIdle
= nullptr;
1232 pPrinterUpdateIdle
->Start();
1235 void PrinterUpdate::update(SalGenericInstance
const &rInstance
)
1237 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1240 if( ! rInstance
.isPrinterInit() )
1242 // #i45389# start background printer detection
1243 psp::PrinterInfoManager::get();
1247 if( nActiveJobs
< 1 )
1249 else if( ! pPrinterUpdateIdle
)
1251 pPrinterUpdateIdle
= new Idle("PrinterUpdateTimer");
1252 pPrinterUpdateIdle
->SetPriority( TaskPriority::LOWEST
);
1253 pPrinterUpdateIdle
->SetInvokeHandler( LINK( nullptr, PrinterUpdate
, UpdateTimerHdl
) );
1254 pPrinterUpdateIdle
->Start();
1258 void SalGenericInstance::updatePrinterUpdate()
1260 PrinterUpdate::update(*this);
1263 void SalGenericInstance::jobStartedPrinterUpdate()
1265 PrinterUpdate::jobStarted();
1268 void PrinterUpdate::jobEnded()
1271 if( nActiveJobs
< 1 )
1273 if( pPrinterUpdateIdle
)
1275 pPrinterUpdateIdle
->Stop();
1276 delete pPrinterUpdateIdle
;
1277 pPrinterUpdateIdle
= nullptr;
1283 void SalGenericInstance::jobEndedPrinterUpdate()
1285 PrinterUpdate::jobEnded();
1288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */