/* -*- 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 aunderlying 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. */ // For spawning PDF and FAX generation #if defined( UNX ) # include # include # include #endif #include "rtl/ustring.hxx" #include "vcl/button.hxx" #include "vcl/dialog.hxx" #include "vcl/edit.hxx" #include "vcl/fixed.hxx" #include "vcl/svapp.hxx" #include "vcl/print.hxx" #include "vcl/pdfwriter.hxx" #include "vcl/printerinfomanager.hxx" #include "vcl/settings.hxx" #include "svids.hrc" #include "saldatabasic.hxx" #include "generic/genprn.h" #include "generic/geninst.h" #include "generic/genpspgraphics.h" #include "jobset.h" #include "print.h" #include "prtsetup.hxx" #include "salptype.hxx" #include using namespace psp; using namespace com::sun::star; /* * static helpers */ static OUString getPdfDir( const PrinterInfo& rInfo ) { OUString aDir; sal_Int32 nIndex = 0; while( nIndex != -1 ) { OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); if( aToken.startsWith( "pdf=" ) ) { sal_Int32 nPos = 0; aDir = aToken.getToken( 1, '=', nPos ); if( aDir.isEmpty() && getenv( "HOME" ) ) aDir = OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() ); break; } } return aDir; } namespace { class QueryString : public ModalDialog { private: OKButton* m_pOKButton; FixedText* m_pFixedText; Edit* m_pEdit; OUString& m_rReturnValue; DECL_LINK( ClickBtnHdl, Button* ); public: // parent window, Query text, initial value QueryString(vcl::Window*, OUString &, OUString &); }; /* * QueryString */ QueryString::QueryString(vcl::Window* pParent, OUString& rQuery, OUString& rRet) : ModalDialog(pParent, "QueryDialog", "vcl/ui/querydialog.ui" ) , m_rReturnValue( rRet ) { get(m_pOKButton, "ok"); get(m_pFixedText, "label"); get(m_pEdit, "entry"); m_pOKButton->SetClickHdl(LINK(this, QueryString, ClickBtnHdl)); m_pFixedText->SetText(rQuery); m_pEdit->SetText(m_rReturnValue); SetText(rQuery); } IMPL_LINK( QueryString, ClickBtnHdl, Button*, pButton ) { if (pButton == m_pOKButton) { m_rReturnValue = m_pEdit->GetText(); EndDialog( 1 ); } else EndDialog(0); return 0; } int QueryFaxNumber(OUString& rNumber) { OUString aTmpString(VclResId(SV_PRINT_QUERYFAXNUMBER_TXT)); QueryString aQuery(NULL, aTmpString, rNumber); return aQuery.Execute(); } } inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); } inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); } static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData ) { pJobSetup->meOrientation = (Orientation)(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->mePaperFormat = PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 )); pJobSetup->mnPaperWidth = 0; pJobSetup->mnPaperHeight = 0; if( pJobSetup->mePaperFormat == PAPER_USER ) { // transform to 100dth mm width = PtTo10Mu( width ); height = PtTo10Mu( height ); if( rData.m_eOrientation == psp::orientation::Portrait ) { pJobSetup->mnPaperWidth = width; pJobSetup->mnPaperHeight= height; } else { pJobSetup->mnPaperWidth = height; pJobSetup->mnPaperHeight= width; } } // copy input slot const PPDKey* pKey = NULL; const PPDValue* pValue = NULL; pJobSetup->mnPaperBin = 0; if( rData.m_pParser ) pKey = rData.m_pParser->getKey( OUString("InputSlot") ); if( pKey ) pValue = rData.m_aContext.getValue( pKey ); if( pKey && pValue ) { for( pJobSetup->mnPaperBin = 0; pValue != pKey->getValue( pJobSetup->mnPaperBin ) && pJobSetup->mnPaperBin < pKey->countValues(); pJobSetup->mnPaperBin++ ) ; if( pJobSetup->mnPaperBin >= pKey->countValues() ) pJobSetup->mnPaperBin = 0; } // copy duplex pKey = NULL; pValue = NULL; pJobSetup->meDuplexMode = DUPLEX_UNKNOWN; if( rData.m_pParser ) pKey = rData.m_pParser->getKey( OUString("Duplex") ); if( pKey ) pValue = rData.m_aContext.getValue( pKey ); if( pKey && pValue ) { if( pValue->m_aOption.equalsIgnoreAsciiCase( "None" ) || pValue->m_aOption.startsWithIgnoreAsciiCase( "Simplex" ) ) { pJobSetup->meDuplexMode = DUPLEX_OFF; } else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexNoTumble" ) ) { pJobSetup->meDuplexMode = DUPLEX_LONGEDGE; } else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexTumble" ) ) { pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE; } } // copy the whole context if( pJobSetup->mpDriverData ) rtl_freeMemory( pJobSetup->mpDriverData ); int nBytes; void* pBuffer = NULL; if( rData.getStreamBuffer( pBuffer, nBytes ) ) { pJobSetup->mnDriverDataLen = nBytes; pJobSetup->mpDriverData = (sal_uInt8*)pBuffer; } else { pJobSetup->mnDriverDataLen = 0; pJobSetup->mpDriverData = NULL; } } // Needs a cleaner abstraction ... #if defined( UNX ) static bool passFileToCommandLine( const OUString& rFilename, const OUString& rCommandLine, bool bRemoveFile = true ) { bool bSuccess = false; rtl_TextEncoding aEncoding = osl_getThreadTextEncoding(); OString aCmdLine(OUStringToOString(rCommandLine, aEncoding)); OString aFilename(OUStringToOString(rFilename, aEncoding)); bool bPipe = aCmdLine.indexOf( "(TMP)" ) != -1 ? false : true; // setup command line for exec if( ! bPipe ) aCmdLine = aCmdLine.replaceAll(OString("(TMP)"), aFilename); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "%s commandline: \"%s\"\n", bPipe ? "piping to" : "executing", aCmdLine.getStr() ); struct stat aStat; if( stat( aFilename.getStr(), &aStat ) ) fprintf( stderr, "stat( %s ) failed\n", aFilename.getStr() ); fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.getStr(), (long)aStat.st_mode ); #endif const char* argv[4]; if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) ) argv[ 0 ] = "/bin/sh"; argv[ 1 ] = "-c"; argv[ 2 ] = aCmdLine.getStr(); argv[ 3 ] = 0; bool bHavePipes = false; int pid, fd[2]; if( bPipe ) bHavePipes = pipe( fd ) ? false : true; if( ( pid = fork() ) > 0 ) { if( bPipe && bHavePipes ) { close( fd[0] ); char aBuffer[ 2048 ]; FILE* fp = fopen( aFilename.getStr(), "r" ); while (fp && !feof(fp)) { size_t nBytesRead = fread(aBuffer, 1, sizeof( aBuffer ), fp); if (nBytesRead ) { size_t nBytesWritten = write(fd[1], aBuffer, nBytesRead); OSL_ENSURE(nBytesWritten == nBytesRead, "short write"); if (nBytesWritten != nBytesRead) break; } } fclose( fp ); close( fd[ 1 ] ); } int status = 0; if(waitpid( pid, &status, 0 ) != -1) { if( ! status ) bSuccess = true; } } else if( ! pid ) { if( bPipe && bHavePipes ) { close( fd[1] ); if( fd[0] != STDIN_FILENO ) // not probable, but who knows :) dup2( fd[0], STDIN_FILENO ); } execv( argv[0], const_cast(argv) ); fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.getStr() ); _exit( 1 ); } else fprintf( stderr, "failed to fork\n" ); // clean up the mess if( bRemoveFile ) unlink( aFilename.getStr() ); return bSuccess; } #endif static std::vector getFaxNumbers() { std::vector aFaxNumbers; OUString aNewNr; if (QueryFaxNumber(aNewNr)) { sal_Int32 nIndex = 0; do { OUString sToken = aNewNr.getToken( 0, ';', nIndex ); aFaxNumbers.push_back(sToken); } while (nIndex >= 0); } return aFaxNumbers; } static bool createPdf( const OUString& rToFile, const OUString& rFromFile, const OUString& rCommandLine ) { #if defined( UNX ) OUString aCommandLine( rCommandLine.replaceAll("(OUTFILE)", rToFile)); return passFileToCommandLine( rFromFile, aCommandLine ); #else (void)rToFile; (void)rFromFile; (void)rCommandLine; return false; #endif } /* * SalInstance */ void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter *pPrinter, SalPrinterQueueInfo* pQueueInfo, ImplJobSetup* pJobSetup) { if( pJobSetup ) { PrinterInfoManager& rManager( PrinterInfoManager::get() ); PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) ); pPrinter->m_aJobData = aInfo; pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData ); if( pJobSetup->mpDriverData ) JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo ); pJobSetup->mnSystem = JOBSETUP_SYSTEM_UNIX; pJobSetup->maPrinterName = pQueueInfo->maPrinterName; pJobSetup->maDriver = 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; } SalPrinter* SalGenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter ) { mbPrinterInit = true; // create and initialize SalPrinter PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter ); pPrinter->m_aJobData = static_cast(pInfoPrinter)->m_aJobData; return pPrinter; } void SalGenericInstance::DestroyPrinter( SalPrinter* pPrinter ) { delete 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::list< OUString > aPrinters; rManager.listPrinters( aPrinters ); for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it ) { const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) ); // Neuen Eintrag anlegen SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo; pInfo->maPrinterName = *it; pInfo->maDriver = rInfo.m_aDriverName; pInfo->maLocation = rInfo.m_aLocation; pInfo->maComment = rInfo.m_aComment; pInfo->mpSysData = NULL; sal_Int32 nIndex = 0; while( nIndex != -1 ) { OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); if( aToken.match( "pdf=" ) ) { pInfo->maLocation = getPdfDir( rInfo ); break; } } pList->Add( pInfo ); } } void SalGenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo ) { delete pInfo; } void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo* ) { mbPrinterInit = true; } OUString SalGenericInstance::GetDefaultPrinter() { mbPrinterInit = true; PrinterInfoManager& rManager( PrinterInfoManager::get() ); return rManager.getDefaultPrinter(); } PspSalInfoPrinter::PspSalInfoPrinter() : m_pGraphics( NULL ) { } PspSalInfoPrinter::~PspSalInfoPrinter() { if( m_pGraphics ) { delete m_pGraphics; m_pGraphics = NULL; } } void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* ) { m_aPaperFormats.clear(); m_bPapersInit = true; if( m_aJobData.m_pParser ) { const PPDKey* pKey = m_aJobData.m_pParser->getKey( OUString("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 = NULL; if( ! m_pGraphics ) { m_pGraphics = GetGenericInstance()->CreatePrintGraphics(); m_pGraphics->Init(&m_aJobData, &m_aPrinterGfx, this); pRet = m_pGraphics; } return pRet; } void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics ) { if( pGraphics == m_pGraphics ) { delete pGraphics; m_pGraphics = NULL; } return; } bool PspSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup ) { if( ! pFrame || ! pJobSetup ) return false; PrinterInfoManager& rManager = PrinterInfoManager::get(); PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) ); if ( pJobSetup->mpDriverData ) { SetData( ~0, pJobSetup ); JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo ); } if (SetupPrinterDriver(aInfo)) { aInfo.resolveDefaultBackend(); rtl_freeMemory( pJobSetup->mpDriverData ); pJobSetup->mpDriverData = NULL; int nBytes; void* pBuffer = NULL; aInfo.getStreamBuffer( pBuffer, nBytes ); pJobSetup->mnDriverDataLen = nBytes; pJobSetup->mpDriverData = (sal_uInt8*)pBuffer; // copy everything to job setup copyJobDataToJobSetup( pJobSetup, aInfo ); JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); return true; } return false; } // This function gets the driver data and puts it into pJobSetup // If pJobSetup->mpDriverData is NOT NULL, then the independent // data should be merged into the driver data // If pJobSetup->mpDriverData IS NULL, then the driver defaults // should be merged into the independent data bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup ) { if( pJobSetup->mpDriverData ) return SetData( ~0, pJobSetup ); copyJobDataToJobSetup( pJobSetup, m_aJobData ); return true; } // This function merges the independ driver data // and sets the new independ data in pJobSetup // Only the data must be changed, where the bit // in nGetDataFlags is set bool PspSalInfoPrinter::SetData( sal_uLong nSetDataFlags, ImplJobSetup* pJobSetup ) { JobData aData; JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); if( aData.m_pParser ) { const PPDKey* pKey; const PPDValue* pValue; // merge papersize if necessary if( nSetDataFlags & SAL_JOBSET_PAPERSIZE ) { OUString aPaper; if( pJobSetup->mePaperFormat == PAPER_USER ) aPaper = aData.m_pParser->matchPaper( TenMuToPt( pJobSetup->mnPaperWidth ), TenMuToPt( pJobSetup->mnPaperHeight ) ); else aPaper = OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1); pKey = aData.m_pParser->getKey( OUString("PageSize") ); pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL; // 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->mePaperFormat != PAPER_USER ) { PaperInfo aInfo( pJobSetup->mePaperFormat ); aPaper = aData.m_pParser->matchPaper( TenMuToPt( aInfo.getWidth() ), TenMuToPt( aInfo.getHeight() ) ); pValue = pKey->getValueCaseInsensitive( aPaper ); } if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) ) return false; } // merge paperbin if necessary if( nSetDataFlags & SAL_JOBSET_PAPERBIN ) { pKey = aData.m_pParser->getKey( OUString("InputSlot") ); if( pKey ) { int nPaperBin = pJobSetup->mnPaperBin; if( nPaperBin >= pKey->countValues() ) pValue = pKey->getDefaultValue(); else pValue = pKey->getValue( pJobSetup->mnPaperBin ); // 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 orientation if necessary if( nSetDataFlags & SAL_JOBSET_ORIENTATION ) aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait; // merge duplex if necessary if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE ) { pKey = aData.m_pParser->getKey( OUString("Duplex") ); if( pKey ) { pValue = NULL; switch( pJobSetup->meDuplexMode ) { case DUPLEX_OFF: pValue = pKey->getValue( OUString("None") ); if( pValue == NULL ) pValue = pKey->getValue( OUString("SimplexNoTumble") ); break; case DUPLEX_SHORTEDGE: pValue = pKey->getValue( OUString("DuplexTumble") ); break; case DUPLEX_LONGEDGE: pValue = pKey->getValue( OUString("DuplexNoTumble") ); break; case DUPLEX_UNKNOWN: default: pValue = 0; break; } if( ! pValue ) pValue = pKey->getDefaultValue(); aData.m_aContext.setValue( pKey, pValue ); } } m_aJobData = aData; copyJobDataToJobSetup( pJobSetup, aData ); return true; } return false; } void PspSalInfoPrinter::GetPageInfo( const ImplJobSetup* pJobSetup, long& rOutWidth, long& rOutHeight, long& rPageOffX, long& rPageOffY, long& rPageWidth, long& rPageHeight ) { if( ! pJobSetup ) return; JobData aData; JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); // get the selected page size if( aData.m_pParser ) { 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 ); } rPageWidth = width * nDPI / 72; rPageHeight = height * nDPI / 72; rPageOffX = left * nDPI / 72; rPageOffY = top * nDPI / 72; rOutWidth = ( width - left - right ) * nDPI / 72; rOutHeight = ( height - top - bottom ) * nDPI / 72; } } sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup ) { if( ! pJobSetup ) return 0; JobData aData; JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( OUString("InputSlot") ): NULL; return pKey ? pKey->countValues() : 0; } OUString PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin ) { JobData aData; JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); OUString aRet; if( aData.m_pParser ) { const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( OUString("InputSlot") ): NULL; if( ! pKey || nPaperBin >= (sal_uLong)pKey->countValues() ) aRet = aData.m_pParser->getDefaultInputSlot(); else { const PPDValue* pValue = pKey->getValue( nPaperBin ); if( pValue ) aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption ); } } return aRet; } sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType ) { switch( nType ) { case PRINTER_CAPABILITIES_SUPPORTDIALOG: return 1; case PRINTER_CAPABILITIES_COPIES: return 0xffff; case PRINTER_CAPABILITIES_COLLATECOPIES: { // PPDs don't mention the number of possible collated copies. // so let's guess as many as we want ? return 0xffff; } case PRINTER_CAPABILITIES_SETORIENTATION: return 1; case PRINTER_CAPABILITIES_SETDUPLEX: return 1; case PRINTER_CAPABILITIES_SETPAPERBIN: return 1; case PRINTER_CAPABILITIES_SETPAPERSIZE: return 1; case PRINTER_CAPABILITIES_SETPAPER: return 0; case PRINTER_CAPABILITIES_FAX: { // see if the PPD contains the fax4CUPS "Dial" option and that it's not set // to "manually" JobData aData = PrinterInfoManager::get().getPrinterInfo(pJobSetup->maPrinterName); if( pJobSetup->mpDriverData ) JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey(OUString("Dial")) : NULL; const PPDValue* pValue = pKey ? aData.m_aContext.getValue(pKey) : NULL; if (pValue && !pValue->m_aOption.equalsIgnoreAsciiCase("Manually")) return 1; return 0; } case PRINTER_CAPABILITIES_PDF: if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "pdf" ) ) return 1; else { // see if the PPD contains a value to set PDF device JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName ); if( pJobSetup->mpDriverData ) JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); return aData.m_nPDFDevice > 0 ? 1 : 0; } case PRINTER_CAPABILITIES_EXTERNALDIALOG: return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "external_dialog" ) ? 1 : 0; case PRINTER_CAPABILITIES_USEPULLMODEL: { // see if the PPD contains a value to set PDF device JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName ); if( pJobSetup->mpDriverData ) JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData ); return aData.m_nPDFDevice > 0 ? 1 : 0; } default: break; } return 0; } /* * SalPrinter */ PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter ) : m_bPdf( false ), m_bIsPDFWriterJob( false ), m_pGraphics( NULL ), m_nCopies( 1 ), m_bCollate( false ), m_pInfoPrinter( pInfoPrinter ) { } PspSalPrinter::~PspSalPrinter() { } static OUString getTmpName() { OUString aTmp, aSys; osl_createTempFile( NULL, NULL, &aTmp.pData ); osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData ); return aSys; } bool PspSalPrinter::StartJob( const OUString* pFileName, const OUString& rJobName, const OUString& rAppName, sal_uLong nCopies, bool bCollate, bool bDirect, ImplJobSetup* pJobSetup ) { OSL_TRACE("PspSalPrinter::StartJob"); GetSalData()->m_pInstance->jobStartedPrinterUpdate(); m_bPdf = false; m_aFileName = pFileName ? *pFileName : OUString(); m_aTmpFile = OUString(); m_nCopies = nCopies; m_bCollate = bCollate; JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); if( m_nCopies > 1 ) { // in case user did not do anything (m_nCopies=1) // take the default from jobsetup m_aJobData.m_nCopies = m_nCopies; m_aJobData.setCollate( bCollate ); } int nMode = 0; #if defined( UNX ) // check whether this printer is configured as fax sal_Int32 nIndex = 0; const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) ); while( nIndex != -1 ) { OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) ); if( aToken.startsWith( "pdf=" ) ) { m_bPdf = true; m_aTmpFile = getTmpName(); nMode = S_IRUSR | S_IWUSR; if( m_aFileName.isEmpty() ) { OUStringBuffer aFileName( getPdfDir( rInfo ) ); aFileName.append( '/' ); aFileName.append( rJobName ); aFileName.appendAscii( ".pdf" ); m_aFileName = aFileName.makeStringAndClear(); } break; } } #endif m_aPrinterGfx.Init( m_aJobData ); return m_aPrintJob.StartJob( ! m_aTmpFile.isEmpty() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect ) ? true : false; } bool PspSalPrinter::EndJob() { bool bSuccess = false; if( m_bIsPDFWriterJob ) bSuccess = true; else { bSuccess = m_aPrintJob.EndJob(); OSL_TRACE("PspSalPrinter::EndJob %d", bSuccess); if( bSuccess && m_bPdf ) { const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) ); bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand ); } } GetSalData()->m_pInstance->jobEndedPrinterUpdate(); return bSuccess; } bool PspSalPrinter::AbortJob() { bool bAbort = m_aPrintJob.AbortJob() ? true : false; GetSalData()->m_pInstance->jobEndedPrinterUpdate(); return bAbort; } SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, bool ) { OSL_TRACE("PspSalPrinter::StartPage"); JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData ); m_pGraphics = GetGenericInstance()->CreatePrintGraphics(); m_pGraphics->Init(&m_aJobData, &m_aPrinterGfx, m_pInfoPrinter); if( m_nCopies > 1 ) { // in case user did not do anything (m_nCopies=1) // take the default from jobsetup m_aJobData.m_nCopies = m_nCopies; m_aJobData.setCollate( m_nCopies > 1 && m_bCollate ); } m_aPrintJob.StartPage( m_aJobData ); m_aPrinterGfx.Init( m_aPrintJob ); return m_pGraphics; } bool PspSalPrinter::EndPage() { bool bResult = m_aPrintJob.EndPage(); m_aPrinterGfx.Clear(); OSL_TRACE("PspSalPrinter::EndPage"); return bResult; } sal_uLong PspSalPrinter::GetErrorCode() { return 0; } 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 long nRotatedWidth = rComp.maPageSize.Height(); const 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 ! this->operator==(rComp); } }; struct PDFPrintFile { OUString maTmpURL; PDFNewJobParameters maParameters; PDFPrintFile( const OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters ) : maTmpURL( i_rURL ) , 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 ) { OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName ? OUStringToOString( *i_pFileName, RTL_TEXTENCODING_UTF8 ).getStr() : "" ); // mark for endjob m_bIsPDFWriterJob = true; // reset IsLastPage i_rController.setLastPage( false ); // is this a fax device bool bFax = m_pInfoPrinter->GetCapabilities(i_pSetupData, PRINTER_CAPABILITIES_FAX) == 1; // update job data if( i_pSetupData ) JobData::constructFromStreamBuffer( i_pSetupData->mpDriverData, i_pSetupData->mnDriverDataLen, m_aJobData ); OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 ); m_aJobData.m_nPDFDevice = 1; // possibly create one job for collated output bool bSinglePrintJobs = false; beans::PropertyValue* pSingleValue = i_rController.getValue( OUString( "PrintCollateAsSingleJobs" ) ); if( pSingleValue ) { pSingleValue->Value >>= bSinglePrintJobs; } int nCopies = i_rController.getPrinter()->GetCopyCount(); bool bCollate = i_rController.getPrinter()->IsCollateCopy(); // notify start of real print job i_rController.jobStarted(); // setup PDFWriter context vcl::PDFWriter::PDFWriterContext aContext; aContext.Version = vcl::PDFWriter::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; boost::shared_ptr pWriter; std::vector< PDFPrintFile > aPDFFiles; boost::shared_ptr pPrinter( i_rController.getPrinter() ); int nAllPages = i_rController.getFilteredPageCount(); i_rController.createProgressDialog(); bool bAborted = false; PDFNewJobParameters aLastParm; aContext.DPIx = pPrinter->GetDPIX(); aContext.DPIy = pPrinter->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 { pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) ); pPrinter->SetPaperSizeUser( aPageSize.aSize, true ); PDFNewJobParameters aNewParm( pPrinter->GetPaperSize(), pPrinter->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( ! pWriter || (aNewParm != aLastParm && ! i_pFileName ) ) { if( pWriter ) { pWriter->Emit(); } // produce PDF file OUString aPDFUrl; if( i_pFileName ) aPDFUrl = *i_pFileName; else osl_createTempFile( NULL, NULL, &aPDFUrl.pData ); // normalize to file URL if( !aPDFUrl.startsWith( "file:" ) ) { // this is not a file URL, but it should // form it into a osl friendly file URL OUString aTmp; osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData ); aPDFUrl = aTmp; } // save current file and paper format aLastParm = aNewParm; aPDFFiles.push_back( PDFPrintFile( aPDFUrl, aNewParm ) ); // update context aContext.URL = aPDFUrl; // create and initialize PDFWriter pWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) ); } pWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ), TenMuToPt( aNewParm.maPageSize.Height() ), vcl::PDFWriter::Portrait ); pWriter->PlayMetafile( aPageFile, aMtfContext, NULL ); } } // emit the last file if( pWriter ) pWriter->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 && pPrinter->HasSupport( SUPPORT_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 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 = NULL; 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( pPrinter->GetName(), i_rController.isDirectPrint() ); if( fp ) { sal_uInt64 nBytesRead = 0; do { osl_readFile( pFile, &buffer[0], buffer.size(), &nBytesRead ); if( nBytesRead > 0 ) { size_t nBytesWritten = fwrite(&buffer[0], 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( pPrinter->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( size_t i = 0; i < aPDFFiles.size(); i++ ) { osl_removeFile( aPDFFiles[i].maTmpURL.pData ); OSL_TRACE( "removed print PDF file %s", OUStringToOString( aPDFFiles[i].maTmpURL, RTL_TEXTENCODING_UTF8 ).getStr() ); } } return true; } class PrinterUpdate { static Timer* pPrinterUpdateTimer; static int nActiveJobs; static void doUpdate(); DECL_STATIC_LINK( PrinterUpdate, UpdateTimerHdl, void* ); public: static void update(SalGenericInstance &rInstance); static void jobStarted() { nActiveJobs++; } static void jobEnded(); }; Timer* PrinterUpdate::pPrinterUpdateTimer = NULL; int PrinterUpdate::nActiveJobs = 0; void PrinterUpdate::doUpdate() { ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() ); SalGenericInstance *pInst = static_cast( GetSalData()->m_pInstance ); if( pInst && rManager.checkPrintersChanged( false ) ) pInst->PostPrintersChanged(); } IMPL_STATIC_LINK_NOINSTANCE( PrinterUpdate, UpdateTimerHdl, void*, EMPTYARG ) { if( nActiveJobs < 1 ) { doUpdate(); delete pPrinterUpdateTimer; pPrinterUpdateTimer = NULL; } else pPrinterUpdateTimer->Start(); return 0; } void PrinterUpdate::update(SalGenericInstance &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( ! pPrinterUpdateTimer ) { pPrinterUpdateTimer = new Timer(); pPrinterUpdateTimer->SetTimeout( 500 ); pPrinterUpdateTimer->SetTimeoutHdl( STATIC_LINK( NULL, PrinterUpdate, UpdateTimerHdl ) ); pPrinterUpdateTimer->Start(); } } void SalGenericInstance::updatePrinterUpdate() { PrinterUpdate::update(*this); } void SalGenericInstance::jobStartedPrinterUpdate() { PrinterUpdate::jobStarted(); } void PrinterUpdate::jobEnded() { nActiveJobs--; if( nActiveJobs < 1 ) { if( pPrinterUpdateTimer ) { pPrinterUpdateTimer->Stop(); delete pPrinterUpdateTimer; pPrinterUpdateTimer = NULL; doUpdate(); } } } void SalGenericInstance::jobEndedPrinterUpdate() { PrinterUpdate::jobEnded(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */