diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/unx/generic/print | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/unx/generic/print')
-rw-r--r-- | vcl/unx/generic/print/genprnpsp.cxx | 1097 | ||||
-rw-r--r-- | vcl/unx/generic/print/genpspgraphics.cxx | 195 | ||||
-rw-r--r-- | vcl/unx/generic/print/prtsetup.cxx | 465 | ||||
-rw-r--r-- | vcl/unx/generic/print/prtsetup.hxx | 135 | ||||
-rw-r--r-- | vcl/unx/generic/print/psheader.ps | 363 |
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 |