Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / generic / print / genprnpsp.cxx
blob6f4a9075052a879a1cbdbf08299ced7263acc7d8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 /**
21 this file implements the sal printer interface ( SalPrinter, SalInfoPrinter
22 and some printer relevant methods of SalInstance and SalGraphicsData )
24 as aunderlying library the printer features of psprint are used.
26 The query methods of a SalInfoPrinter are implemented by querying psprint
28 The job methods of a SalPrinter are implemented by calling psprint
29 printer job functions.
32 // For spawning PDF and FAX generation
33 #if defined( UNX )
34 # include <unistd.h>
35 # include <sys/wait.h>
36 # include <sys/stat.h>
37 #endif
39 #include "rtl/ustring.hxx"
41 #include "vcl/button.hxx"
42 #include "vcl/dialog.hxx"
43 #include "vcl/edit.hxx"
44 #include "vcl/fixed.hxx"
45 #include "vcl/idle.hxx"
46 #include "vcl/svapp.hxx"
47 #include "vcl/print.hxx"
48 #include "vcl/pdfwriter.hxx"
49 #include "vcl/printerinfomanager.hxx"
50 #include "vcl/settings.hxx"
51 #include "svids.hrc"
52 #include "saldatabasic.hxx"
53 #include "generic/genprn.h"
54 #include "generic/geninst.h"
55 #include "generic/genpspgraphics.h"
57 #include "jobset.h"
58 #include "print.h"
59 #include "prtsetup.hxx"
60 #include "salptype.hxx"
62 #include <com/sun/star/beans/PropertyValue.hpp>
64 using namespace psp;
65 using namespace com::sun::star;
68 * static helpers
70 static OUString getPdfDir( const PrinterInfo& rInfo )
72 OUString aDir;
73 sal_Int32 nIndex = 0;
74 while( nIndex != -1 )
76 OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
77 if( aToken.startsWith( "pdf=" ) )
79 sal_Int32 nPos = 0;
80 aDir = aToken.getToken( 1, '=', nPos );
81 if( aDir.isEmpty() && getenv( "HOME" ) )
82 aDir = OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
83 break;
86 return aDir;
89 namespace
91 class QueryString : public ModalDialog
93 private:
94 VclPtr<OKButton> m_pOKButton;
95 VclPtr<FixedText> m_pFixedText;
96 VclPtr<Edit> m_pEdit;
97 OUString& m_rReturnValue;
99 DECL_LINK( ClickBtnHdl, Button* );
101 public:
102 // parent window, Query text, initial value
103 QueryString(vcl::Window*, OUString &, OUString &);
104 virtual ~QueryString() { disposeOnce(); }
105 virtual void dispose() SAL_OVERRIDE
107 m_pOKButton.clear();
108 m_pFixedText.clear();
109 m_pEdit.clear();
110 ModalDialog::dispose();
115 * QueryString
117 QueryString::QueryString(vcl::Window* pParent, OUString& rQuery, OUString& rRet)
118 : ModalDialog(pParent, "QueryDialog",
119 "vcl/ui/querydialog.ui" )
120 , m_rReturnValue( rRet )
122 get(m_pOKButton, "ok");
123 get(m_pFixedText, "label");
124 get(m_pEdit, "entry");
126 m_pOKButton->SetClickHdl(LINK(this, QueryString, ClickBtnHdl));
127 m_pFixedText->SetText(rQuery);
128 m_pEdit->SetText(m_rReturnValue);
129 SetText(rQuery);
132 IMPL_LINK( QueryString, ClickBtnHdl, Button*, pButton )
134 if (pButton == m_pOKButton)
136 m_rReturnValue = m_pEdit->GetText();
137 EndDialog( 1 );
139 else
140 EndDialog(0);
141 return 0;
144 int QueryFaxNumber(OUString& rNumber)
146 OUString aTmpString(VclResId(SV_PRINT_QUERYFAXNUMBER_TXT));
147 ScopedVclPtrInstance< QueryString > aQuery( nullptr, aTmpString, rNumber );
148 return aQuery->Execute();
152 inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); }
154 inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); }
156 static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
158 pJobSetup->meOrientation = (Orientation)(rData.m_eOrientation == orientation::Landscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT);
160 // copy page size
161 OUString aPaper;
162 int width, height;
164 rData.m_aContext.getPageSize( aPaper, width, height );
165 pJobSetup->mePaperFormat = PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 ));
167 pJobSetup->mnPaperWidth = 0;
168 pJobSetup->mnPaperHeight = 0;
169 if( pJobSetup->mePaperFormat == PAPER_USER )
171 // transform to 100dth mm
172 width = PtTo10Mu( width );
173 height = PtTo10Mu( height );
175 if( rData.m_eOrientation == psp::orientation::Portrait )
177 pJobSetup->mnPaperWidth = width;
178 pJobSetup->mnPaperHeight= height;
180 else
182 pJobSetup->mnPaperWidth = height;
183 pJobSetup->mnPaperHeight= width;
187 // copy input slot
188 const PPDKey* pKey = NULL;
189 const PPDValue* pValue = NULL;
191 pJobSetup->mnPaperBin = 0;
192 if( rData.m_pParser )
193 pKey = rData.m_pParser->getKey( OUString("InputSlot") );
194 if( pKey )
195 pValue = rData.m_aContext.getValue( pKey );
196 if( pKey && pValue )
198 for( pJobSetup->mnPaperBin = 0;
199 pValue != pKey->getValue( pJobSetup->mnPaperBin ) &&
200 pJobSetup->mnPaperBin < pKey->countValues();
201 pJobSetup->mnPaperBin++ )
203 if( pJobSetup->mnPaperBin >= pKey->countValues() )
204 pJobSetup->mnPaperBin = 0;
207 // copy duplex
208 pKey = NULL;
209 pValue = NULL;
211 pJobSetup->meDuplexMode = DUPLEX_UNKNOWN;
212 if( rData.m_pParser )
213 pKey = rData.m_pParser->getKey( OUString("Duplex") );
214 if( pKey )
215 pValue = rData.m_aContext.getValue( pKey );
216 if( pKey && pValue )
218 if( pValue->m_aOption.equalsIgnoreAsciiCase( "None" ) ||
219 pValue->m_aOption.startsWithIgnoreAsciiCase( "Simplex" )
222 pJobSetup->meDuplexMode = DUPLEX_OFF;
224 else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
226 pJobSetup->meDuplexMode = DUPLEX_LONGEDGE;
228 else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexTumble" ) )
230 pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE;
234 // copy the whole context
235 if( pJobSetup->mpDriverData )
236 rtl_freeMemory( pJobSetup->mpDriverData );
238 int nBytes;
239 void* pBuffer = NULL;
240 if( rData.getStreamBuffer( pBuffer, nBytes ) )
242 pJobSetup->mnDriverDataLen = nBytes;
243 pJobSetup->mpDriverData = static_cast<sal_uInt8*>(pBuffer);
245 else
247 pJobSetup->mnDriverDataLen = 0;
248 pJobSetup->mpDriverData = NULL;
252 // Needs a cleaner abstraction ...
253 #if defined( UNX )
254 static bool passFileToCommandLine( const OUString& rFilename, const OUString& rCommandLine, bool bRemoveFile = true )
256 bool bSuccess = false;
258 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
259 OString aCmdLine(OUStringToOString(rCommandLine, aEncoding));
260 OString aFilename(OUStringToOString(rFilename, aEncoding));
262 bool bPipe = aCmdLine.indexOf( "(TMP)" ) == -1;
264 // setup command line for exec
265 if( ! bPipe )
266 aCmdLine = aCmdLine.replaceAll(OString("(TMP)"), aFilename);
268 #if OSL_DEBUG_LEVEL > 1
269 fprintf( stderr, "%s commandline: \"%s\"\n",
270 bPipe ? "piping to" : "executing",
271 aCmdLine.getStr() );
272 struct stat aStat;
273 if( stat( aFilename.getStr(), &aStat ) )
274 fprintf( stderr, "stat( %s ) failed\n", aFilename.getStr() );
275 fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.getStr(), (long)aStat.st_mode );
276 #endif
277 const char* argv[4];
278 if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) )
279 argv[ 0 ] = "/bin/sh";
280 argv[ 1 ] = "-c";
281 argv[ 2 ] = aCmdLine.getStr();
282 argv[ 3 ] = 0;
284 bool bHavePipes = false;
285 int pid, fd[2];
287 if( bPipe )
288 bHavePipes = pipe( fd ) == 0;
289 if( ( pid = fork() ) > 0 )
291 if( bPipe && bHavePipes )
293 close( fd[0] );
294 char aBuffer[ 2048 ];
295 FILE* fp = fopen( aFilename.getStr(), "r" );
296 while (fp && !feof(fp))
298 size_t nBytesRead = fread(aBuffer, 1, sizeof( aBuffer ), fp);
299 if (nBytesRead )
301 size_t nBytesWritten = write(fd[1], aBuffer, nBytesRead);
302 OSL_ENSURE(nBytesWritten == nBytesRead, "short write");
303 if (nBytesWritten != nBytesRead)
304 break;
307 fclose( fp );
308 close( fd[ 1 ] );
310 int status = 0;
311 if(waitpid( pid, &status, 0 ) != -1)
313 if( ! status )
314 bSuccess = true;
317 else if( ! pid )
319 if( bPipe && bHavePipes )
321 close( fd[1] );
322 if( fd[0] != STDIN_FILENO ) // not probable, but who knows :)
323 dup2( fd[0], STDIN_FILENO );
325 execv( argv[0], const_cast<char**>(argv) );
326 fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.getStr() );
327 _exit( 1 );
329 else
330 fprintf( stderr, "failed to fork\n" );
332 // clean up the mess
333 if( bRemoveFile )
334 unlink( aFilename.getStr() );
336 return bSuccess;
338 #endif
340 static std::vector<OUString> getFaxNumbers()
342 std::vector<OUString> aFaxNumbers;
344 OUString aNewNr;
345 if (QueryFaxNumber(aNewNr))
347 sal_Int32 nIndex = 0;
350 OUString sToken = aNewNr.getToken( 0, ';', nIndex );
351 aFaxNumbers.push_back(sToken);
353 while (nIndex >= 0);
356 return aFaxNumbers;
359 static bool createPdf( const OUString& rToFile, const OUString& rFromFile, const OUString& rCommandLine )
361 #if defined( UNX )
362 OUString aCommandLine(
363 rCommandLine.replaceAll("(OUTFILE)", rToFile));
365 return passFileToCommandLine( rFromFile, aCommandLine );
366 #else
367 (void)rToFile; (void)rFromFile; (void)rCommandLine;
368 return false;
369 #endif
373 * SalInstance
376 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter *pPrinter,
377 SalPrinterQueueInfo* pQueueInfo, ImplJobSetup* pJobSetup)
379 if( pJobSetup )
381 PrinterInfoManager& rManager( PrinterInfoManager::get() );
382 PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
383 pPrinter->m_aJobData = aInfo;
384 pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData );
386 if( pJobSetup->mpDriverData )
387 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
389 pJobSetup->mnSystem = JOBSETUP_SYSTEM_UNIX;
390 pJobSetup->maPrinterName = pQueueInfo->maPrinterName;
391 pJobSetup->maDriver = aInfo.m_aDriverName;
392 copyJobDataToJobSetup( pJobSetup, aInfo );
396 SalInfoPrinter* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
397 ImplJobSetup* pJobSetup )
399 mbPrinterInit = true;
400 // create and initialize SalInfoPrinter
401 PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter();
402 configurePspInfoPrinter(pPrinter, pQueueInfo, pJobSetup);
403 return pPrinter;
406 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
408 delete pPrinter;
411 SalPrinter* SalGenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
413 mbPrinterInit = true;
414 // create and initialize SalPrinter
415 PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
416 pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
418 return pPrinter;
421 void SalGenericInstance::DestroyPrinter( SalPrinter* pPrinter )
423 delete pPrinter;
426 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
428 mbPrinterInit = true;
429 PrinterInfoManager& rManager( PrinterInfoManager::get() );
430 static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
431 if( ! pNoSyncDetection || ! *pNoSyncDetection )
433 // #i62663# synchronize possible asynchronouse printer detection now
434 rManager.checkPrintersChanged( true );
436 ::std::list< OUString > aPrinters;
437 rManager.listPrinters( aPrinters );
439 for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it )
441 const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) );
442 // Neuen Eintrag anlegen
443 SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
444 pInfo->maPrinterName = *it;
445 pInfo->maDriver = rInfo.m_aDriverName;
446 pInfo->maLocation = rInfo.m_aLocation;
447 pInfo->maComment = rInfo.m_aComment;
448 pInfo->mpSysData = NULL;
450 sal_Int32 nIndex = 0;
451 while( nIndex != -1 )
453 OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
454 if( aToken.match( "pdf=" ) )
456 pInfo->maLocation = getPdfDir( rInfo );
457 break;
461 pList->Add( pInfo );
465 void SalGenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
467 delete pInfo;
470 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
472 mbPrinterInit = true;
475 OUString SalGenericInstance::GetDefaultPrinter()
477 mbPrinterInit = true;
478 PrinterInfoManager& rManager( PrinterInfoManager::get() );
479 return rManager.getDefaultPrinter();
482 PspSalInfoPrinter::PspSalInfoPrinter()
483 : m_pGraphics( NULL )
487 PspSalInfoPrinter::~PspSalInfoPrinter()
489 if( m_pGraphics )
491 delete m_pGraphics;
492 m_pGraphics = NULL;
496 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
498 m_aPaperFormats.clear();
499 m_bPapersInit = true;
501 if( m_aJobData.m_pParser )
503 const PPDKey* pKey = m_aJobData.m_pParser->getKey( OUString("PageSize") );
504 if( pKey )
506 int nValues = pKey->countValues();
507 for( int i = 0; i < nValues; i++ )
509 const PPDValue* pValue = pKey->getValue( i );
510 int nWidth = 0, nHeight = 0;
511 m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
512 PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
513 m_aPaperFormats.push_back( aInfo );
519 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
521 return 900;
524 SalGraphics* PspSalInfoPrinter::AcquireGraphics()
526 // return a valid pointer only once
527 // the reasoning behind this is that we could have different
528 // SalGraphics that can run in multiple threads
529 // (future plans)
530 SalGraphics* pRet = NULL;
531 if( ! m_pGraphics )
533 m_pGraphics = GetGenericInstance()->CreatePrintGraphics();
534 m_pGraphics->Init(&m_aJobData, &m_aPrinterGfx, this);
535 pRet = m_pGraphics;
537 return pRet;
540 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
542 if( pGraphics == m_pGraphics )
544 delete pGraphics;
545 m_pGraphics = NULL;
547 return;
550 bool PspSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup )
552 if( ! pFrame || ! pJobSetup )
553 return false;
555 PrinterInfoManager& rManager = PrinterInfoManager::get();
557 PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) );
558 if ( pJobSetup->mpDriverData )
560 SetData( ~0, pJobSetup );
561 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
563 aInfo.m_bPapersizeFromSetup = pJobSetup->mbPapersizeFromSetup;
565 if (SetupPrinterDriver(aInfo))
567 aInfo.resolveDefaultBackend();
568 rtl_freeMemory( pJobSetup->mpDriverData );
569 pJobSetup->mpDriverData = NULL;
571 int nBytes;
572 void* pBuffer = NULL;
573 aInfo.getStreamBuffer( pBuffer, nBytes );
574 pJobSetup->mnDriverDataLen = nBytes;
575 pJobSetup->mpDriverData = static_cast<sal_uInt8*>(pBuffer);
577 // copy everything to job setup
578 copyJobDataToJobSetup( pJobSetup, aInfo );
579 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
580 return true;
582 return false;
585 // This function gets the driver data and puts it into pJobSetup
586 // If pJobSetup->mpDriverData is NOT NULL, then the independent
587 // data should be merged into the driver data
588 // If pJobSetup->mpDriverData IS NULL, then the driver defaults
589 // should be merged into the independent data
590 bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
592 if( pJobSetup->mpDriverData )
593 return SetData( ~0, pJobSetup );
595 copyJobDataToJobSetup( pJobSetup, m_aJobData );
597 return true;
600 // This function merges the independ driver data
601 // and sets the new independ data in pJobSetup
602 // Only the data must be changed, where the bit
603 // in nGetDataFlags is set
604 bool PspSalInfoPrinter::SetData(
605 sal_uLong nSetDataFlags,
606 ImplJobSetup* pJobSetup )
608 JobData aData;
609 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
611 if( aData.m_pParser )
613 const PPDKey* pKey;
614 const PPDValue* pValue;
616 // merge papersize if necessary
617 if( nSetDataFlags & SAL_JOBSET_PAPERSIZE )
619 OUString aPaper;
621 if( pJobSetup->mePaperFormat == PAPER_USER )
622 aPaper = aData.m_pParser->matchPaper(
623 TenMuToPt( pJobSetup->mnPaperWidth ),
624 TenMuToPt( pJobSetup->mnPaperHeight ) );
625 else
626 aPaper = OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1);
628 pKey = aData.m_pParser->getKey( OUString("PageSize") );
629 pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL;
631 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
632 // try to find the correct paper anyway using the size
633 if( pKey && ! pValue && pJobSetup->mePaperFormat != PAPER_USER )
635 PaperInfo aInfo( pJobSetup->mePaperFormat );
636 aPaper = aData.m_pParser->matchPaper(
637 TenMuToPt( aInfo.getWidth() ),
638 TenMuToPt( aInfo.getHeight() ) );
639 pValue = pKey->getValueCaseInsensitive( aPaper );
642 if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) )
643 return false;
646 // merge paperbin if necessary
647 if( nSetDataFlags & SAL_JOBSET_PAPERBIN )
649 pKey = aData.m_pParser->getKey( OUString("InputSlot") );
650 if( pKey )
652 int nPaperBin = pJobSetup->mnPaperBin;
653 if( nPaperBin >= pKey->countValues() )
654 pValue = pKey->getDefaultValue();
655 else
656 pValue = pKey->getValue( pJobSetup->mnPaperBin );
658 // may fail due to constraints;
659 // real paper bin is copied back to jobsetup in that case
660 aData.m_aContext.setValue( pKey, pValue );
662 // if printer has no InputSlot key simply ignore this setting
663 // (e.g. SGENPRT has no InputSlot)
666 // merge orientation if necessary
667 if( nSetDataFlags & SAL_JOBSET_ORIENTATION )
668 aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait;
670 // merge duplex if necessary
671 if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE )
673 pKey = aData.m_pParser->getKey( OUString("Duplex") );
674 if( pKey )
676 pValue = NULL;
677 switch( pJobSetup->meDuplexMode )
679 case DUPLEX_OFF:
680 pValue = pKey->getValue( OUString("None") );
681 if( pValue == NULL )
682 pValue = pKey->getValue( OUString("SimplexNoTumble") );
683 break;
684 case DUPLEX_SHORTEDGE:
685 pValue = pKey->getValue( OUString("DuplexTumble") );
686 break;
687 case DUPLEX_LONGEDGE:
688 pValue = pKey->getValue( OUString("DuplexNoTumble") );
689 break;
690 case DUPLEX_UNKNOWN:
691 default:
692 pValue = 0;
693 break;
695 if( ! pValue )
696 pValue = pKey->getDefaultValue();
697 aData.m_aContext.setValue( pKey, pValue );
701 m_aJobData = aData;
702 copyJobDataToJobSetup( pJobSetup, aData );
703 return true;
706 return false;
709 void PspSalInfoPrinter::GetPageInfo(
710 const ImplJobSetup* pJobSetup,
711 long& rOutWidth, long& rOutHeight,
712 long& rPageOffX, long& rPageOffY,
713 long& rPageWidth, long& rPageHeight )
715 if( ! pJobSetup )
716 return;
718 JobData aData;
719 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
721 // get the selected page size
722 if( aData.m_pParser )
725 OUString aPaper;
726 int width, height;
727 int left = 0, top = 0, right = 0, bottom = 0;
728 int nDPI = aData.m_aContext.getRenderResolution();
730 if( aData.m_eOrientation == psp::orientation::Portrait )
732 aData.m_aContext.getPageSize( aPaper, width, height );
733 aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
735 else
737 aData.m_aContext.getPageSize( aPaper, height, width );
738 aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
741 rPageWidth = width * nDPI / 72;
742 rPageHeight = height * nDPI / 72;
743 rPageOffX = left * nDPI / 72;
744 rPageOffY = top * nDPI / 72;
745 rOutWidth = ( width - left - right ) * nDPI / 72;
746 rOutHeight = ( height - top - bottom ) * nDPI / 72;
750 sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
752 if( ! pJobSetup )
753 return 0;
755 JobData aData;
756 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
758 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( OUString("InputSlot") ): NULL;
759 return pKey ? pKey->countValues() : 0;
762 OUString PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin )
764 JobData aData;
765 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
767 OUString aRet;
768 if( aData.m_pParser )
770 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( OUString("InputSlot") ): NULL;
771 if( ! pKey || nPaperBin >= (sal_uLong)pKey->countValues() )
772 aRet = aData.m_pParser->getDefaultInputSlot();
773 else
775 const PPDValue* pValue = pKey->getValue( nPaperBin );
776 if( pValue )
777 aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
781 return aRet;
784 sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType )
786 switch( nType )
788 case PRINTER_CAPABILITIES_SUPPORTDIALOG:
789 return 1;
790 case PRINTER_CAPABILITIES_COPIES:
791 return 0xffff;
792 case PRINTER_CAPABILITIES_COLLATECOPIES:
794 // PPDs don't mention the number of possible collated copies.
795 // so let's guess as many as we want ?
796 return 0xffff;
798 case PRINTER_CAPABILITIES_SETORIENTATION:
799 return 1;
800 case PRINTER_CAPABILITIES_SETDUPLEX:
801 return 1;
802 case PRINTER_CAPABILITIES_SETPAPERBIN:
803 return 1;
804 case PRINTER_CAPABILITIES_SETPAPERSIZE:
805 return 1;
806 case PRINTER_CAPABILITIES_SETPAPER:
807 return 0;
808 case PRINTER_CAPABILITIES_FAX:
810 // see if the PPD contains the fax4CUPS "Dial" option and that it's not set
811 // to "manually"
812 JobData aData = PrinterInfoManager::get().getPrinterInfo(pJobSetup->maPrinterName);
813 if( pJobSetup->mpDriverData )
814 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
815 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey(OUString("Dial")) : NULL;
816 const PPDValue* pValue = pKey ? aData.m_aContext.getValue(pKey) : NULL;
817 if (pValue && !pValue->m_aOption.equalsIgnoreAsciiCase("Manually"))
818 return 1;
819 return 0;
822 case PRINTER_CAPABILITIES_PDF:
823 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "pdf" ) )
824 return 1;
825 else
827 // see if the PPD contains a value to set PDF device
828 JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
829 if( pJobSetup->mpDriverData )
830 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
831 return aData.m_nPDFDevice > 0 ? 1 : 0;
833 case PRINTER_CAPABILITIES_EXTERNALDIALOG:
834 return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "external_dialog" ) ? 1 : 0;
835 case PRINTER_CAPABILITIES_USEPULLMODEL:
837 // see if the PPD contains a value to set PDF device
838 JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
839 if( pJobSetup->mpDriverData )
840 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
841 return aData.m_nPDFDevice > 0 ? 1 : 0;
843 default: break;
845 return 0;
849 * SalPrinter
851 PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
852 : m_bPdf( false ),
853 m_bIsPDFWriterJob( false ),
854 m_pGraphics( NULL ),
855 m_nCopies( 1 ),
856 m_bCollate( false ),
857 m_pInfoPrinter( pInfoPrinter )
861 PspSalPrinter::~PspSalPrinter()
865 static OUString getTmpName()
867 OUString aTmp, aSys;
868 osl_createTempFile( NULL, NULL, &aTmp.pData );
869 osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData );
871 return aSys;
874 bool PspSalPrinter::StartJob(
875 const OUString* pFileName,
876 const OUString& rJobName,
877 const OUString& rAppName,
878 sal_uLong nCopies,
879 bool bCollate,
880 bool bDirect,
881 ImplJobSetup* pJobSetup )
883 OSL_TRACE("PspSalPrinter::StartJob");
884 GetSalData()->m_pInstance->jobStartedPrinterUpdate();
885 m_bPdf = false;
886 if (pFileName)
887 m_aFileName = *pFileName;
888 else
889 m_aFileName.clear();
890 m_aTmpFile.clear();
891 m_nCopies = nCopies;
892 m_bCollate = bCollate;
894 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
895 if( m_nCopies > 1 )
897 // in case user did not do anything (m_nCopies=1)
898 // take the default from jobsetup
899 m_aJobData.m_nCopies = m_nCopies;
900 m_aJobData.setCollate( bCollate );
903 int nMode = 0;
904 #if defined( UNX )
905 // check whether this printer is configured as fax
906 sal_Int32 nIndex = 0;
907 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
908 while( nIndex != -1 )
910 OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
911 if( aToken.startsWith( "pdf=" ) )
913 m_bPdf = true;
914 m_aTmpFile = getTmpName();
915 nMode = S_IRUSR | S_IWUSR;
917 if( m_aFileName.isEmpty() )
919 OUStringBuffer aFileName( getPdfDir( rInfo ) );
920 aFileName.append( '/' );
921 aFileName.append( rJobName );
922 aFileName.appendAscii( ".pdf" );
923 m_aFileName = aFileName.makeStringAndClear();
925 break;
928 #endif
929 m_aPrinterGfx.Init( m_aJobData );
931 return m_aPrintJob.StartJob( ! m_aTmpFile.isEmpty() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect );
934 bool PspSalPrinter::EndJob()
936 bool bSuccess = false;
937 if( m_bIsPDFWriterJob )
938 bSuccess = true;
939 else
941 bSuccess = m_aPrintJob.EndJob();
942 OSL_TRACE("PspSalPrinter::EndJob %d", bSuccess);
944 if( bSuccess && m_bPdf )
946 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
947 bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand );
950 GetSalData()->m_pInstance->jobEndedPrinterUpdate();
951 return bSuccess;
954 bool PspSalPrinter::AbortJob()
956 bool bAbort = m_aPrintJob.AbortJob();
957 GetSalData()->m_pInstance->jobEndedPrinterUpdate();
958 return bAbort;
961 SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, bool )
963 OSL_TRACE("PspSalPrinter::StartPage");
965 JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
966 m_pGraphics = GetGenericInstance()->CreatePrintGraphics();
967 m_pGraphics->Init(&m_aJobData, &m_aPrinterGfx, m_pInfoPrinter);
969 if( m_nCopies > 1 )
971 // in case user did not do anything (m_nCopies=1)
972 // take the default from jobsetup
973 m_aJobData.m_nCopies = m_nCopies;
974 m_aJobData.setCollate( m_nCopies > 1 && m_bCollate );
977 m_aPrintJob.StartPage( m_aJobData );
978 m_aPrinterGfx.Init( m_aPrintJob );
980 return m_pGraphics;
983 bool PspSalPrinter::EndPage()
985 bool bResult = m_aPrintJob.EndPage();
986 m_aPrinterGfx.Clear();
987 OSL_TRACE("PspSalPrinter::EndPage");
988 return bResult;
991 sal_uLong PspSalPrinter::GetErrorCode()
993 return 0;
996 struct PDFNewJobParameters
998 Size maPageSize;
999 sal_uInt16 mnPaperBin;
1001 PDFNewJobParameters( const Size& i_rSize = Size(),
1002 sal_uInt16 i_nPaperBin = 0xffff )
1003 : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
1005 bool operator==(const PDFNewJobParameters& rComp ) const
1007 const long nRotatedWidth = rComp.maPageSize.Height();
1008 const long nRotatedHeight = rComp.maPageSize.Width();
1009 Size aCompLSSize(nRotatedWidth, nRotatedHeight);
1010 return
1011 (maPageSize == rComp.maPageSize || maPageSize == aCompLSSize)
1012 && mnPaperBin == rComp.mnPaperBin
1016 bool operator!=(const PDFNewJobParameters& rComp) const
1018 return ! this->operator==(rComp);
1022 struct PDFPrintFile
1024 OUString maTmpURL;
1025 PDFNewJobParameters maParameters;
1027 PDFPrintFile( const OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters )
1028 : maTmpURL( i_rURL )
1029 , maParameters( i_rNewParameters ) {}
1032 bool PspSalPrinter::StartJob( const OUString* i_pFileName, const OUString& i_rJobName, const OUString& i_rAppName,
1033 ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
1035 OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName ? OUStringToOString( *i_pFileName, RTL_TEXTENCODING_UTF8 ).getStr() : "<nil>" );
1036 // mark for endjob
1037 m_bIsPDFWriterJob = true;
1038 // reset IsLastPage
1039 i_rController.setLastPage( false );
1040 // is this a fax device
1041 bool bFax = m_pInfoPrinter->GetCapabilities(i_pSetupData, PRINTER_CAPABILITIES_FAX) == 1;
1043 // update job data
1044 if( i_pSetupData )
1045 JobData::constructFromStreamBuffer( i_pSetupData->mpDriverData, i_pSetupData->mnDriverDataLen, m_aJobData );
1047 OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 );
1048 m_aJobData.m_nPDFDevice = 1;
1050 // possibly create one job for collated output
1051 bool bSinglePrintJobs = false;
1052 beans::PropertyValue* pSingleValue = i_rController.getValue( OUString( "PrintCollateAsSingleJobs" ) );
1053 if( pSingleValue )
1055 pSingleValue->Value >>= bSinglePrintJobs;
1058 int nCopies = i_rController.getPrinter()->GetCopyCount();
1059 bool bCollate = i_rController.getPrinter()->IsCollateCopy();
1061 // notify start of real print job
1062 i_rController.jobStarted();
1064 // setup PDFWriter context
1065 vcl::PDFWriter::PDFWriterContext aContext;
1066 aContext.Version = vcl::PDFWriter::PDF_1_4;
1067 aContext.Tagged = false;
1068 aContext.DocumentLocale = Application::GetSettings().GetLanguageTag().getLocale();
1069 aContext.ColorMode = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
1070 ? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor;
1072 // prepare doc info
1073 aContext.DocumentInfo.Title = i_rJobName;
1074 aContext.DocumentInfo.Creator = i_rAppName;
1075 aContext.DocumentInfo.Producer = i_rAppName;
1077 // define how we handle metafiles in PDFWriter
1078 vcl::PDFWriter::PlayMetafileContext aMtfContext;
1079 aMtfContext.m_bOnlyLosslessCompression = true;
1081 std::shared_ptr<vcl::PDFWriter> xWriter;
1082 std::vector< PDFPrintFile > aPDFFiles;
1083 VclPtr<Printer> xPrinter( i_rController.getPrinter() );
1084 int nAllPages = i_rController.getFilteredPageCount();
1085 i_rController.createProgressDialog();
1086 bool bAborted = false;
1087 PDFNewJobParameters aLastParm;
1089 aContext.DPIx = xPrinter->GetDPIX();
1090 aContext.DPIy = xPrinter->GetDPIY();
1091 for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ )
1093 if( nPage == nAllPages-1 )
1094 i_rController.setLastPage( true );
1096 // get the page's metafile
1097 GDIMetaFile aPageFile;
1098 vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
1099 if( i_rController.isProgressCanceled() )
1101 bAborted = true;
1102 if( nPage != nAllPages-1 )
1104 i_rController.createProgressDialog();
1105 i_rController.setLastPage( true );
1106 i_rController.getFilteredPageFile( nPage, aPageFile );
1109 else
1111 xPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
1112 xPrinter->SetPaperSizeUser( aPageSize.aSize, true );
1113 PDFNewJobParameters aNewParm(xPrinter->GetPaperSize(), xPrinter->GetPaperBin());
1115 // create PDF writer on demand
1116 // either on first page
1117 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1118 // so we need to start a new job to get a new paper format from the printer
1119 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1120 if( ! xWriter ||
1121 (aNewParm != aLastParm && ! i_pFileName ) )
1123 if( xWriter )
1125 xWriter->Emit();
1127 // produce PDF file
1128 OUString aPDFUrl;
1129 if( i_pFileName )
1130 aPDFUrl = *i_pFileName;
1131 else
1132 osl_createTempFile( NULL, NULL, &aPDFUrl.pData );
1133 // normalize to file URL
1134 if( !aPDFUrl.startsWith( "file:" ) )
1136 // this is not a file URL, but it should
1137 // form it into a osl friendly file URL
1138 OUString aTmp;
1139 osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
1140 aPDFUrl = aTmp;
1142 // save current file and paper format
1143 aLastParm = aNewParm;
1144 aPDFFiles.push_back( PDFPrintFile( aPDFUrl, aNewParm ) );
1145 // update context
1146 aContext.URL = aPDFUrl;
1148 // create and initialize PDFWriter
1149 xWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) );
1152 xWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
1153 TenMuToPt( aNewParm.maPageSize.Height() ),
1154 vcl::PDFWriter::Portrait );
1156 xWriter->PlayMetafile( aPageFile, aMtfContext, NULL );
1160 // emit the last file
1161 if( xWriter )
1162 xWriter->Emit();
1164 // handle collate, copy count and multiple jobs correctly
1165 int nOuterJobs = 1;
1166 if( bSinglePrintJobs )
1168 nOuterJobs = nCopies;
1169 m_aJobData.m_nCopies = 1;
1171 else
1173 if( bCollate )
1175 if (aPDFFiles.size() == 1 && xPrinter->HasSupport(SUPPORT_COLLATECOPY))
1177 m_aJobData.setCollate( true );
1178 m_aJobData.m_nCopies = nCopies;
1180 else
1182 nOuterJobs = nCopies;
1183 m_aJobData.m_nCopies = 1;
1186 else
1188 m_aJobData.setCollate( false );
1189 m_aJobData.m_nCopies = nCopies;
1193 std::vector<OUString> aFaxNumbers;
1195 // check for fax numbers
1196 if (!bAborted && bFax)
1198 aFaxNumbers = getFaxNumbers();
1199 bAborted = aFaxNumbers.empty();
1202 bool bSuccess(true);
1203 // spool files
1204 if( ! i_pFileName && ! bAborted )
1208 OUString sFaxNumber;
1209 if (!aFaxNumbers.empty())
1211 sFaxNumber = aFaxNumbers.back();
1212 aFaxNumbers.pop_back();
1215 bool bFirstJob = true;
1216 for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ )
1218 for( size_t i = 0; i < aPDFFiles.size(); i++ )
1220 oslFileHandle pFile = NULL;
1221 osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
1222 if (pFile && (osl_setFilePos(pFile, osl_Pos_Absolut, 0) == osl_File_E_None))
1224 std::vector< char > buffer( 0x10000, 0 );
1225 // update job data with current page size
1226 Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
1227 m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
1228 // update job data with current paperbin
1229 m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
1231 // spool current file
1232 FILE* fp = PrinterInfoManager::get().startSpool(xPrinter->GetName(), i_rController.isDirectPrint());
1233 if( fp )
1235 sal_uInt64 nBytesRead = 0;
1238 osl_readFile( pFile, &buffer[0], buffer.size(), &nBytesRead );
1239 if( nBytesRead > 0 )
1241 size_t nBytesWritten = fwrite(&buffer[0], 1, nBytesRead, fp);
1242 OSL_ENSURE(nBytesRead == nBytesWritten, "short write");
1243 if (nBytesRead != nBytesWritten)
1244 break;
1246 } while( nBytesRead == buffer.size() );
1247 OUStringBuffer aBuf( i_rJobName.getLength() + 8 );
1248 aBuf.append( i_rJobName );
1249 if( i > 0 || nCurJob > 0 )
1251 aBuf.append( ' ' );
1252 aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
1254 bSuccess &=
1255 PrinterInfoManager::get().endSpool(xPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob, sFaxNumber);
1256 bFirstJob = false;
1259 osl_closeFile( pFile );
1263 while (!aFaxNumbers.empty());
1266 // job has been spooled
1267 i_rController.setJobState( (bAborted)
1268 ? view::PrintableState_JOB_ABORTED
1269 : ((bSuccess) ? view::PrintableState_JOB_SPOOLED
1270 : view::PrintableState_JOB_SPOOLING_FAILED));
1272 // clean up the temporary PDF files
1273 if( ! i_pFileName || bAborted )
1275 for( size_t i = 0; i < aPDFFiles.size(); i++ )
1277 osl_removeFile( aPDFFiles[i].maTmpURL.pData );
1278 OSL_TRACE( "removed print PDF file %s", OUStringToOString( aPDFFiles[i].maTmpURL, RTL_TEXTENCODING_UTF8 ).getStr() );
1282 return true;
1285 class PrinterUpdate
1287 static Idle* pPrinterUpdateIdle;
1288 static int nActiveJobs;
1290 static void doUpdate();
1291 DECL_STATIC_LINK_TYPED( PrinterUpdate, UpdateTimerHdl, Idle*, void );
1292 public:
1293 static void update(SalGenericInstance &rInstance);
1294 static void jobStarted() { nActiveJobs++; }
1295 static void jobEnded();
1298 Idle* PrinterUpdate::pPrinterUpdateIdle = NULL;
1299 int PrinterUpdate::nActiveJobs = 0;
1301 void PrinterUpdate::doUpdate()
1303 ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
1304 SalGenericInstance *pInst = static_cast<SalGenericInstance *>( GetSalData()->m_pInstance );
1305 if( pInst && rManager.checkPrintersChanged( false ) )
1306 pInst->PostPrintersChanged();
1309 IMPL_STATIC_LINK_NOARG_TYPED( PrinterUpdate, UpdateTimerHdl, Idle*, void )
1311 if( nActiveJobs < 1 )
1313 doUpdate();
1314 delete pPrinterUpdateIdle;
1315 pPrinterUpdateIdle = NULL;
1317 else
1318 pPrinterUpdateIdle->Start();
1321 void PrinterUpdate::update(SalGenericInstance &rInstance)
1323 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1324 return;
1326 if( ! rInstance.isPrinterInit() )
1328 // #i45389# start background printer detection
1329 psp::PrinterInfoManager::get();
1330 return;
1333 if( nActiveJobs < 1 )
1334 doUpdate();
1335 else if( ! pPrinterUpdateIdle )
1337 pPrinterUpdateIdle = new Idle();
1338 pPrinterUpdateIdle->SetPriority( SchedulerPriority::LOWEST );
1339 pPrinterUpdateIdle->SetIdleHdl( LINK( NULL, PrinterUpdate, UpdateTimerHdl ) );
1340 pPrinterUpdateIdle->Start();
1344 void SalGenericInstance::updatePrinterUpdate()
1346 PrinterUpdate::update(*this);
1349 void SalGenericInstance::jobStartedPrinterUpdate()
1351 PrinterUpdate::jobStarted();
1354 void PrinterUpdate::jobEnded()
1356 nActiveJobs--;
1357 if( nActiveJobs < 1 )
1359 if( pPrinterUpdateIdle )
1361 pPrinterUpdateIdle->Stop();
1362 delete pPrinterUpdateIdle;
1363 pPrinterUpdateIdle = NULL;
1364 doUpdate();
1369 void SalGenericInstance::jobEndedPrinterUpdate()
1371 PrinterUpdate::jobEnded();
1374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */