summaryrefslogtreecommitdiffstats
path: root/vcl/unx/generic/print
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/unx/generic/print')
-rw-r--r--vcl/unx/generic/print/genprnpsp.cxx1097
-rw-r--r--vcl/unx/generic/print/genpspgraphics.cxx195
-rw-r--r--vcl/unx/generic/print/prtsetup.cxx465
-rw-r--r--vcl/unx/generic/print/prtsetup.hxx135
-rw-r--r--vcl/unx/generic/print/psheader.ps363
5 files changed, 2255 insertions, 0 deletions
diff --git a/vcl/unx/generic/print/genprnpsp.cxx b/vcl/unx/generic/print/genprnpsp.cxx
new file mode 100644
index 0000000000..33990decad
--- /dev/null
+++ b/vcl/unx/generic/print/genprnpsp.cxx
@@ -0,0 +1,1097 @@
+/* -*- 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/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, "vcl/ui/querydialog.ui", "QueryDialog")
+ , m_rReturnValue( rRet )
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , m_xFixedText(m_xBuilder->weld_label("label"))
+ , m_xEdit(m_xBuilder->weld_entry("entry"))
+ {
+ 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( "InputSlot" );
+ 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( "Duplex" );
+ 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( "PageSize" );
+ 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( "PageSize" );
+ 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( "InputSlot" );
+ 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( "Duplex" );
+ if( pKey )
+ {
+ pValue = nullptr;
+ switch( pJobSetup->GetDuplexMode() )
+ {
+ case DuplexMode::Off:
+ pValue = pKey->getValue( "None" );
+ if( pValue == nullptr )
+ pValue = pKey->getValue( "SimplexNoTumble" );
+ break;
+ case DuplexMode::ShortEdge:
+ pValue = pKey->getValue( "DuplexTumble" );
+ break;
+ case DuplexMode::LongEdge:
+ pValue = pKey->getValue( "DuplexNoTumble" );
+ 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( "InputSlot" ): 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( "InputSlot" ): 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_uInt32 PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, PrinterCapType nType )
+{
+ switch( nType )
+ {
+ case PrinterCapType::SupportDialog:
+ return 1;
+ case PrinterCapType::Copies:
+ return 0xffff;
+ case PrinterCapType::CollateCopies:
+ {
+ // PPDs don't mention the number of possible collated copies.
+ // so let's guess as many as we want ?
+ return 0xffff;
+ }
+ 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("Dial") : 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()
+{
+ GetSalInstance()->jobEndedPrinterUpdate();
+ 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 = 0xffff )
+ : 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 : "<nil>") );
+ // 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( 0x10000, 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;
+}
+
+namespace {
+
+class PrinterUpdate
+{
+ static Idle* pPrinterUpdateIdle;
+ static int nActiveJobs;
+
+ static void doUpdate();
+ DECL_STATIC_LINK( PrinterUpdate, UpdateTimerHdl, Timer*, void );
+public:
+ static void update(SalGenericInstance const &rInstance);
+ static void jobEnded();
+};
+
+}
+
+Idle* PrinterUpdate::pPrinterUpdateIdle = nullptr;
+int PrinterUpdate::nActiveJobs = 0;
+
+void PrinterUpdate::doUpdate()
+{
+ ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
+ SalGenericInstance *pInst = GetGenericInstance();
+ if( pInst && rManager.checkPrintersChanged( false ) )
+ pInst->PostPrintersChanged();
+}
+
+IMPL_STATIC_LINK_NOARG( PrinterUpdate, UpdateTimerHdl, Timer*, void )
+{
+ if( nActiveJobs < 1 )
+ {
+ doUpdate();
+ delete pPrinterUpdateIdle;
+ pPrinterUpdateIdle = nullptr;
+ }
+ else
+ pPrinterUpdateIdle->Start();
+}
+
+void PrinterUpdate::update(SalGenericInstance const &rInstance)
+{
+ if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
+ return;
+
+ if( ! rInstance.isPrinterInit() )
+ {
+ // #i45389# start background printer detection
+ psp::PrinterInfoManager::get();
+ return;
+ }
+
+ if( nActiveJobs < 1 )
+ doUpdate();
+ else if( ! pPrinterUpdateIdle )
+ {
+ pPrinterUpdateIdle = new Idle("PrinterUpdateTimer");
+ pPrinterUpdateIdle->SetPriority( TaskPriority::LOWEST );
+ pPrinterUpdateIdle->SetInvokeHandler( LINK( nullptr, PrinterUpdate, UpdateTimerHdl ) );
+ pPrinterUpdateIdle->Start();
+ }
+}
+
+void SalGenericInstance::updatePrinterUpdate()
+{
+ PrinterUpdate::update(*this);
+}
+
+void PrinterUpdate::jobEnded()
+{
+ nActiveJobs--;
+ if( nActiveJobs < 1 )
+ {
+ if( pPrinterUpdateIdle )
+ {
+ pPrinterUpdateIdle->Stop();
+ delete pPrinterUpdateIdle;
+ pPrinterUpdateIdle = nullptr;
+ doUpdate();
+ }
+ }
+}
+
+void SalGenericInstance::jobEndedPrinterUpdate()
+{
+ PrinterUpdate::jobEnded();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/genpspgraphics.cxx b/vcl/unx/generic/print/genpspgraphics.cxx
new file mode 100644
index 0000000000..66bf452474
--- /dev/null
+++ b/vcl/unx/generic/print/genpspgraphics.cxx
@@ -0,0 +1,195 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vector>
+
+#include <sal/types.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <i18nlangtag/mslangid.hxx>
+#include <jobdata.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <config_cairo_canvas.h>
+
+#include <fontsubset.hxx>
+#include <unx/freetype_glyphcache.hxx>
+#include <unx/geninst.h>
+#include <unx/genpspgraphics.h>
+#include <langboost.hxx>
+#include <font/LogicalFontInstance.hxx>
+#include <fontattributes.hxx>
+#include <font/FontMetricData.hxx>
+#include <font/FontSelectPattern.hxx>
+#include <font/PhysicalFontCollection.hxx>
+#include <font/PhysicalFontFace.hxx>
+#include <o3tl/string_view.hxx>
+#include <sallayout.hxx>
+
+using namespace psp;
+
+/*******************************************************
+ * GenPspGraphics
+ *******************************************************/
+
+GenPspGraphics::GenPspGraphics()
+ : m_pJobData( nullptr )
+ , m_aTextRenderImpl(m_aCairoCommon)
+ , m_pBackend(new SvpGraphicsBackend(m_aCairoCommon))
+{
+}
+
+void GenPspGraphics::Init(psp::JobData* pJob)
+{
+ m_pJobData = pJob;
+ SetLayout( SalLayoutFlags::NONE );
+}
+
+GenPspGraphics::~GenPspGraphics()
+{
+ ReleaseFonts();
+}
+
+void GenPspGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY )
+{
+ if (m_pJobData != nullptr)
+ {
+ int x = m_pJobData->m_aContext.getRenderResolution();
+
+ rDPIX = x;
+ rDPIY = x;
+ }
+}
+
+void GenPspGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ m_aTextRenderImpl.DrawTextLayout(rLayout, *this);
+}
+
+FontCharMapRef GenPspGraphics::GetFontCharMap() const
+{
+ return m_aTextRenderImpl.GetFontCharMap();
+}
+
+bool GenPspGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ return m_aTextRenderImpl.GetFontCapabilities(rFontCapabilities);
+}
+
+void GenPspGraphics::SetFont(LogicalFontInstance *pFontInstance, int nFallbackLevel)
+{
+ m_aTextRenderImpl.SetFont(pFontInstance, nFallbackLevel);
+}
+
+void GenPspGraphics::SetTextColor( Color nColor )
+{
+ m_aTextRenderImpl.SetTextColor(nColor);
+}
+
+bool GenPspGraphics::AddTempDevFont( vcl::font::PhysicalFontCollection* pFontCollection,
+ const OUString& rFileURL,
+ const OUString& rFontName )
+{
+ return m_aTextRenderImpl.AddTempDevFont(pFontCollection, rFileURL, rFontName);
+}
+
+void GenPspGraphics::GetDevFontList( vcl::font::PhysicalFontCollection *pFontCollection )
+{
+ m_aTextRenderImpl.GetDevFontList(pFontCollection);
+}
+
+void GenPspGraphics::ClearDevFontCache()
+{
+ m_aTextRenderImpl.ClearDevFontCache();
+}
+
+void GenPspGraphics::GetFontMetric(FontMetricDataRef& rxFontMetric, int nFallbackLevel)
+{
+ m_aTextRenderImpl.GetFontMetric(rxFontMetric, nFallbackLevel);
+}
+
+std::unique_ptr<GenericSalLayout> GenPspGraphics::GetTextLayout(int nFallbackLevel)
+{
+ return m_aTextRenderImpl.GetTextLayout(nFallbackLevel);
+}
+
+namespace vcl
+{
+ const char* getLangBoost()
+ {
+ const char* pLangBoost;
+ const LanguageType eLang = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ if (eLang == LANGUAGE_JAPANESE)
+ pLangBoost = "jan";
+ else if (MsLangId::isKorean(eLang))
+ pLangBoost = "kor";
+ else if (MsLangId::isSimplifiedChinese(eLang))
+ pLangBoost = "zhs";
+ else if (MsLangId::isTraditionalChinese(eLang))
+ pLangBoost = "zht";
+ else
+ pLangBoost = nullptr;
+ return pLangBoost;
+ }
+}
+
+SystemGraphicsData GenPspGraphics::GetGraphicsData() const
+{
+ return SystemGraphicsData();
+}
+
+#if ENABLE_CAIRO_CANVAS
+
+bool GenPspGraphics::SupportsCairo() const
+{
+ return false;
+}
+
+cairo::SurfaceSharedPtr GenPspGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr& /*rSurface*/) const
+{
+ return cairo::SurfaceSharedPtr();
+}
+
+cairo::SurfaceSharedPtr GenPspGraphics::CreateSurface(const OutputDevice& /*rRefDevice*/, int /*x*/, int /*y*/, int /*width*/, int /*height*/) const
+{
+ return cairo::SurfaceSharedPtr();
+}
+
+cairo::SurfaceSharedPtr GenPspGraphics::CreateBitmapSurface(const OutputDevice& /*rRefDevice*/, const BitmapSystemData& /*rData*/, const Size& /*rSize*/) const
+{
+ return cairo::SurfaceSharedPtr();
+}
+
+css::uno::Any GenPspGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& /*rSurface*/, const basegfx::B2ISize& /*rSize*/) const
+{
+ return css::uno::Any();
+}
+
+#endif // ENABLE_CAIRO_CANVAS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/prtsetup.cxx b/vcl/unx/generic/print/prtsetup.cxx
new file mode 100644
index 0000000000..4b0a63c1b3
--- /dev/null
+++ b/vcl/unx/generic/print/prtsetup.cxx
@@ -0,0 +1,465 @@
+/* -*- 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 .
+ */
+
+#include "prtsetup.hxx"
+#include <svdata.hxx>
+#include <strings.hrc>
+
+#include <officecfg/Office/Common.hxx>
+
+using namespace psp;
+
+void RTSDialog::insertAllPPDValues(weld::ComboBox& rBox, const PPDParser* pParser, const PPDKey* pKey )
+{
+ if( ! pKey || ! pParser )
+ return;
+
+ const PPDValue* pValue = nullptr;
+ OUString aOptionText;
+
+ for (int i = 0; i < pKey->countValues(); ++i)
+ {
+ pValue = pKey->getValue( i );
+ if (pValue->m_bCustomOption)
+ continue;
+ aOptionText = pParser->translateOption( pKey->getKey(), pValue->m_aOption) ;
+
+ OUString sId(weld::toId(pValue));
+ int nCurrentPos = rBox.find_id(sId);
+ if( m_aJobData.m_aContext.checkConstraints( pKey, pValue ) )
+ {
+ if (nCurrentPos == -1)
+ rBox.append(sId, aOptionText);
+ }
+ else
+ {
+ if (nCurrentPos != -1)
+ rBox.remove(nCurrentPos);
+ }
+ }
+ pValue = m_aJobData.m_aContext.getValue( pKey );
+ if (pValue && !pValue->m_bCustomOption)
+ {
+ OUString sId(weld::toId(pValue));
+ int nPos = rBox.find_id(sId);
+ if (nPos != -1)
+ rBox.set_active(nPos);
+ }
+}
+
+/*
+ * RTSDialog
+ */
+
+RTSDialog::RTSDialog(const PrinterInfo& rJobData, weld::Window* pParent)
+ : GenericDialogController(pParent, "vcl/ui/printerpropertiesdialog.ui", "PrinterPropertiesDialog")
+ , m_aJobData(rJobData)
+ , m_bDataModified(false)
+ , m_xTabControl(m_xBuilder->weld_notebook("tabcontrol"))
+ , m_xOKButton(m_xBuilder->weld_button("ok"))
+ , m_xCancelButton(m_xBuilder->weld_button("cancel"))
+ , m_xPaperPage(new RTSPaperPage(m_xTabControl->get_page("paper"), this))
+ , m_xDevicePage(new RTSDevicePage(m_xTabControl->get_page("device"), this))
+{
+ OUString aTitle(m_xDialog->get_title());
+ m_xDialog->set_title(aTitle.replaceAll("%s", m_aJobData.m_aPrinterName));
+
+ m_xTabControl->connect_enter_page( LINK( this, RTSDialog, ActivatePage ) );
+ m_xOKButton->connect_clicked( LINK( this, RTSDialog, ClickButton ) );
+ m_xCancelButton->connect_clicked( LINK( this, RTSDialog, ClickButton ) );
+ ActivatePage(m_xTabControl->get_current_page_ident());
+}
+
+RTSDialog::~RTSDialog()
+{
+}
+
+IMPL_LINK(RTSDialog, ActivatePage, const OUString&, rPage, void)
+{
+ if (rPage == "paper")
+ m_xPaperPage->update();
+}
+
+IMPL_LINK( RTSDialog, ClickButton, weld::Button&, rButton, void )
+{
+ if (&rButton == m_xOKButton.get())
+ {
+ // refresh the changed values
+ if (m_xPaperPage)
+ {
+ // orientation
+ m_aJobData.m_eOrientation = m_xPaperPage->getOrientation() == 0 ?
+ orientation::Portrait : orientation::Landscape;
+ // assume use of paper size from printer setup if the user
+ // got here via File > Printer Settings ...
+ if ( m_aJobData.meSetupMode == PrinterSetupMode::DocumentGlobal )
+ m_aJobData.m_bPapersizeFromSetup = true;
+ }
+ if( m_xDevicePage )
+ {
+ m_aJobData.m_nColorDepth = m_xDevicePage->getDepth();
+ m_aJobData.m_nColorDevice = m_xDevicePage->getColorDevice();
+ }
+ m_xDialog->response(RET_OK);
+ }
+ else if (&rButton == m_xCancelButton.get())
+ m_xDialog->response(RET_CANCEL);
+}
+
+/*
+ * RTSPaperPage
+ */
+
+RTSPaperPage::RTSPaperPage(weld::Widget* pPage, RTSDialog* pDialog)
+ : m_xBuilder(Application::CreateBuilder(pPage, "vcl/ui/printerpaperpage.ui"))
+ , m_pParent(pDialog)
+ , m_xContainer(m_xBuilder->weld_widget("PrinterPaperPage"))
+ , m_xCbFromSetup(m_xBuilder->weld_check_button("papersizefromsetup"))
+ , m_xPaperText(m_xBuilder->weld_label("paperft"))
+ , m_xPaperBox(m_xBuilder->weld_combo_box("paperlb"))
+ , m_xOrientText(m_xBuilder->weld_label("orientft"))
+ , m_xOrientBox(m_xBuilder->weld_combo_box("orientlb"))
+ , m_xDuplexText(m_xBuilder->weld_label("duplexft"))
+ , m_xDuplexBox(m_xBuilder->weld_combo_box("duplexlb"))
+ , m_xSlotText(m_xBuilder->weld_label("slotft"))
+ , m_xSlotBox(m_xBuilder->weld_combo_box("slotlb"))
+{
+ //PrinterPaperPage
+ m_xPaperBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
+ m_xOrientBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
+ m_xDuplexBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
+ m_xSlotBox->connect_changed( LINK( this, RTSPaperPage, SelectHdl ) );
+ m_xCbFromSetup->connect_toggled( LINK( this, RTSPaperPage, CheckBoxHdl ) );
+
+ update();
+}
+
+RTSPaperPage::~RTSPaperPage()
+{
+}
+
+void RTSPaperPage::update()
+{
+ const PPDKey* pKey = nullptr;
+
+ // orientation
+ m_xOrientBox->set_active(m_pParent->m_aJobData.m_eOrientation == orientation::Portrait ? 0 : 1);
+
+ // duplex
+ if( m_pParent->m_aJobData.m_pParser &&
+ (pKey = m_pParent->m_aJobData.m_pParser->getKey( "Duplex" )) )
+ {
+ m_pParent->insertAllPPDValues( *m_xDuplexBox, m_pParent->m_aJobData.m_pParser, pKey );
+ }
+ else
+ {
+ m_xDuplexText->set_sensitive( false );
+ m_xDuplexBox->set_sensitive( false );
+ }
+
+ // paper
+ if( m_pParent->m_aJobData.m_pParser &&
+ (pKey = m_pParent->m_aJobData.m_pParser->getKey( "PageSize" )) )
+ {
+ m_pParent->insertAllPPDValues( *m_xPaperBox, m_pParent->m_aJobData.m_pParser, pKey );
+ }
+ else
+ {
+ m_xPaperText->set_sensitive( false );
+ m_xPaperBox->set_sensitive( false );
+ }
+
+ // input slots
+ if( m_pParent->m_aJobData.m_pParser &&
+ (pKey = m_pParent->m_aJobData.m_pParser->getKey( "InputSlot" )) )
+ {
+ m_pParent->insertAllPPDValues( *m_xSlotBox, m_pParent->m_aJobData.m_pParser, pKey );
+ }
+ else
+ {
+ m_xSlotText->set_sensitive( false );
+ m_xSlotBox->set_sensitive( false );
+ }
+
+ if ( m_pParent->m_aJobData.meSetupMode != PrinterSetupMode::SingleJob )
+ return;
+
+ m_xCbFromSetup->show();
+
+ if ( m_pParent->m_aJobData.m_bPapersizeFromSetup )
+ m_xCbFromSetup->set_active(m_pParent->m_aJobData.m_bPapersizeFromSetup);
+ // disable those, unless user wants to use papersize from printer prefs
+ // as they have no influence on what's going to be printed anyway
+ else
+ {
+ m_xPaperText->set_sensitive( false );
+ m_xPaperBox->set_sensitive( false );
+ m_xOrientText->set_sensitive( false );
+ m_xOrientBox->set_sensitive( false );
+ }
+}
+
+IMPL_LINK( RTSPaperPage, SelectHdl, weld::ComboBox&, rBox, void )
+{
+ const PPDKey* pKey = nullptr;
+ if( &rBox == m_xPaperBox.get() )
+ {
+ if( m_pParent->m_aJobData.m_pParser )
+ pKey = m_pParent->m_aJobData.m_pParser->getKey( "PageSize" );
+ }
+ else if( &rBox == m_xDuplexBox.get() )
+ {
+ if( m_pParent->m_aJobData.m_pParser )
+ pKey = m_pParent->m_aJobData.m_pParser->getKey( "Duplex" );
+ }
+ else if( &rBox == m_xSlotBox.get() )
+ {
+ if( m_pParent->m_aJobData.m_pParser )
+ pKey = m_pParent->m_aJobData.m_pParser->getKey( "InputSlot" );
+ }
+ else if( &rBox == m_xOrientBox.get() )
+ {
+ m_pParent->m_aJobData.m_eOrientation = m_xOrientBox->get_active() == 0 ? orientation::Portrait : orientation::Landscape;
+ }
+ if( pKey )
+ {
+ PPDValue* pValue = weld::fromId<PPDValue*>(rBox.get_active_id());
+ m_pParent->m_aJobData.m_aContext.setValue( pKey, pValue );
+ update();
+ }
+
+ m_pParent->SetDataModified( true );
+}
+
+IMPL_LINK_NOARG(RTSPaperPage, CheckBoxHdl, weld::Toggleable&, void)
+{
+ bool bFromSetup = m_xCbFromSetup->get_active();
+ m_pParent->m_aJobData.m_bPapersizeFromSetup = bFromSetup;
+ m_xPaperText->set_sensitive(bFromSetup);
+ m_xPaperBox->set_sensitive(bFromSetup);
+ m_xOrientText->set_sensitive(bFromSetup);
+ m_xOrientBox->set_sensitive(bFromSetup);
+ m_pParent->SetDataModified(true);
+}
+
+/*
+ * RTSDevicePage
+ */
+RTSDevicePage::RTSDevicePage(weld::Widget* pPage, RTSDialog* pParent)
+ : m_xBuilder(Application::CreateBuilder(pPage, "vcl/ui/printerdevicepage.ui"))
+ , m_pCustomValue(nullptr)
+ , m_pParent(pParent)
+ , m_xContainer(m_xBuilder->weld_widget("PrinterDevicePage"))
+ , m_xPPDKeyBox(m_xBuilder->weld_tree_view("options"))
+ , m_xPPDValueBox(m_xBuilder->weld_tree_view("values"))
+ , m_xCustomEdit(m_xBuilder->weld_entry("custom"))
+ , m_xSpaceBox(m_xBuilder->weld_combo_box("colorspace"))
+ , m_xDepthBox(m_xBuilder->weld_combo_box("colordepth"))
+ , m_aReselectCustomIdle("RTSDevicePage m_aReselectCustomIdle")
+{
+ m_aReselectCustomIdle.SetInvokeHandler(LINK(this, RTSDevicePage, ImplHandleReselectHdl));
+
+ m_xPPDKeyBox->set_size_request(m_xPPDKeyBox->get_approximate_digit_width() * 32,
+ m_xPPDKeyBox->get_height_rows(12));
+
+ m_xCustomEdit->connect_changed(LINK(this, RTSDevicePage, ModifyHdl));
+
+ m_xPPDKeyBox->connect_changed( LINK( this, RTSDevicePage, SelectHdl ) );
+ m_xPPDValueBox->connect_changed( LINK( this, RTSDevicePage, SelectHdl ) );
+
+ m_xSpaceBox->connect_changed(LINK(this, RTSDevicePage, ComboChangedHdl));
+ m_xDepthBox->connect_changed(LINK(this, RTSDevicePage, ComboChangedHdl));
+
+ switch( m_pParent->m_aJobData.m_nColorDevice )
+ {
+ case 0:
+ m_xSpaceBox->set_active(0);
+ break;
+ case 1:
+ m_xSpaceBox->set_active(1);
+ break;
+ case -1:
+ m_xSpaceBox->set_active(2);
+ break;
+ }
+
+ if (m_pParent->m_aJobData.m_nColorDepth == 8)
+ m_xDepthBox->set_active(0);
+ else if (m_pParent->m_aJobData.m_nColorDepth == 24)
+ m_xDepthBox->set_active(1);
+
+ // fill ppd boxes
+ if( !m_pParent->m_aJobData.m_pParser )
+ return;
+
+ for( int i = 0; i < m_pParent->m_aJobData.m_pParser->getKeys(); i++ )
+ {
+ const PPDKey* pKey = m_pParent->m_aJobData.m_pParser->getKey( i );
+
+ // skip options already shown somewhere else
+ // also skip options from the "InstallableOptions" PPD group
+ // Options in that group define hardware features that are not
+ // job-specific and should better be handled in the system-wide
+ // printer configuration. Keyword is defined in PPD specification
+ // (version 4.3), section 5.4.
+ if( pKey->isUIKey() &&
+ pKey->getKey() != "PageSize" &&
+ pKey->getKey() != "InputSlot" &&
+ pKey->getKey() != "PageRegion" &&
+ pKey->getKey() != "Duplex" &&
+ pKey->getGroup() != "InstallableOptions")
+ {
+ OUString aEntry( m_pParent->m_aJobData.m_pParser->translateKey( pKey->getKey() ) );
+ m_xPPDKeyBox->append(weld::toId(pKey), aEntry);
+ }
+ }
+}
+
+RTSDevicePage::~RTSDevicePage()
+{
+}
+
+sal_uLong RTSDevicePage::getDepth() const
+{
+ sal_uInt16 nSelectPos = m_xDepthBox->get_active();
+ if (nSelectPos == 0)
+ return 8;
+ else
+ return 24;
+}
+
+sal_uLong RTSDevicePage::getColorDevice() const
+{
+ sal_uInt16 nSelectPos = m_xSpaceBox->get_active();
+ switch (nSelectPos)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ return -1;
+ }
+ return 0;
+}
+
+IMPL_LINK(RTSDevicePage, ModifyHdl, weld::Entry&, rEdit, void)
+{
+ if (m_pCustomValue)
+ {
+ // tdf#123734 Custom PPD option values are a CUPS extension to PPDs and the user-set value
+ // needs to be prefixed with "Custom." in order to be processed properly
+ m_pCustomValue->m_aCustomOption = "Custom." + rEdit.get_text();
+ m_pCustomValue->m_bCustomOptionSetViaApp = true;
+ }
+}
+
+IMPL_LINK( RTSDevicePage, SelectHdl, weld::TreeView&, rBox, void )
+{
+ if (&rBox == m_xPPDKeyBox.get())
+ {
+ const PPDKey* pKey = weld::fromId<PPDKey*>(m_xPPDKeyBox->get_selected_id());
+ FillValueBox( pKey );
+ }
+ else if (&rBox == m_xPPDValueBox.get())
+ {
+ const PPDKey* pKey = weld::fromId<PPDKey*>(m_xPPDKeyBox->get_selected_id());
+ const PPDValue* pValue = weld::fromId<PPDValue*>(m_xPPDValueBox->get_selected_id());
+ if (pKey && pValue)
+ {
+ m_pParent->m_aJobData.m_aContext.setValue( pKey, pValue );
+ ValueBoxChanged(pKey);
+ }
+ }
+ m_pParent->SetDataModified( true );
+}
+
+IMPL_LINK_NOARG( RTSDevicePage, ComboChangedHdl, weld::ComboBox&, void )
+{
+ m_pParent->SetDataModified( true );
+}
+
+void RTSDevicePage::FillValueBox( const PPDKey* pKey )
+{
+ m_xPPDValueBox->clear();
+ m_xCustomEdit->hide();
+
+ if( ! pKey )
+ return;
+
+ const PPDValue* pValue = nullptr;
+ for( int i = 0; i < pKey->countValues(); i++ )
+ {
+ pValue = pKey->getValue( i );
+ if( m_pParent->m_aJobData.m_aContext.checkConstraints( pKey, pValue ) &&
+ m_pParent->m_aJobData.m_pParser )
+ {
+ OUString aEntry;
+ if (pValue->m_bCustomOption)
+ aEntry = VclResId(SV_PRINT_CUSTOM_TXT);
+ else
+ aEntry = m_pParent->m_aJobData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption);
+ m_xPPDValueBox->append(weld::toId(pValue), aEntry);
+ }
+ }
+ pValue = m_pParent->m_aJobData.m_aContext.getValue( pKey );
+ m_xPPDValueBox->select_id(weld::toId(pValue));
+
+ ValueBoxChanged(pKey);
+}
+
+IMPL_LINK_NOARG(RTSDevicePage, ImplHandleReselectHdl, Timer*, void)
+{
+ //in case selected entry is now not visible select it again to scroll it into view
+ m_xPPDValueBox->select(m_xPPDValueBox->get_selected_index());
+}
+
+void RTSDevicePage::ValueBoxChanged( const PPDKey* pKey )
+{
+ const PPDValue* pValue = m_pParent->m_aJobData.m_aContext.getValue(pKey);
+ if (pValue->m_bCustomOption)
+ {
+ m_pCustomValue = pValue;
+ m_pParent->m_aJobData.m_aContext.setValue(pKey, pValue);
+ // don't show the "Custom." prefix in the UI, s.a. comment in ModifyHdl
+ m_xCustomEdit->set_text(m_pCustomValue->m_aCustomOption.replaceFirst("Custom.", ""));
+ m_xCustomEdit->show();
+ m_aReselectCustomIdle.Start();
+ }
+ else
+ m_xCustomEdit->hide();
+}
+
+int SetupPrinterDriver(weld::Window* pParent, ::psp::PrinterInfo& rJobData)
+{
+ int nRet = 0;
+ RTSDialog aDialog(rJobData, pParent);
+
+ // return 0 if cancel was pressed or if the data
+ // weren't modified, 1 otherwise
+ if (aDialog.run() != RET_CANCEL)
+ {
+ rJobData = aDialog.getSetup();
+ nRet = aDialog.GetDataModified() ? 1 : 0;
+ }
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/prtsetup.hxx b/vcl/unx/generic/print/prtsetup.hxx
new file mode 100644
index 0000000000..1a32eb3318
--- /dev/null
+++ b/vcl/unx/generic/print/prtsetup.hxx
@@ -0,0 +1,135 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <vcl/weld.hxx>
+#include <ppdparser.hxx>
+#include <printerinfomanager.hxx>
+
+class RTSPaperPage;
+class RTSDevicePage;
+
+class RTSDialog : public weld::GenericDialogController
+{
+ friend class RTSPaperPage;
+ friend class RTSDevicePage;
+
+ ::psp::PrinterInfo m_aJobData;
+
+ bool m_bDataModified;
+
+ // controls
+ std::unique_ptr<weld::Notebook> m_xTabControl;
+ std::unique_ptr<weld::Button> m_xOKButton;
+ std::unique_ptr<weld::Button> m_xCancelButton;
+
+ // pages
+ std::unique_ptr<RTSPaperPage> m_xPaperPage;
+ std::unique_ptr<RTSDevicePage> m_xDevicePage;
+
+ DECL_LINK(ActivatePage, const OUString&, void);
+ DECL_LINK(ClickButton, weld::Button&, void);
+
+ // helper functions
+ void insertAllPPDValues(weld::ComboBox&, const psp::PPDParser*, const psp::PPDKey*);
+
+public:
+ RTSDialog(const ::psp::PrinterInfo& rJobData, weld::Window* pParent);
+ virtual ~RTSDialog() override;
+
+ const ::psp::PrinterInfo& getSetup() const { return m_aJobData; }
+
+ void SetDataModified(bool bModified) { m_bDataModified = bModified; }
+ bool GetDataModified() const { return m_bDataModified; }
+};
+
+class RTSPaperPage
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+
+ RTSDialog* m_pParent;
+
+ std::unique_ptr<weld::Widget> m_xContainer;
+
+ std::unique_ptr<weld::CheckButton> m_xCbFromSetup;
+
+ std::unique_ptr<weld::Label> m_xPaperText;
+ std::unique_ptr<weld::ComboBox> m_xPaperBox;
+
+ std::unique_ptr<weld::Label> m_xOrientText;
+ std::unique_ptr<weld::ComboBox> m_xOrientBox;
+
+ std::unique_ptr<weld::Label> m_xDuplexText;
+ std::unique_ptr<weld::ComboBox> m_xDuplexBox;
+
+ std::unique_ptr<weld::Label> m_xSlotText;
+ std::unique_ptr<weld::ComboBox> m_xSlotBox;
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+
+public:
+ RTSPaperPage(weld::Widget* pPage, RTSDialog* pDialog);
+ ~RTSPaperPage();
+
+ void update();
+
+ sal_Int32 getOrientation() const { return m_xOrientBox->get_active(); }
+};
+
+class RTSDevicePage
+{
+private:
+ std::unique_ptr<weld::Builder> m_xBuilder;
+
+ const psp::PPDValue* m_pCustomValue;
+ RTSDialog* m_pParent;
+
+ std::unique_ptr<weld::Widget> m_xContainer;
+ std::unique_ptr<weld::TreeView> m_xPPDKeyBox;
+ std::unique_ptr<weld::TreeView> m_xPPDValueBox;
+ std::unique_ptr<weld::Entry> m_xCustomEdit;
+
+ std::unique_ptr<weld::ComboBox> m_xSpaceBox;
+ std::unique_ptr<weld::ComboBox> m_xDepthBox;
+
+ void FillValueBox(const ::psp::PPDKey*);
+ void ValueBoxChanged(const ::psp::PPDKey*);
+
+ Idle m_aReselectCustomIdle;
+
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+ DECL_LINK(ModifyHdl, weld::Entry&, void);
+ DECL_LINK(ComboChangedHdl, weld::ComboBox&, void);
+ DECL_LINK(ImplHandleReselectHdl, Timer*, void);
+
+public:
+ RTSDevicePage(weld::Widget* pPage, RTSDialog* pDialog);
+ ~RTSDevicePage();
+
+ sal_uLong getDepth() const;
+ sal_uLong getColorDevice() const;
+};
+
+int SetupPrinterDriver(weld::Window* pParent, ::psp::PrinterInfo& rJobData);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/print/psheader.ps b/vcl/unx/generic/print/psheader.ps
new file mode 100644
index 0000000000..49f0f51011
--- /dev/null
+++ b/vcl/unx/generic/print/psheader.ps
@@ -0,0 +1,363 @@
+%
+% 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 is an "unobsfucated version of postscript header" in printerjob.cxx. It
+% was probably kept separate for the comments, but it is not used in itself
+% and probably was not kept in sync with the actual header.
+
+%
+%
+% readpath
+%
+% The intention of readpath is to save disk space since the vcl clip region routines
+% produce a huge amount of lineto/moveto commands
+%
+% The principal idea is to maintain the current point on stack and to provide only deltas
+% in the command. These deltas are added to the current point. The new point is used for
+% the lineto and moveto command and saved on stack for the next command.
+%
+% pathdict implements binary/hex representation of lineto and moveto commands.
+% The command consists of a 1byte opcode to switch between lineto and moveto and the size
+% of the following delta-x and delta-y values. The opcode is read with /rcmd, the two
+% coordinates are read with /rhex. The whole command is executed with /xcmd
+%
+%
+
+/pathdict dup 8 dict def load
+begin
+
+ % the command is of the bit format cxxyy
+ % with c=0 meaning lineto
+ % c=1 meaning moveto
+ % xx is a 2bit value for the number of bytes for x position
+ % yy is the same for y, values are off by one: 00 means 1; 11 means 4 !
+ % the command has been added to 'A' to be always in the ascii character
+ % range. the command is followed by 2*xx + 2*yy hexchars.
+ % '~' denotes the special case of EOD
+ /rcmd {
+ {
+ currentfile 1 string readstring % s bool
+ pop % s
+ 0 get % s[0]
+ % --- check whether s[0] is CR, LF ...
+ dup 32 gt % s > ' ' ? then read on
+ { exit }
+ { pop }
+ ifelse
+ }
+ loop
+
+ dup 126 eq { pop exit } if % -- Exit loop if cmd is '~'
+ 65 sub % cmd=s[0]-'A'
+ % -- Separate yy bits
+ dup 16#3 and 1 add % cmd yy
+ % -- Separate xx bits
+ exch % yy cmd
+ dup 16#C and -2 bitshift
+ 16#3 and 1 add exch % yy xx cmd
+ % -- Separate command bit
+ 16#10 and 16#10 eq % yy xx bool
+ 3 1 roll exch % bool xx yy
+ } def
+
+ % length rhex -- reads a signed hex value of given length
+ % the left most bit of char 0 is considered as the sign (0 means '+', 1 means '-')
+ % the rest of the bits is considered to be the abs value. Please note that this
+ % does not match the C binary representation of integers
+ /rhex {
+ dup 1 sub exch % l-1 l
+ currentfile exch string readhexstring % l-1 substring[l] bool
+ pop
+ dup 0 get dup % l-1 s s[0] s[0]
+ % -- Extract the sign
+ 16#80 and 16#80 eq dup % l-1 s s[0] sign=- sign=-
+ % -- Mask out the sign bit and put value back
+ 3 1 roll % l-1 s sign=- s[0] sign=-
+ { 16#7f and } if % l-1 s sign=- +s[0]
+ 2 index 0 % l-1 s sign=- +s[0] s 0
+ 3 -1 roll put % l-1 s sign=- s 0 +s[0]
+ % -- Read loop: add to prev sum, mul with 256
+ 3 1 roll 0 % sign=- l-1 s Sum=0
+ 0 1 5 -1 roll % sign=- s Sum=0 0 1 l-1
+ { % sign=- s Sum idx
+ 2 index exch % sign=- s Sum s idx
+ get % sign=- s Sum s[idx]
+ add 256 mul % sign=- s Sum=(s[idx]+Sum)*256
+ }
+ for
+ % -- mul was once too often, weave in the sign
+ 256 div % sign=- s Sum/256
+ exch pop % sign=- Sum/256
+ exch { neg } if % (sign=- ? -Sum : Sum)
+ } def
+
+ % execute a single command, the former x and y position is already on stack
+ % only offsets are read from cmdstring
+ /xcmd { % x y
+ rcmd % x y bool wx wy
+ exch rhex % x y bool wy Dx
+ exch rhex % x y bool Dx Dy
+ exch 5 -1 roll % y bool Dy Dx x
+ add exch % y bool X Dy
+ 4 -1 roll add % bool X Y
+ 1 index 1 index % bool X Y X Y
+ 5 -1 roll % X Y X Y bool
+ { moveto }
+ { lineto }
+ ifelse % X Y
+ } def
+end
+
+/readpath
+{
+ 0 0 % push initial-x initial-y
+ pathdict begin
+ { xcmd } loop
+ end
+ pop pop % pop final-x final-y
+} def
+
+%
+%
+% if languagelevel is not in the systemdict then its level 1 interpreter:
+% provide compatibility routines
+%
+%
+
+systemdict /languagelevel known not
+{
+ % string numarray xxshow -
+ % does only work for single byte fonts
+ /xshow {
+ exch dup % a s s
+ length 0 1 % a s l(s) 1 1
+ 3 -1 roll 1 sub % a s 0 1 l(s)-1
+ { % a s idx
+ dup % a s idx idx
+ % -- extract the delta offset
+ 3 index exch get % a s idx a[idx]
+ % -- extract the character
+ exch % a s a[idx] idx
+ 2 index exch get % a s a[idx] s[idx]
+ % -- create a tmp string for show
+ 1 string dup 0 % a s a[idx] s[idx] s1 s1 0
+ 4 -1 roll % a s a[idx] s1 s1 0 s[idx]
+ put % a s a[idx] s1
+ % -- store the current point
+ currentpoint 3 -1 roll % a s a[idx] x y s1
+ % -- draw the character
+ show % a s a[idx] x y
+ % -- move to the offset
+ moveto 0 rmoveto % a s
+ }
+ for
+ pop pop % -
+ } def
+
+ % x y width height rectfill
+ % x y width height rectshow
+ % in contrast to the languagelevel 2 operator
+ % they use and change the currentpath
+ /rectangle {
+ 4 -2 roll % width height x y
+ moveto % width height
+ 1 index 0 rlineto % width height % rmoveto(width, 0)
+ 0 exch rlineto % width % rmoveto(0, height)
+ neg 0 rlineto % - % rmoveto(-width, 0)
+ closepath
+ } def
+
+ /rectfill { rectangle fill } def
+ /rectstroke { rectangle stroke } def
+}
+if
+
+% -- small test program
+% 75 75 moveto /Times-Roman findfont 12 scalefont setfont
+% <292a2b2c2d2e2f30313233343536373839>
+% [5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 5] xshow <21>[0] xshow
+% showpage
+
+%
+%
+% shortcuts for image header with compression
+%
+%
+
+/psp_lzwfilter {
+ currentfile /ASCII85Decode filter /LZWDecode filter
+} def
+/psp_ascii85filter {
+ currentfile /ASCII85Decode filter
+} def
+/psp_lzwstring {
+ psp_lzwfilter 1024 string readstring
+} def
+/psp_ascii85string {
+ psp_ascii85filter 1024 string readstring
+} def
+/psp_imagedict {
+ /psp_bitspercomponent {
+ 3 eq
+ { 1 }
+ { 8 }
+ ifelse
+ } def
+ /psp_decodearray {
+ [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get
+ } def
+
+ 7 dict dup
+ /ImageType 1 put dup
+ /Width 7 -1 roll put dup
+ /Height 5 index put dup
+ /BitsPerComponent 4 index
+ psp_bitspercomponent put dup
+ /Decode 5 -1 roll
+ psp_decodearray put dup
+ /ImageMatrix [1 0 0 1 0 0] dup
+ 5 8 -1 roll put put dup
+ /DataSource 4 -1 roll
+ 1 eq
+ { psp_lzwfilter }
+ { psp_ascii85filter }
+ ifelse put
+} def
+
+
+%
+%
+% font encoding and reencoding
+%
+%
+
+/ISO1252Encoding [
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
+ /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash
+ /zero /one /two /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less /equal /greater /question
+ /at /A /B /C /D /E /F /G
+ /H /I /J /K /L /M /N /O
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+ /grave /a /b /c /d /e /f /g
+ /h /i /j /k /l /m /n /o
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde /unused
+ /Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused
+ /unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis
+ /space /exclamdown /cent /sterling /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
+ /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
+ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
+ /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
+ /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
+ /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis
+] def
+
+% /fontname /encoding psp_findfont
+/psp_findfont {
+ exch dup % encoding fontname fontname
+ findfont % encoding fontname
+ dup length dict
+ begin
+ {
+ 1 index /FID ne
+ { def }
+ { pop pop }
+ ifelse
+ } forall
+ /Encoding 3 -1 roll def
+ currentdict
+ end
+ /psp_reencodedfont exch definefont
+} def
+
+% bshow shows a text in artificial bold
+% this is achieved by first showing the text
+% then stroking its outline over it with
+% the linewidth set to the second parameter
+% usage: (string) num bshow
+
+/bshow {
+ currentlinewidth % save current linewidth
+ 3 1 roll % move it to the last stack position
+ currentpoint % save the current point
+ 3 index % copy the string to show
+ show % show it
+ moveto % move to the original coordinates again
+ setlinewidth % set the linewidth
+ false charpath % create the outline path of the shown string
+ stroke % and stroke it
+ setlinewidth % reset the stored linewidth
+} def
+
+% bxshow shows a text with a delta array in artificial bold
+% that is it does what bshow does for show
+% usage: (string) [deltaarray] num bxshow
+
+/bxshow {
+ currentlinewidth % save linewidth
+ 4 1 roll % move it to the last stack position
+ setlinewidth % set the new linewidth
+ exch % exchange string and delta array
+ dup
+ length % get length of string
+ 1 sub % prepare parameters for {} for
+ 0 1
+ 3 -1 roll
+ {
+ 1 string % create a string object length 1
+ 2 index % get the text
+ 2 index % get charpos (for index variable)
+ get % have char value at charpos
+ 1 index % prepare string for put
+ exch
+ 0
+ exch
+ put % put into string of length 1
+ dup % duplicate the it
+ currentpoint % save current position
+ 3 -1 roll % prepare show
+ show % show the character
+ moveto % move back to beginning
+ currentpoint % save current position
+ 3 -1 roll % prepare outline path of character
+ false charpath
+ stroke % stroke it
+ moveto % move back
+ % now move to next point
+ 2 index % get advance array
+ exch % get charpos
+ get % get advance element
+ 0 rmoveto % advance current position
+ } for
+ pop pop % remove string and delta array
+ setlinewidth % restore linewidth
+} def