/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
/**
this file implements the sal printer interface ( SalPrinter , SalInfoPrinter
and some printer relevant methods of SalInstance and SalGraphicsData )
as underlying library the printer features of psprint are used .
The query methods of a SalInfoPrinter are implemented by querying psprint
The job methods of a SalPrinter are implemented by calling psprint
printer job functions .
*/
#include <sal/config.h>
#include <string_view>
// For spawning PDF and FAX generation
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <comphelper/fileurl.hxx>
#include <o3tl/safeint.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>
#include <osl/file.hxx>
#include <utility>
#include <vcl/gdimtf.hxx>
#include <vcl/idle.hxx>
#include <vcl/printer/Options.hxx>
#include <vcl/print.hxx>
#include <vcl/QueueInfo.hxx>
#include <vcl/pdfwriter.hxx>
#include <printerinfomanager.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/weld.hxx>
#include <strings.hrc>
#include <unx/genprn.h>
#include <unx/geninst.h>
#include <unx/genpspgraphics.h>
#include <jobset.h>
#include <print.h>
#include "prtsetup.hxx"
#include <salptype.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
using namespace psp;
using namespace com::sun::star;
static bool getPdfDir( const PrinterInfo& rInfo, OUString &rDir )
{
sal_Int32 nIndex = 0 ;
while ( nIndex != -1 )
{
OUString aToken( rInfo.m_aFeatures.getToken( 0 , ',' , nIndex ) );
if ( aToken.startsWith( "pdf=" ) )
{
sal_Int32 nPos = 0 ;
rDir = aToken.getToken( 1 , '=' , nPos );
if ( rDir.isEmpty() && getenv( "HOME" ) )
rDir = OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
return true ;
}
}
return false ;
}
namespace
{
class QueryString : public weld::GenericDialogController
{
private :
OUString& m_rReturnValue;
std::unique_ptr<weld::Button> m_xOKButton;
std::unique_ptr<weld::Label> m_xFixedText;
std::unique_ptr<weld::Entry> m_xEdit;
DECL_LINK( ClickBtnHdl, weld::Button&, void );
public :
// parent window, Query text, initial value
QueryString(weld::Window*, OUString const &, OUString &);
};
/*
* QueryString
*/
QueryString::QueryString(weld::Window* pParent, OUString const & rQuery, OUString& rRet)
: GenericDialogController(pParent, u"vcl/ui/querydialog.ui" _ustr, u"QueryDialog" _ustr)
, m_rReturnValue( rRet )
, m_xOKButton(m_xBuilder->weld_button(u"ok" _ustr))
, m_xFixedText(m_xBuilder->weld_label(u"label" _ustr))
, m_xEdit(m_xBuilder->weld_entry(u"entry" _ustr))
{
m_xOKButton->connect_clicked(LINK(this , QueryString, ClickBtnHdl));
m_xFixedText->set_label(rQuery);
m_xEdit->set_text(m_rReturnValue);
m_xDialog->set_title(rQuery);
}
IMPL_LINK(QueryString, ClickBtnHdl, weld::Button&, rButton, void )
{
if (&rButton == m_xOKButton.get())
{
m_rReturnValue = m_xEdit->get_text();
m_xDialog->response(RET_OK);
}
else
m_xDialog->response(RET_CANCEL);
}
int QueryFaxNumber(OUString& rNumber)
{
QueryString aQuery(Application::GetDefDialogParent(), VclResId(SV_PRINT_QUERYFAXNUMBER_TXT), rNumber);
return aQuery.run();
}
}
static int PtTo10Mu( int nPoints ) { return static_cast <int >((static_cast <double >(nPoints)*35 .27777778 )+0 .5 ); }
static int TenMuToPt( int nUnits ) { return static_cast <int >((static_cast <double >(nUnits)/35 .27777778 )+0 .5 ); }
static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
{
pJobSetup->SetOrientation( rData.m_eOrientation == orientation::Landscape ?
Orientation::Landscape : Orientation::Portrait );
// copy page size
OUString aPaper;
int width, height;
rData.m_aContext.getPageSize( aPaper, width, height );
pJobSetup->SetPaperFormat( PaperInfo::fromPSName(
OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 )));
pJobSetup->SetPaperWidth( 0 );
pJobSetup->SetPaperHeight( 0 );
if ( pJobSetup->GetPaperFormat() == PAPER_USER )
{
// transform to 100dth mm
width = PtTo10Mu( width );
height = PtTo10Mu( height );
if ( rData.m_eOrientation == psp::orientation::Portrait )
{
pJobSetup->SetPaperWidth( width );
pJobSetup->SetPaperHeight( height );
}
else
{
pJobSetup->SetPaperWidth( height );
pJobSetup->SetPaperHeight( width );
}
}
// copy input slot
const PPDKey* pKey = nullptr;
const PPDValue* pValue = nullptr;
pJobSetup->SetPaperBin( 0 );
if ( rData.m_pParser )
pKey = rData.m_pParser->getKey( u"InputSlot" _ustr );
if ( pKey )
pValue = rData.m_aContext.getValue( pKey );
if ( pKey && pValue )
{
int nPaperBin;
for ( nPaperBin = 0 ;
pValue != pKey->getValue( nPaperBin ) &&
nPaperBin < pKey->countValues();
nPaperBin++);
pJobSetup->SetPaperBin(
nPaperBin == pKey->countValues() ? 0 : nPaperBin);
}
// copy duplex
pKey = nullptr;
pValue = nullptr;
pJobSetup->SetDuplexMode( DuplexMode::Unknown );
if ( rData.m_pParser )
pKey = rData.m_pParser->getKey( u"Duplex" _ustr );
if ( pKey )
pValue = rData.m_aContext.getValue( pKey );
if ( pKey && pValue )
{
if ( pValue->m_aOption.equalsIgnoreAsciiCase( "None" ) ||
pValue->m_aOption.startsWithIgnoreAsciiCase( "Simplex" )
)
{
pJobSetup->SetDuplexMode( DuplexMode::Off);
}
else if ( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
{
pJobSetup->SetDuplexMode( DuplexMode::LongEdge );
}
else if ( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexTumble" ) )
{
pJobSetup->SetDuplexMode( DuplexMode::ShortEdge );
}
}
// copy the whole context
sal_uInt32 nBytes;
std::unique_ptr<sal_uInt8[]> pBuffer;
if ( rData.getStreamBuffer( pBuffer, nBytes ) )
{
pJobSetup->SetDriverData( std::move(pBuffer), nBytes );
}
else
{
pJobSetup->SetDriverData( nullptr, 0 );
}
pJobSetup->SetPapersizeFromSetup( rData.m_bPapersizeFromSetup );
}
static std::vector<OUString> getFaxNumbers()
{
std::vector<OUString> aFaxNumbers;
OUString aNewNr;
if (QueryFaxNumber(aNewNr))
{
for (sal_Int32 nIndex {0 }; nIndex >= 0 ; )
aFaxNumbers.push_back(aNewNr.getToken( 0 , ';' , nIndex ));
}
return aFaxNumbers;
}
/*
* SalInstance
*/
void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter *pPrinter,
SalPrinterQueueInfo const * pQueueInfo, ImplJobSetup* pJobSetup)
{
if ( !pJobSetup )
return ;
PrinterInfoManager& rManager( PrinterInfoManager::get() );
PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
pPrinter->m_aJobData = aInfo;
if ( pJobSetup->GetDriverData() )
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(),
pJobSetup->GetDriverDataLen(), aInfo );
pJobSetup->SetSystem( JOBSETUP_SYSTEM_UNIX );
pJobSetup->SetPrinterName( pQueueInfo->maPrinterName );
pJobSetup->SetDriver( aInfo.m_aDriverName );
copyJobDataToJobSetup( pJobSetup, aInfo );
}
SalInfoPrinter* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
ImplJobSetup* pJobSetup )
{
mbPrinterInit = true ;
// create and initialize SalInfoPrinter
PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter();
configurePspInfoPrinter(pPrinter, pQueueInfo, pJobSetup);
return pPrinter;
}
void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
{
delete pPrinter;
}
std::unique_ptr<SalPrinter> SalGenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
{
mbPrinterInit = true ;
// create and initialize SalPrinter
PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
pPrinter->m_aJobData = static_cast <PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
return std::unique_ptr<SalPrinter>(pPrinter);
}
void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
{
mbPrinterInit = true ;
PrinterInfoManager& rManager( PrinterInfoManager::get() );
static const char * pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
if ( ! pNoSyncDetection || ! *pNoSyncDetection )
{
// #i62663# synchronize possible asynchronouse printer detection now
rManager.checkPrintersChanged( true );
}
::std::vector< OUString > aPrinters;
rManager.listPrinters( aPrinters );
for (auto const & printer : aPrinters)
{
const PrinterInfo& rInfo( rManager.getPrinterInfo(printer) );
// create new entry
std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
pInfo->maPrinterName = printer;
pInfo->maDriver = rInfo.m_aDriverName;
pInfo->maLocation = rInfo.m_aLocation;
pInfo->maComment = rInfo.m_aComment;
OUString sPdfDir;
if (getPdfDir(rInfo, sPdfDir))
pInfo->maLocation = sPdfDir;
pList->Add( std::move(pInfo) );
}
}
void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
{
mbPrinterInit = true ;
}
OUString SalGenericInstance::GetDefaultPrinter()
{
mbPrinterInit = true ;
PrinterInfoManager& rManager( PrinterInfoManager::get() );
return rManager.getDefaultPrinter();
}
PspSalInfoPrinter::PspSalInfoPrinter()
{
}
PspSalInfoPrinter::~PspSalInfoPrinter()
{
}
void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
{
m_aPaperFormats.clear();
m_bPapersInit = true ;
if ( !m_aJobData.m_pParser )
return ;
const PPDKey* pKey = m_aJobData.m_pParser->getKey( u"PageSize" _ustr );
if ( pKey )
{
int nValues = pKey->countValues();
for ( int i = 0 ; i < nValues; i++ )
{
const PPDValue* pValue = pKey->getValue( i );
int nWidth = 0 , nHeight = 0 ;
m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
m_aPaperFormats.push_back( aInfo );
}
}
}
int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
{
return 900 ;
}
SalGraphics* PspSalInfoPrinter::AcquireGraphics()
{
// return a valid pointer only once
// the reasoning behind this is that we could have different
// SalGraphics that can run in multiple threads
// (future plans)
SalGraphics* pRet = nullptr;
if ( ! m_pGraphics )
{
m_pGraphics = GetGenericInstance()->CreatePrintGraphics();
m_pGraphics->Init(&m_aJobData);
pRet = m_pGraphics.get();
}
return pRet;
}
void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
{
if ( m_pGraphics.get() == pGraphics )
{
m_pGraphics.reset();
}
}
bool PspSalInfoPrinter::Setup( weld::Window* pFrame, ImplJobSetup* pJobSetup )
{
if ( ! pFrame || ! pJobSetup )
return false ;
PrinterInfoManager& rManager = PrinterInfoManager::get();
PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->GetPrinterName() ) );
if ( pJobSetup->GetDriverData() )
{
SetData( JobSetFlags::ALL, pJobSetup );
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aInfo );
}
aInfo.m_bPapersizeFromSetup = pJobSetup->GetPapersizeFromSetup();
aInfo.meSetupMode = pJobSetup->GetPrinterSetupMode();
if (SetupPrinterDriver(pFrame, aInfo))
{
pJobSetup->SetDriverData( nullptr, 0 );
sal_uInt32 nBytes;
std::unique_ptr<sal_uInt8[]> pBuffer;
aInfo.getStreamBuffer( pBuffer, nBytes );
pJobSetup->SetDriverData( std::move(pBuffer), nBytes );
// copy everything to job setup
copyJobDataToJobSetup( pJobSetup, aInfo );
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), m_aJobData );
return true ;
}
return false ;
}
// This function gets the driver data and puts it into pJobSetup
// If pJobSetup->GetDriverData() is NOT NULL, then the independent
// data should be merged into the driver data
// If pJobSetup->GetDriverData() IS NULL, then the driver defaults
// should be merged into the independent data
bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
{
if ( pJobSetup->GetDriverData() )
return SetData( JobSetFlags::ALL, pJobSetup );
copyJobDataToJobSetup( pJobSetup, m_aJobData );
return true ;
}
// This function merges the independent driver data
// and sets the new independent data in pJobSetup
// Only the data must be changed, where the bit
// in nGetDataFlags is set
bool PspSalInfoPrinter::SetData(
JobSetFlags nSetDataFlags,
ImplJobSetup* pJobSetup )
{
JobData aData;
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
if ( aData.m_pParser )
{
const PPDKey* pKey;
const PPDValue* pValue;
// merge orientation if necessary
if ( nSetDataFlags & JobSetFlags::ORIENTATION )
aData.m_eOrientation = pJobSetup->GetOrientation() == Orientation::Landscape ? orientation::Landscape : orientation::Portrait;
// merge papersize if necessary
if ( nSetDataFlags & JobSetFlags::PAPERSIZE )
{
OUString aPaper;
if ( pJobSetup->GetPaperFormat() == PAPER_USER )
aPaper = aData.m_pParser->matchPaper(
TenMuToPt( pJobSetup->GetPaperWidth() ),
TenMuToPt( pJobSetup->GetPaperHeight() ),
&aData.m_eOrientation );
else
aPaper = OStringToOUString(PaperInfo::toPSName(pJobSetup->GetPaperFormat()), RTL_TEXTENCODING_ISO_8859_1);
pKey = aData.m_pParser->getKey( u"PageSize" _ustr );
pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : nullptr;
// some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
// try to find the correct paper anyway using the size
if ( pKey && ! pValue && pJobSetup->GetPaperFormat() != PAPER_USER )
{
PaperInfo aInfo( pJobSetup->GetPaperFormat() );
aPaper = aData.m_pParser->matchPaper(
TenMuToPt( aInfo.getWidth() ),
TenMuToPt( aInfo.getHeight() ),
&aData.m_eOrientation );
pValue = pKey->getValueCaseInsensitive( aPaper );
}
if ( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue ) == pValue ) )
return false ;
}
// merge paperbin if necessary
if ( nSetDataFlags & JobSetFlags::PAPERBIN )
{
pKey = aData.m_pParser->getKey( u"InputSlot" _ustr );
if ( pKey )
{
int nPaperBin = pJobSetup->GetPaperBin();
if ( nPaperBin >= pKey->countValues() )
pValue = pKey->getDefaultValue();
else
pValue = pKey->getValue( pJobSetup->GetPaperBin() );
// may fail due to constraints;
// real paper bin is copied back to jobsetup in that case
aData.m_aContext.setValue( pKey, pValue );
}
// if printer has no InputSlot key simply ignore this setting
// (e.g. SGENPRT has no InputSlot)
}
// merge duplex if necessary
if ( nSetDataFlags & JobSetFlags::DUPLEXMODE )
{
pKey = aData.m_pParser->getKey( u"Duplex" _ustr );
if ( pKey )
{
pValue = nullptr;
switch ( pJobSetup->GetDuplexMode() )
{
case DuplexMode::Off:
pValue = pKey->getValue( u"None" _ustr );
if ( pValue == nullptr )
pValue = pKey->getValue( u"SimplexNoTumble" _ustr );
break ;
case DuplexMode::ShortEdge:
pValue = pKey->getValue( u"DuplexTumble" _ustr );
break ;
case DuplexMode::LongEdge:
pValue = pKey->getValue( u"DuplexNoTumble" _ustr );
break ;
case DuplexMode::Unknown:
default :
pValue = nullptr;
break ;
}
if ( ! pValue )
pValue = pKey->getDefaultValue();
aData.m_aContext.setValue( pKey, pValue );
}
}
aData.m_bPapersizeFromSetup = pJobSetup->GetPapersizeFromSetup();
m_aJobData = aData;
copyJobDataToJobSetup( pJobSetup, aData );
return true ;
}
return false ;
}
void PspSalInfoPrinter::GetPageInfo(
const ImplJobSetup* pJobSetup,
tools::Long & rOutWidth, tools::Long & rOutHeight,
Point& rPageOffset,
Size& rPaperSize )
{
if ( ! pJobSetup )
return ;
JobData aData;
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
// get the selected page size
if ( !aData.m_pParser )
return ;
OUString aPaper;
int width, height;
int left = 0 , top = 0 , right = 0 , bottom = 0 ;
int nDPI = aData.m_aContext.getRenderResolution();
if ( aData.m_eOrientation == psp::orientation::Portrait )
{
aData.m_aContext.getPageSize( aPaper, width, height );
aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
}
else
{
aData.m_aContext.getPageSize( aPaper, height, width );
aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
}
rPaperSize.setWidth( width * nDPI / 72 );
rPaperSize.setHeight( height * nDPI / 72 );
rPageOffset.setX( left * nDPI / 72 );
rPageOffset.setY( top * nDPI / 72 );
rOutWidth = ( width - left - right ) * nDPI / 72 ;
rOutHeight = ( height - top - bottom ) * nDPI / 72 ;
}
sal_uInt16 PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
{
if ( ! pJobSetup )
return 0 ;
JobData aData;
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( u"InputSlot" _ustr ): nullptr;
return pKey ? pKey->countValues() : 0 ;
}
OUString PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uInt16 nPaperBin )
{
JobData aData;
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
if ( aData.m_pParser )
{
const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( u"InputSlot" _ustr ): nullptr;
if ( ! pKey || nPaperBin >= o3tl::make_unsigned(pKey->countValues()) )
return aData.m_pParser->getDefaultInputSlot();
const PPDValue* pValue = pKey->getValue( nPaperBin );
if ( pValue )
return aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
}
return OUString();
}
sal_uInt16 PspSalInfoPrinter::GetPaperBinBySourceIndex( const ImplJobSetup*, sal_uInt16 )
{
return 0 xffff;
}
sal_uInt16 PspSalInfoPrinter::GetSourceIndexByPaperBin(const ImplJobSetup*, sal_uInt16)
{
return 0 ;
}
sal_uInt32 PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, PrinterCapType nType )
{
switch ( nType )
{
case PrinterCapType::SupportDialog:
return 1 ;
case PrinterCapType::Copies:
return 0 xffff;
case PrinterCapType::CollateCopies:
{
// PPDs don't mention the number of possible collated copies.
// so let's guess as many as we want ?
return 0 xffff;
}
case PrinterCapType::SetOrientation:
return 1 ;
case PrinterCapType::SetPaperSize:
return 1 ;
case PrinterCapType::SetPaper:
return 0 ;
case PrinterCapType::Fax:
{
// see if the PPD contains the fax4CUPS "Dial" option and that it's not set
// to "manually"
JobData aData = PrinterInfoManager::get().getPrinterInfo(pJobSetup->GetPrinterName());
if ( pJobSetup->GetDriverData() )
JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey(u"Dial" _ustr) : nullptr;
const PPDValue* pValue = pKey ? aData.m_aContext.getValue(pKey) : nullptr;
if (pValue && !pValue->m_aOption.equalsIgnoreAsciiCase("Manually" ))
return 1 ;
return 0 ;
}
case PrinterCapType::PDF:
return 1 ;
case PrinterCapType::ExternalDialog:
return PrinterInfoManager::get().checkFeatureToken( pJobSetup->GetPrinterName(), "external_dialog" ) ? 1 : 0 ;
case PrinterCapType::UsePullModel:
return 1 ;
default : break ;
}
return 0 ;
}
/*
* SalPrinter
*/
PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
: m_pInfoPrinter( pInfoPrinter )
{
}
PspSalPrinter::~PspSalPrinter()
{
}
bool PspSalPrinter::StartJob(
const OUString* /*pFileName*/,
const OUString& /*rJobName*/,
const OUString& /*rAppName*/,
sal_uInt32 /*nCopies*/,
bool /*bCollate*/,
bool /*bDirect*/,
ImplJobSetup* /*pJobSetup*/ )
{
OSL_FAIL( "should never be called" );
return false ;
}
bool PspSalPrinter::EndJob()
{
return true ;
}
SalGraphics* PspSalPrinter::StartPage( ImplJobSetup*, bool )
{
OSL_FAIL( "should never be called" );
return nullptr;
}
void PspSalPrinter::EndPage()
{
OSL_FAIL( "should never be called" );
}
namespace {
struct PDFNewJobParameters
{
Size maPageSize;
sal_uInt16 mnPaperBin;
PDFNewJobParameters( const Size& i_rSize = Size(),
sal_uInt16 i_nPaperBin = 0 xffff )
: maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
bool operator ==(const PDFNewJobParameters& rComp ) const
{
const tools::Long nRotatedWidth = rComp.maPageSize.Height();
const tools::Long nRotatedHeight = rComp.maPageSize.Width();
Size aCompLSSize(nRotatedWidth, nRotatedHeight);
return
(maPageSize == rComp.maPageSize || maPageSize == aCompLSSize)
&& mnPaperBin == rComp.mnPaperBin
;
}
bool operator !=(const PDFNewJobParameters& rComp) const
{
return ! operator ==(rComp);
}
};
struct PDFPrintFile
{
OUString maTmpURL;
PDFNewJobParameters maParameters;
PDFPrintFile( OUString i_URL, const PDFNewJobParameters& i_rNewParameters )
: maTmpURL(std::move( i_URL ))
, maParameters( i_rNewParameters ) {}
};
}
bool PspSalPrinter::StartJob( const OUString* i_pFileName, const OUString& i_rJobName, const OUString& i_rAppName,
ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
{
SAL_INFO( "vcl.unx.print" , "StartJob with controller: pFilename = " << (i_pFileName ? *i_pFileName : u"<nil>" _ustr) );
// reset IsLastPage
i_rController.setLastPage( false );
// is this a fax device
bool bFax = m_pInfoPrinter->GetCapabilities(i_pSetupData, PrinterCapType::Fax) == 1 ;
// update job data
if ( i_pSetupData )
JobData::constructFromStreamBuffer( i_pSetupData->GetDriverData(), i_pSetupData->GetDriverDataLen(), m_aJobData );
// possibly create one job for collated output
int nCopies = i_rController.getPrinter()->GetCopyCount();
bool bCollate = i_rController.getPrinter()->IsCollateCopy();
bool bSinglePrintJobs = i_rController.getPrinter()->IsSinglePrintJobs();
// notify start of real print job
i_rController.jobStarted();
// setup PDFWriter context
vcl::PDFWriter::PDFWriterContext aContext;
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_4;
aContext.Tagged = false ;
aContext.DocumentLocale = Application::GetSettings().GetLanguageTag().getLocale();
aContext.ColorMode = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor;
// prepare doc info
aContext.DocumentInfo.Title = i_rJobName;
aContext.DocumentInfo.Creator = i_rAppName;
aContext.DocumentInfo.Producer = i_rAppName;
// define how we handle metafiles in PDFWriter
vcl::PDFWriter::PlayMetafileContext aMtfContext;
aMtfContext.m_bOnlyLosslessCompression = true ;
std::shared_ptr<vcl::PDFWriter> xWriter;
std::vector< PDFPrintFile > aPDFFiles;
VclPtr<Printer> xPrinter( i_rController.getPrinter() );
int nAllPages = i_rController.getFilteredPageCount();
i_rController.createProgressDialog();
bool bAborted = false ;
PDFNewJobParameters aLastParm;
aContext.DPIx = xPrinter->GetDPIX();
aContext.DPIy = xPrinter->GetDPIY();
for ( int nPage = 0 ; nPage < nAllPages && ! bAborted; nPage++ )
{
if ( nPage == nAllPages-1 )
i_rController.setLastPage( true );
// get the page's metafile
GDIMetaFile aPageFile;
vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
if ( i_rController.isProgressCanceled() )
{
bAborted = true ;
if ( nPage != nAllPages-1 )
{
i_rController.createProgressDialog();
i_rController.setLastPage( true );
i_rController.getFilteredPageFile( nPage, aPageFile );
}
}
else
{
xPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
xPrinter->SetPaperSizeUser( aPageSize.aSize );
PDFNewJobParameters aNewParm(xPrinter->GetPaperSize(), xPrinter->GetPaperBin());
// create PDF writer on demand
// either on first page
// or on paper format change - cups does not support multiple paper formats per job (yet?)
// so we need to start a new job to get a new paper format from the printer
// orientation switches (that is switch of height and width) is handled transparently by CUPS
if ( ! xWriter ||
(aNewParm != aLastParm && ! i_pFileName ) )
{
if ( xWriter )
{
xWriter->Emit();
}
// produce PDF file
OUString aPDFUrl;
if ( i_pFileName )
aPDFUrl = *i_pFileName;
else
osl_createTempFile( nullptr, nullptr, &aPDFUrl.pData );
// normalize to file URL
if ( !comphelper::isFileUrl(aPDFUrl) )
{
// this is not a file URL, but it should
// form it into an osl friendly file URL
OUString aTmp;
osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
aPDFUrl = aTmp;
}
// save current file and paper format
aLastParm = aNewParm;
aPDFFiles.emplace_back( aPDFUrl, aNewParm );
// update context
aContext.URL = aPDFUrl;
// create and initialize PDFWriter
xWriter = std::make_shared<vcl::PDFWriter>( aContext, uno::Reference< beans::XMaterialHolder >() );
}
xWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
TenMuToPt( aNewParm.maPageSize.Height() ),
vcl::PDFWriter::Orientation::Portrait );
xWriter->PlayMetafile( aPageFile, aMtfContext );
}
}
// emit the last file
if ( xWriter )
xWriter->Emit();
// handle collate, copy count and multiple jobs correctly
int nOuterJobs = 1 ;
if ( bSinglePrintJobs )
{
nOuterJobs = nCopies;
m_aJobData.m_nCopies = 1 ;
}
else
{
if ( bCollate )
{
if (aPDFFiles.size() == 1 && xPrinter->HasSupport(PrinterSupport::CollateCopy))
{
m_aJobData.setCollate( true );
m_aJobData.m_nCopies = nCopies;
}
else
{
nOuterJobs = nCopies;
m_aJobData.m_nCopies = 1 ;
}
}
else
{
m_aJobData.setCollate( false );
m_aJobData.m_nCopies = nCopies;
}
}
std::vector<OUString> aFaxNumbers;
// check for fax numbers
if (!bAborted && bFax)
{
aFaxNumbers = getFaxNumbers();
bAborted = aFaxNumbers.empty();
}
bool bSuccess(true );
// spool files
if ( ! i_pFileName && ! bAborted )
{
do
{
OUString sFaxNumber;
if (!aFaxNumbers.empty())
{
sFaxNumber = aFaxNumbers.back();
aFaxNumbers.pop_back();
}
bool bFirstJob = true ;
for ( int nCurJob = 0 ; nCurJob < nOuterJobs; nCurJob++ )
{
for ( size_t i = 0 ; i < aPDFFiles.size(); i++ )
{
oslFileHandle pFile = nullptr;
osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
if (pFile && (osl_setFilePos(pFile, osl_Pos_Absolut, 0 ) == osl_File_E_None))
{
std::vector< char > buffer( 0 x10000, 0 );
// update job data with current page size
Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
// update job data with current paperbin
m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
// spool current file
FILE* fp = PrinterInfoManager::get().startSpool(xPrinter->GetName(), i_rController.isDirectPrint());
if ( fp )
{
sal_uInt64 nBytesRead = 0 ;
do
{
osl_readFile( pFile, buffer.data(), buffer.size(), &nBytesRead );
if ( nBytesRead > 0 )
{
size_t nBytesWritten = fwrite(buffer.data(), 1 , nBytesRead, fp);
OSL_ENSURE(nBytesRead == nBytesWritten, "short write" );
if (nBytesRead != nBytesWritten)
break ;
}
} while ( nBytesRead == buffer.size() );
OUStringBuffer aBuf( i_rJobName.getLength() + 8 );
aBuf.append( i_rJobName );
if ( i > 0 || nCurJob > 0 )
{
aBuf.append( ' ' );
aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
}
bSuccess &=
PrinterInfoManager::get().endSpool(xPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob, sFaxNumber);
bFirstJob = false ;
}
}
osl_closeFile( pFile );
}
}
}
while (!aFaxNumbers.empty());
}
// job has been spooled
i_rController.setJobState( bAborted
? view::PrintableState_JOB_ABORTED
: (bSuccess ? view::PrintableState_JOB_SPOOLED
: view::PrintableState_JOB_SPOOLING_FAILED));
// clean up the temporary PDF files
if ( ! i_pFileName || bAborted )
{
for (PDFPrintFile & rPDFFile : aPDFFiles)
{
osl_removeFile( rPDFFile.maTmpURL.pData );
SAL_INFO( "vcl.unx.print" , "removed print PDF file " << rPDFFile.maTmpURL );
}
}
return true ;
}
void SalGenericInstance::updatePrinterUpdate()
{
if ( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
return ;
if (!isPrinterInit())
{
// #i45389# start background printer detection
psp::PrinterInfoManager::get();
return ;
}
::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
if (rManager.checkPrintersChanged(false ))
PostPrintersChanged();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=92 H=93 G=92
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-07-12)
¤
*© Formatika GbR, Deutschland