Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / generic / print / genprnpsp.cxx
blob7b479816e1ecab91ecad44c22f390d2cccd6f586
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 underlying 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 #include <unistd.h>
34 #include <sys/wait.h>
35 #include <sys/stat.h>
37 #include <comphelper/fileurl.hxx>
38 #include <rtl/ustring.hxx>
39 #include <sal/log.hxx>
41 #include <vcl/gdimtf.hxx>
42 #include <vcl/idle.hxx>
43 #include <vcl/print.hxx>
44 #include <vcl/pdfwriter.hxx>
45 #include <printerinfomanager.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/settings.hxx>
48 #include <vcl/weld.hxx>
49 #include <strings.hrc>
50 #include <saldatabasic.hxx>
51 #include <unx/genprn.h>
52 #include <unx/geninst.h>
53 #include <unx/genpspgraphics.h>
55 #include <jobset.h>
56 #include <print.h>
57 #include "prtsetup.hxx"
58 #include <salptype.hxx>
60 #include <com/sun/star/beans/PropertyValue.hpp>
62 using namespace psp;
63 using namespace com::sun::star;
65 static bool getPdfDir( const PrinterInfo& rInfo, OUString &rDir )
67 sal_Int32 nIndex = 0;
68 while( nIndex != -1 )
70 OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
71 if( aToken.startsWith( "pdf=" ) )
73 sal_Int32 nPos = 0;
74 rDir = aToken.getToken( 1, '=', nPos );
75 if( rDir.isEmpty() && getenv( "HOME" ) )
76 rDir = OUString( getenv( "HOME" ), strlen( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
77 return true;
80 return false;
83 namespace
85 class QueryString : public weld::GenericDialogController
87 private:
88 OUString& m_rReturnValue;
90 std::unique_ptr<weld::Button> m_xOKButton;
91 std::unique_ptr<weld::Label> m_xFixedText;
92 std::unique_ptr<weld::Entry> m_xEdit;
94 DECL_LINK( ClickBtnHdl, weld::Button&, void );
96 public:
97 // parent window, Query text, initial value
98 QueryString(weld::Window*, OUString const &, OUString &);
102 * QueryString
104 QueryString::QueryString(weld::Window* pParent, OUString const & rQuery, OUString& rRet)
105 : GenericDialogController(pParent, "vcl/ui/querydialog.ui", "QueryDialog")
106 , m_rReturnValue( rRet )
107 , m_xOKButton(m_xBuilder->weld_button("ok"))
108 , m_xFixedText(m_xBuilder->weld_label("label"))
109 , m_xEdit(m_xBuilder->weld_entry("entry"))
111 m_xOKButton->connect_clicked(LINK(this, QueryString, ClickBtnHdl));
112 m_xFixedText->set_label(rQuery);
113 m_xEdit->set_text(m_rReturnValue);
114 m_xDialog->set_title(rQuery);
117 IMPL_LINK(QueryString, ClickBtnHdl, weld::Button&, rButton, void)
119 if (&rButton == m_xOKButton.get())
121 m_rReturnValue = m_xEdit->get_text();
122 m_xDialog->response(RET_OK);
124 else
125 m_xDialog->response(RET_CANCEL);
128 int QueryFaxNumber(OUString& rNumber)
130 vcl::Window* pWin = Application::GetDefDialogParent();
131 QueryString aQuery(pWin ? pWin->GetFrameWeld() : nullptr, VclResId(SV_PRINT_QUERYFAXNUMBER_TXT), rNumber);
132 return aQuery.run();
136 static int PtTo10Mu( int nPoints ) { return static_cast<int>((static_cast<double>(nPoints)*35.27777778)+0.5); }
138 static int TenMuToPt( int nUnits ) { return static_cast<int>((static_cast<double>(nUnits)/35.27777778)+0.5); }
140 static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
142 pJobSetup->SetOrientation( rData.m_eOrientation == orientation::Landscape ?
143 Orientation::Landscape : Orientation::Portrait );
145 // copy page size
146 OUString aPaper;
147 int width, height;
149 rData.m_aContext.getPageSize( aPaper, width, height );
150 pJobSetup->SetPaperFormat( PaperInfo::fromPSName(
151 OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 )));
153 pJobSetup->SetPaperWidth( 0 );
154 pJobSetup->SetPaperHeight( 0 );
155 if( pJobSetup->GetPaperFormat() == PAPER_USER )
157 // transform to 100dth mm
158 width = PtTo10Mu( width );
159 height = PtTo10Mu( height );
161 if( rData.m_eOrientation == psp::orientation::Portrait )
163 pJobSetup->SetPaperWidth( width );
164 pJobSetup->SetPaperHeight( height );
166 else
168 pJobSetup->SetPaperWidth( height );
169 pJobSetup->SetPaperHeight( width );
173 // copy input slot
174 const PPDKey* pKey = nullptr;
175 const PPDValue* pValue = nullptr;
177 pJobSetup->SetPaperBin( 0 );
178 if( rData.m_pParser )
179 pKey = rData.m_pParser->getKey( "InputSlot" );
180 if( pKey )
181 pValue = rData.m_aContext.getValue( pKey );
182 if( pKey && pValue )
184 int nPaperBin;
185 for( nPaperBin = 0;
186 pValue != pKey->getValue( nPaperBin ) &&
187 nPaperBin < pKey->countValues();
188 nPaperBin++);
189 pJobSetup->SetPaperBin(
190 nPaperBin == pKey->countValues() ? 0 : nPaperBin);
193 // copy duplex
194 pKey = nullptr;
195 pValue = nullptr;
197 pJobSetup->SetDuplexMode( DuplexMode::Unknown );
198 if( rData.m_pParser )
199 pKey = rData.m_pParser->getKey( "Duplex" );
200 if( pKey )
201 pValue = rData.m_aContext.getValue( pKey );
202 if( pKey && pValue )
204 if( pValue->m_aOption.equalsIgnoreAsciiCase( "None" ) ||
205 pValue->m_aOption.startsWithIgnoreAsciiCase( "Simplex" )
208 pJobSetup->SetDuplexMode( DuplexMode::Off);
210 else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexNoTumble" ) )
212 pJobSetup->SetDuplexMode( DuplexMode::LongEdge );
214 else if( pValue->m_aOption.equalsIgnoreAsciiCase( "DuplexTumble" ) )
216 pJobSetup->SetDuplexMode( DuplexMode::ShortEdge );
220 // copy the whole context
221 if( pJobSetup->GetDriverData() )
222 std::free( const_cast<sal_uInt8*>(pJobSetup->GetDriverData()) );
224 sal_uInt32 nBytes;
225 void* pBuffer = nullptr;
226 if( rData.getStreamBuffer( pBuffer, nBytes ) )
228 pJobSetup->SetDriverDataLen( nBytes );
229 pJobSetup->SetDriverData( static_cast<sal_uInt8*>(pBuffer) );
231 else
233 pJobSetup->SetDriverDataLen( 0 );
234 pJobSetup->SetDriverData( nullptr );
236 pJobSetup->SetPapersizeFromSetup( rData.m_bPapersizeFromSetup );
239 // Needs a cleaner abstraction ...
240 static bool passFileToCommandLine( const OUString& rFilename, const OUString& rCommandLine )
242 bool bSuccess = false;
244 rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
245 OString aCmdLine(OUStringToOString(rCommandLine, aEncoding));
246 OString aFilename(OUStringToOString(rFilename, aEncoding));
248 bool bPipe = aCmdLine.indexOf( "(TMP)" ) == -1;
250 // setup command line for exec
251 if( ! bPipe )
252 aCmdLine = aCmdLine.replaceAll("(TMP)", aFilename);
254 #if OSL_DEBUG_LEVEL > 1
255 fprintf( stderr, "%s commandline: \"%s\"\n",
256 bPipe ? "piping to" : "executing",
257 aCmdLine.getStr() );
258 struct stat aStat;
259 if( stat( aFilename.getStr(), &aStat ) )
260 fprintf( stderr, "stat( %s ) failed\n", aFilename.getStr() );
261 fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.getStr(), (long)aStat.st_mode );
262 #endif
263 const char* argv[4];
264 if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) )
265 argv[ 0 ] = "/bin/sh";
266 argv[ 1 ] = "-c";
267 argv[ 2 ] = aCmdLine.getStr();
268 argv[ 3 ] = nullptr;
270 bool bHavePipes = false;
271 int pid, fd[2];
273 if( bPipe )
274 bHavePipes = pipe( fd ) == 0;
275 if( ( pid = fork() ) > 0 )
277 if( bPipe && bHavePipes )
279 close( fd[0] );
280 char aBuffer[ 2048 ];
281 FILE* fp = fopen( aFilename.getStr(), "r" );
282 while (fp && !feof(fp))
284 size_t nBytesRead = fread(aBuffer, 1, sizeof( aBuffer ), fp);
285 if (nBytesRead )
287 size_t nBytesWritten = write(fd[1], aBuffer, nBytesRead);
288 OSL_ENSURE(nBytesWritten == nBytesRead, "short write");
289 if (nBytesWritten != nBytesRead)
290 break;
293 fclose( fp );
294 close( fd[ 1 ] );
296 int status = 0;
297 if(waitpid( pid, &status, 0 ) != -1)
299 if( ! status )
300 bSuccess = true;
303 else if( ! pid )
305 if( bPipe && bHavePipes )
307 close( fd[1] );
308 if( fd[0] != STDIN_FILENO ) // not probable, but who knows :)
309 dup2( fd[0], STDIN_FILENO );
311 execv( argv[0], const_cast<char**>(argv) );
312 fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.getStr() );
313 _exit( 1 );
315 else
316 fprintf( stderr, "failed to fork\n" );
318 // clean up the mess
319 unlink( aFilename.getStr() );
321 return bSuccess;
324 static std::vector<OUString> getFaxNumbers()
326 std::vector<OUString> aFaxNumbers;
328 OUString aNewNr;
329 if (QueryFaxNumber(aNewNr))
331 for (sal_Int32 nIndex {0}; nIndex >= 0; )
332 aFaxNumbers.push_back(aNewNr.getToken( 0, ';', nIndex ));
335 return aFaxNumbers;
338 static bool createPdf( const OUString& rToFile, const OUString& rFromFile, const OUString& rCommandLine )
340 return passFileToCommandLine( rFromFile, rCommandLine.replaceAll("(OUTFILE)", rToFile) );
344 * SalInstance
347 void SalGenericInstance::configurePspInfoPrinter(PspSalInfoPrinter *pPrinter,
348 SalPrinterQueueInfo const * pQueueInfo, ImplJobSetup* pJobSetup)
350 if( pJobSetup )
352 PrinterInfoManager& rManager( PrinterInfoManager::get() );
353 PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
354 pPrinter->m_aJobData = aInfo;
355 pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData );
357 if( pJobSetup->GetDriverData() )
358 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(),
359 pJobSetup->GetDriverDataLen(), aInfo );
361 pJobSetup->SetSystem( JOBSETUP_SYSTEM_UNIX );
362 pJobSetup->SetPrinterName( pQueueInfo->maPrinterName );
363 pJobSetup->SetDriver( aInfo.m_aDriverName );
364 copyJobDataToJobSetup( pJobSetup, aInfo );
368 SalInfoPrinter* SalGenericInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
369 ImplJobSetup* pJobSetup )
371 mbPrinterInit = true;
372 // create and initialize SalInfoPrinter
373 PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter();
374 configurePspInfoPrinter(pPrinter, pQueueInfo, pJobSetup);
375 return pPrinter;
378 void SalGenericInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
380 delete pPrinter;
383 std::unique_ptr<SalPrinter> SalGenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
385 mbPrinterInit = true;
386 // create and initialize SalPrinter
387 PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
388 pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
390 return std::unique_ptr<SalPrinter>(pPrinter);
393 void SalGenericInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
395 mbPrinterInit = true;
396 PrinterInfoManager& rManager( PrinterInfoManager::get() );
397 static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
398 if( ! pNoSyncDetection || ! *pNoSyncDetection )
400 // #i62663# synchronize possible asynchronouse printer detection now
401 rManager.checkPrintersChanged( true );
403 ::std::vector< OUString > aPrinters;
404 rManager.listPrinters( aPrinters );
406 for (auto const& printer : aPrinters)
408 const PrinterInfo& rInfo( rManager.getPrinterInfo(printer) );
409 // create new entry
410 std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
411 pInfo->maPrinterName = printer;
412 pInfo->maDriver = rInfo.m_aDriverName;
413 pInfo->maLocation = rInfo.m_aLocation;
414 pInfo->maComment = rInfo.m_aComment;
416 OUString sPdfDir;
417 if (getPdfDir(rInfo, sPdfDir))
418 pInfo->maLocation = sPdfDir;
420 pList->Add( std::move(pInfo) );
424 void SalGenericInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
426 mbPrinterInit = true;
429 OUString SalGenericInstance::GetDefaultPrinter()
431 mbPrinterInit = true;
432 PrinterInfoManager& rManager( PrinterInfoManager::get() );
433 return rManager.getDefaultPrinter();
436 PspSalInfoPrinter::PspSalInfoPrinter()
440 PspSalInfoPrinter::~PspSalInfoPrinter()
444 void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
446 m_aPaperFormats.clear();
447 m_bPapersInit = true;
449 if( m_aJobData.m_pParser )
451 const PPDKey* pKey = m_aJobData.m_pParser->getKey( "PageSize" );
452 if( pKey )
454 int nValues = pKey->countValues();
455 for( int i = 0; i < nValues; i++ )
457 const PPDValue* pValue = pKey->getValue( i );
458 int nWidth = 0, nHeight = 0;
459 m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
460 PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
461 m_aPaperFormats.push_back( aInfo );
467 int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
469 return 900;
472 SalGraphics* PspSalInfoPrinter::AcquireGraphics()
474 // return a valid pointer only once
475 // the reasoning behind this is that we could have different
476 // SalGraphics that can run in multiple threads
477 // (future plans)
478 SalGraphics* pRet = nullptr;
479 if( ! m_pGraphics )
481 m_pGraphics = GetGenericInstance()->CreatePrintGraphics();
482 m_pGraphics->Init(&m_aJobData, &m_aPrinterGfx);
483 pRet = m_pGraphics.get();
485 return pRet;
488 void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
490 if( m_pGraphics.get() == pGraphics )
492 m_pGraphics.reset();
496 bool PspSalInfoPrinter::Setup( weld::Window* pFrame, ImplJobSetup* pJobSetup )
498 if( ! pFrame || ! pJobSetup )
499 return false;
501 PrinterInfoManager& rManager = PrinterInfoManager::get();
503 PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->GetPrinterName() ) );
504 if ( pJobSetup->GetDriverData() )
506 SetData( JobSetFlags::ALL, pJobSetup );
507 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aInfo );
509 aInfo.m_bPapersizeFromSetup = pJobSetup->GetPapersizeFromSetup();
510 aInfo.meSetupMode = pJobSetup->GetPrinterSetupMode();
512 if (SetupPrinterDriver(pFrame, aInfo))
514 aInfo.resolveDefaultBackend();
515 std::free( const_cast<sal_uInt8*>(pJobSetup->GetDriverData()) );
516 pJobSetup->SetDriverData( nullptr );
518 sal_uInt32 nBytes;
519 void* pBuffer = nullptr;
520 aInfo.getStreamBuffer( pBuffer, nBytes );
521 pJobSetup->SetDriverDataLen( nBytes );
522 pJobSetup->SetDriverData( static_cast<sal_uInt8*>(pBuffer) );
524 // copy everything to job setup
525 copyJobDataToJobSetup( pJobSetup, aInfo );
526 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), m_aJobData );
527 return true;
529 return false;
532 // This function gets the driver data and puts it into pJobSetup
533 // If pJobSetup->GetDriverData() is NOT NULL, then the independent
534 // data should be merged into the driver data
535 // If pJobSetup->GetDriverData() IS NULL, then the driver defaults
536 // should be merged into the independent data
537 bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
539 if( pJobSetup->GetDriverData() )
540 return SetData( JobSetFlags::ALL, pJobSetup );
542 copyJobDataToJobSetup( pJobSetup, m_aJobData );
544 return true;
547 // This function merges the independent driver data
548 // and sets the new independent data in pJobSetup
549 // Only the data must be changed, where the bit
550 // in nGetDataFlags is set
551 bool PspSalInfoPrinter::SetData(
552 JobSetFlags nSetDataFlags,
553 ImplJobSetup* pJobSetup )
555 JobData aData;
556 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
558 if( aData.m_pParser )
560 const PPDKey* pKey;
561 const PPDValue* pValue;
563 // merge papersize if necessary
564 if( nSetDataFlags & JobSetFlags::PAPERSIZE )
566 OUString aPaper;
568 if( pJobSetup->GetPaperFormat() == PAPER_USER )
569 aPaper = aData.m_pParser->matchPaper(
570 TenMuToPt( pJobSetup->GetPaperWidth() ),
571 TenMuToPt( pJobSetup->GetPaperHeight() ) );
572 else
573 aPaper = OStringToOUString(PaperInfo::toPSName(pJobSetup->GetPaperFormat()), RTL_TEXTENCODING_ISO_8859_1);
575 pKey = aData.m_pParser->getKey( "PageSize" );
576 pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : nullptr;
578 // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
579 // try to find the correct paper anyway using the size
580 if( pKey && ! pValue && pJobSetup->GetPaperFormat() != PAPER_USER )
582 PaperInfo aInfo( pJobSetup->GetPaperFormat() );
583 aPaper = aData.m_pParser->matchPaper(
584 TenMuToPt( aInfo.getWidth() ),
585 TenMuToPt( aInfo.getHeight() ) );
586 pValue = pKey->getValueCaseInsensitive( aPaper );
589 if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue ) == pValue ) )
590 return false;
593 // merge paperbin if necessary
594 if( nSetDataFlags & JobSetFlags::PAPERBIN )
596 pKey = aData.m_pParser->getKey( "InputSlot" );
597 if( pKey )
599 int nPaperBin = pJobSetup->GetPaperBin();
600 if( nPaperBin >= pKey->countValues() )
601 pValue = pKey->getDefaultValue();
602 else
603 pValue = pKey->getValue( pJobSetup->GetPaperBin() );
605 // may fail due to constraints;
606 // real paper bin is copied back to jobsetup in that case
607 aData.m_aContext.setValue( pKey, pValue );
609 // if printer has no InputSlot key simply ignore this setting
610 // (e.g. SGENPRT has no InputSlot)
613 // merge orientation if necessary
614 if( nSetDataFlags & JobSetFlags::ORIENTATION )
615 aData.m_eOrientation = pJobSetup->GetOrientation() == Orientation::Landscape ? orientation::Landscape : orientation::Portrait;
617 // merge duplex if necessary
618 if( nSetDataFlags & JobSetFlags::DUPLEXMODE )
620 pKey = aData.m_pParser->getKey( "Duplex" );
621 if( pKey )
623 pValue = nullptr;
624 switch( pJobSetup->GetDuplexMode() )
626 case DuplexMode::Off:
627 pValue = pKey->getValue( "None" );
628 if( pValue == nullptr )
629 pValue = pKey->getValue( "SimplexNoTumble" );
630 break;
631 case DuplexMode::ShortEdge:
632 pValue = pKey->getValue( "DuplexTumble" );
633 break;
634 case DuplexMode::LongEdge:
635 pValue = pKey->getValue( "DuplexNoTumble" );
636 break;
637 case DuplexMode::Unknown:
638 default:
639 pValue = nullptr;
640 break;
642 if( ! pValue )
643 pValue = pKey->getDefaultValue();
644 aData.m_aContext.setValue( pKey, pValue );
647 aData.m_bPapersizeFromSetup = pJobSetup->GetPapersizeFromSetup();
649 m_aJobData = aData;
650 copyJobDataToJobSetup( pJobSetup, aData );
651 return true;
654 return false;
657 void PspSalInfoPrinter::GetPageInfo(
658 const ImplJobSetup* pJobSetup,
659 long& rOutWidth, long& rOutHeight,
660 Point& rPageOffset,
661 Size& rPaperSize )
663 if( ! pJobSetup )
664 return;
666 JobData aData;
667 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
669 // get the selected page size
670 if( !aData.m_pParser )
671 return;
674 OUString aPaper;
675 int width, height;
676 int left = 0, top = 0, right = 0, bottom = 0;
677 int nDPI = aData.m_aContext.getRenderResolution();
679 if( aData.m_eOrientation == psp::orientation::Portrait )
681 aData.m_aContext.getPageSize( aPaper, width, height );
682 aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
684 else
686 aData.m_aContext.getPageSize( aPaper, height, width );
687 aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
690 rPaperSize.setWidth( width * nDPI / 72 );
691 rPaperSize.setHeight( height * nDPI / 72 );
692 rPageOffset.setX( left * nDPI / 72 );
693 rPageOffset.setY( top * nDPI / 72 );
694 rOutWidth = ( width - left - right ) * nDPI / 72;
695 rOutHeight = ( height - top - bottom ) * nDPI / 72;
699 sal_uInt16 PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
701 if( ! pJobSetup )
702 return 0;
704 JobData aData;
705 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
707 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( "InputSlot" ): nullptr;
708 return pKey ? pKey->countValues() : 0;
711 OUString PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uInt16 nPaperBin )
713 JobData aData;
714 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
716 if( aData.m_pParser )
718 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( "InputSlot" ): nullptr;
719 if( ! pKey || nPaperBin >= static_cast<sal_uInt16>(pKey->countValues()) )
720 return aData.m_pParser->getDefaultInputSlot();
721 const PPDValue* pValue = pKey->getValue( nPaperBin );
722 if( pValue )
723 return aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
726 return OUString();
729 sal_uInt32 PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, PrinterCapType nType )
731 switch( nType )
733 case PrinterCapType::SupportDialog:
734 return 1;
735 case PrinterCapType::Copies:
736 return 0xffff;
737 case PrinterCapType::CollateCopies:
739 // PPDs don't mention the number of possible collated copies.
740 // so let's guess as many as we want ?
741 return 0xffff;
743 case PrinterCapType::SetOrientation:
744 return 1;
745 case PrinterCapType::SetPaperSize:
746 return 1;
747 case PrinterCapType::SetPaper:
748 return 0;
749 case PrinterCapType::Fax:
751 // see if the PPD contains the fax4CUPS "Dial" option and that it's not set
752 // to "manually"
753 JobData aData = PrinterInfoManager::get().getPrinterInfo(pJobSetup->GetPrinterName());
754 if( pJobSetup->GetDriverData() )
755 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
756 const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey("Dial") : nullptr;
757 const PPDValue* pValue = pKey ? aData.m_aContext.getValue(pKey) : nullptr;
758 if (pValue && !pValue->m_aOption.equalsIgnoreAsciiCase("Manually"))
759 return 1;
760 return 0;
763 case PrinterCapType::PDF:
764 if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->GetPrinterName(), "pdf" ) )
765 return 1;
766 else
768 // see if the PPD contains a value to set PDF device
769 JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->GetPrinterName() );
770 if( pJobSetup->GetDriverData() )
771 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
772 return aData.m_nPDFDevice > 0 ? 1 : 0;
774 case PrinterCapType::ExternalDialog:
775 return PrinterInfoManager::get().checkFeatureToken( pJobSetup->GetPrinterName(), "external_dialog" ) ? 1 : 0;
776 case PrinterCapType::UsePullModel:
778 // see if the PPD contains a value to set PDF device
779 JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->GetPrinterName() );
780 if( pJobSetup->GetDriverData() )
781 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), aData );
782 return aData.m_nPDFDevice > 0 ? 1 : 0;
784 default: break;
786 return 0;
790 * SalPrinter
792 PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
793 : m_pInfoPrinter( pInfoPrinter )
794 , m_nCopies( 1 )
795 , m_bCollate( false )
796 , m_bPdf( false )
797 , m_bIsPDFWriterJob( false )
801 PspSalPrinter::~PspSalPrinter()
805 static OUString getTmpName()
807 OUString aTmp, aSys;
808 osl_createTempFile( nullptr, nullptr, &aTmp.pData );
809 osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData );
811 return aSys;
814 bool PspSalPrinter::StartJob(
815 const OUString* pFileName,
816 const OUString& rJobName,
817 const OUString& rAppName,
818 sal_uInt32 nCopies,
819 bool bCollate,
820 bool bDirect,
821 ImplJobSetup* pJobSetup )
823 SAL_INFO( "vcl.unx.print", "PspSalPrinter::StartJob");
824 GetSalData()->m_pInstance->jobStartedPrinterUpdate();
825 m_bPdf = false;
826 if (pFileName)
827 m_aFileName = *pFileName;
828 else
829 m_aFileName.clear();
830 m_aTmpFile.clear();
831 m_nCopies = nCopies;
832 m_bCollate = bCollate;
834 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), m_aJobData );
835 if( m_nCopies > 1 )
837 // in case user did not do anything (m_nCopies=1)
838 // take the default from jobsetup
839 m_aJobData.m_nCopies = m_nCopies;
840 m_aJobData.setCollate( bCollate );
843 int nMode = 0;
844 // check whether this printer is configured as fax
845 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
846 OUString sPdfDir;
847 if (getPdfDir(rInfo, sPdfDir))
849 m_bPdf = true;
850 m_aTmpFile = getTmpName();
851 nMode = S_IRUSR | S_IWUSR;
853 if( m_aFileName.isEmpty() )
854 m_aFileName = sPdfDir + "/" + rJobName + ".pdf";
856 m_aPrinterGfx.Init( m_aJobData );
858 return m_aPrintJob.StartJob( ! m_aTmpFile.isEmpty() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect );
861 bool PspSalPrinter::EndJob()
863 bool bSuccess = false;
864 if( m_bIsPDFWriterJob )
865 bSuccess = true;
866 else
868 bSuccess = m_aPrintJob.EndJob();
869 SAL_INFO( "vcl.unx.print", "PspSalPrinter::EndJob " << bSuccess);
871 if( bSuccess && m_bPdf )
873 const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
874 bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand );
877 GetSalData()->m_pInstance->jobEndedPrinterUpdate();
878 return bSuccess;
881 SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, bool )
883 SAL_INFO( "vcl.unx.print", "PspSalPrinter::StartPage");
885 JobData::constructFromStreamBuffer( pJobSetup->GetDriverData(), pJobSetup->GetDriverDataLen(), m_aJobData );
886 m_xGraphics = GetGenericInstance()->CreatePrintGraphics();
887 m_xGraphics->Init(&m_aJobData, &m_aPrinterGfx);
889 if( m_nCopies > 1 )
891 // in case user did not do anything (m_nCopies=1)
892 // take the default from jobsetup
893 m_aJobData.m_nCopies = m_nCopies;
894 m_aJobData.setCollate( m_nCopies > 1 && m_bCollate );
897 m_aPrintJob.StartPage( m_aJobData );
898 m_aPrinterGfx.Init( m_aPrintJob );
900 return m_xGraphics.get();
903 void PspSalPrinter::EndPage()
905 m_aPrintJob.EndPage();
906 m_aPrinterGfx.Clear();
907 SAL_INFO( "vcl.unx.print", "PspSalPrinter::EndPage");
910 struct PDFNewJobParameters
912 Size maPageSize;
913 sal_uInt16 mnPaperBin;
915 PDFNewJobParameters( const Size& i_rSize = Size(),
916 sal_uInt16 i_nPaperBin = 0xffff )
917 : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
919 bool operator==(const PDFNewJobParameters& rComp ) const
921 const long nRotatedWidth = rComp.maPageSize.Height();
922 const long nRotatedHeight = rComp.maPageSize.Width();
923 Size aCompLSSize(nRotatedWidth, nRotatedHeight);
924 return
925 (maPageSize == rComp.maPageSize || maPageSize == aCompLSSize)
926 && mnPaperBin == rComp.mnPaperBin
930 bool operator!=(const PDFNewJobParameters& rComp) const
932 return ! operator==(rComp);
936 struct PDFPrintFile
938 OUString const maTmpURL;
939 PDFNewJobParameters const maParameters;
941 PDFPrintFile( const OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters )
942 : maTmpURL( i_rURL )
943 , maParameters( i_rNewParameters ) {}
946 bool PspSalPrinter::StartJob( const OUString* i_pFileName, const OUString& i_rJobName, const OUString& i_rAppName,
947 ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
949 SAL_INFO( "vcl.unx.print", "StartJob with controller: pFilename = " << (i_pFileName ? *i_pFileName : "<nil>") );
950 // mark for endjob
951 m_bIsPDFWriterJob = true;
952 // reset IsLastPage
953 i_rController.setLastPage( false );
954 // is this a fax device
955 bool bFax = m_pInfoPrinter->GetCapabilities(i_pSetupData, PrinterCapType::Fax) == 1;
957 // update job data
958 if( i_pSetupData )
959 JobData::constructFromStreamBuffer( i_pSetupData->GetDriverData(), i_pSetupData->GetDriverDataLen(), m_aJobData );
961 OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 );
962 m_aJobData.m_nPDFDevice = 1;
964 // possibly create one job for collated output
965 bool bSinglePrintJobs = false;
966 beans::PropertyValue* pSingleValue = i_rController.getValue( "PrintCollateAsSingleJobs" );
967 if( pSingleValue )
969 pSingleValue->Value >>= bSinglePrintJobs;
972 int nCopies = i_rController.getPrinter()->GetCopyCount();
973 bool bCollate = i_rController.getPrinter()->IsCollateCopy();
975 // notify start of real print job
976 i_rController.jobStarted();
978 // setup PDFWriter context
979 vcl::PDFWriter::PDFWriterContext aContext;
980 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_4;
981 aContext.Tagged = false;
982 aContext.DocumentLocale = Application::GetSettings().GetLanguageTag().getLocale();
983 aContext.ColorMode = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
984 ? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor;
986 // prepare doc info
987 aContext.DocumentInfo.Title = i_rJobName;
988 aContext.DocumentInfo.Creator = i_rAppName;
989 aContext.DocumentInfo.Producer = i_rAppName;
991 // define how we handle metafiles in PDFWriter
992 vcl::PDFWriter::PlayMetafileContext aMtfContext;
993 aMtfContext.m_bOnlyLosslessCompression = true;
995 std::shared_ptr<vcl::PDFWriter> xWriter;
996 std::vector< PDFPrintFile > aPDFFiles;
997 VclPtr<Printer> xPrinter( i_rController.getPrinter() );
998 int nAllPages = i_rController.getFilteredPageCount();
999 i_rController.createProgressDialog();
1000 bool bAborted = false;
1001 PDFNewJobParameters aLastParm;
1003 aContext.DPIx = xPrinter->GetDPIX();
1004 aContext.DPIy = xPrinter->GetDPIY();
1005 for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ )
1007 if( nPage == nAllPages-1 )
1008 i_rController.setLastPage( true );
1010 // get the page's metafile
1011 GDIMetaFile aPageFile;
1012 vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
1013 if( i_rController.isProgressCanceled() )
1015 bAborted = true;
1016 if( nPage != nAllPages-1 )
1018 i_rController.createProgressDialog();
1019 i_rController.setLastPage( true );
1020 i_rController.getFilteredPageFile( nPage, aPageFile );
1023 else
1025 xPrinter->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1026 xPrinter->SetPaperSizeUser( aPageSize.aSize, true );
1027 PDFNewJobParameters aNewParm(xPrinter->GetPaperSize(), xPrinter->GetPaperBin());
1029 // create PDF writer on demand
1030 // either on first page
1031 // or on paper format change - cups does not support multiple paper formats per job (yet?)
1032 // so we need to start a new job to get a new paper format from the printer
1033 // orientation switches (that is switch of height and width) is handled transparently by CUPS
1034 if( ! xWriter ||
1035 (aNewParm != aLastParm && ! i_pFileName ) )
1037 if( xWriter )
1039 xWriter->Emit();
1041 // produce PDF file
1042 OUString aPDFUrl;
1043 if( i_pFileName )
1044 aPDFUrl = *i_pFileName;
1045 else
1046 osl_createTempFile( nullptr, nullptr, &aPDFUrl.pData );
1047 // normalize to file URL
1048 if( !comphelper::isFileUrl(aPDFUrl) )
1050 // this is not a file URL, but it should
1051 // form it into an osl friendly file URL
1052 OUString aTmp;
1053 osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
1054 aPDFUrl = aTmp;
1056 // save current file and paper format
1057 aLastParm = aNewParm;
1058 aPDFFiles.emplace_back( aPDFUrl, aNewParm );
1059 // update context
1060 aContext.URL = aPDFUrl;
1062 // create and initialize PDFWriter
1063 xWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) );
1066 xWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
1067 TenMuToPt( aNewParm.maPageSize.Height() ),
1068 vcl::PDFWriter::Orientation::Portrait );
1070 xWriter->PlayMetafile( aPageFile, aMtfContext );
1074 // emit the last file
1075 if( xWriter )
1076 xWriter->Emit();
1078 // handle collate, copy count and multiple jobs correctly
1079 int nOuterJobs = 1;
1080 if( bSinglePrintJobs )
1082 nOuterJobs = nCopies;
1083 m_aJobData.m_nCopies = 1;
1085 else
1087 if( bCollate )
1089 if (aPDFFiles.size() == 1 && xPrinter->HasSupport(PrinterSupport::CollateCopy))
1091 m_aJobData.setCollate( true );
1092 m_aJobData.m_nCopies = nCopies;
1094 else
1096 nOuterJobs = nCopies;
1097 m_aJobData.m_nCopies = 1;
1100 else
1102 m_aJobData.setCollate( false );
1103 m_aJobData.m_nCopies = nCopies;
1107 std::vector<OUString> aFaxNumbers;
1109 // check for fax numbers
1110 if (!bAborted && bFax)
1112 aFaxNumbers = getFaxNumbers();
1113 bAborted = aFaxNumbers.empty();
1116 bool bSuccess(true);
1117 // spool files
1118 if( ! i_pFileName && ! bAborted )
1122 OUString sFaxNumber;
1123 if (!aFaxNumbers.empty())
1125 sFaxNumber = aFaxNumbers.back();
1126 aFaxNumbers.pop_back();
1129 bool bFirstJob = true;
1130 for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ )
1132 for( size_t i = 0; i < aPDFFiles.size(); i++ )
1134 oslFileHandle pFile = nullptr;
1135 osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
1136 if (pFile && (osl_setFilePos(pFile, osl_Pos_Absolut, 0) == osl_File_E_None))
1138 std::vector< char > buffer( 0x10000, 0 );
1139 // update job data with current page size
1140 Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
1141 m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
1142 // update job data with current paperbin
1143 m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
1145 // spool current file
1146 FILE* fp = PrinterInfoManager::get().startSpool(xPrinter->GetName(), i_rController.isDirectPrint());
1147 if( fp )
1149 sal_uInt64 nBytesRead = 0;
1152 osl_readFile( pFile, buffer.data(), buffer.size(), &nBytesRead );
1153 if( nBytesRead > 0 )
1155 size_t nBytesWritten = fwrite(buffer.data(), 1, nBytesRead, fp);
1156 OSL_ENSURE(nBytesRead == nBytesWritten, "short write");
1157 if (nBytesRead != nBytesWritten)
1158 break;
1160 } while( nBytesRead == buffer.size() );
1161 OUStringBuffer aBuf( i_rJobName.getLength() + 8 );
1162 aBuf.append( i_rJobName );
1163 if( i > 0 || nCurJob > 0 )
1165 aBuf.append( ' ' );
1166 aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
1168 bSuccess &=
1169 PrinterInfoManager::get().endSpool(xPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob, sFaxNumber);
1170 bFirstJob = false;
1173 osl_closeFile( pFile );
1177 while (!aFaxNumbers.empty());
1180 // job has been spooled
1181 i_rController.setJobState( bAborted
1182 ? view::PrintableState_JOB_ABORTED
1183 : (bSuccess ? view::PrintableState_JOB_SPOOLED
1184 : view::PrintableState_JOB_SPOOLING_FAILED));
1186 // clean up the temporary PDF files
1187 if( ! i_pFileName || bAborted )
1189 for(PDFPrintFile & rPDFFile : aPDFFiles)
1191 osl_removeFile( rPDFFile.maTmpURL.pData );
1192 SAL_INFO( "vcl.unx.print", "removed print PDF file " << rPDFFile.maTmpURL );
1196 return true;
1199 class PrinterUpdate
1201 static Idle* pPrinterUpdateIdle;
1202 static int nActiveJobs;
1204 static void doUpdate();
1205 DECL_STATIC_LINK( PrinterUpdate, UpdateTimerHdl, Timer*, void );
1206 public:
1207 static void update(SalGenericInstance const &rInstance);
1208 static void jobStarted() { nActiveJobs++; }
1209 static void jobEnded();
1212 Idle* PrinterUpdate::pPrinterUpdateIdle = nullptr;
1213 int PrinterUpdate::nActiveJobs = 0;
1215 void PrinterUpdate::doUpdate()
1217 ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
1218 SalGenericInstance *pInst = static_cast<SalGenericInstance *>( GetSalData()->m_pInstance );
1219 if( pInst && rManager.checkPrintersChanged( false ) )
1220 pInst->PostPrintersChanged();
1223 IMPL_STATIC_LINK_NOARG( PrinterUpdate, UpdateTimerHdl, Timer*, void )
1225 if( nActiveJobs < 1 )
1227 doUpdate();
1228 delete pPrinterUpdateIdle;
1229 pPrinterUpdateIdle = nullptr;
1231 else
1232 pPrinterUpdateIdle->Start();
1235 void PrinterUpdate::update(SalGenericInstance const &rInstance)
1237 if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
1238 return;
1240 if( ! rInstance.isPrinterInit() )
1242 // #i45389# start background printer detection
1243 psp::PrinterInfoManager::get();
1244 return;
1247 if( nActiveJobs < 1 )
1248 doUpdate();
1249 else if( ! pPrinterUpdateIdle )
1251 pPrinterUpdateIdle = new Idle("PrinterUpdateTimer");
1252 pPrinterUpdateIdle->SetPriority( TaskPriority::LOWEST );
1253 pPrinterUpdateIdle->SetInvokeHandler( LINK( nullptr, PrinterUpdate, UpdateTimerHdl ) );
1254 pPrinterUpdateIdle->Start();
1258 void SalGenericInstance::updatePrinterUpdate()
1260 PrinterUpdate::update(*this);
1263 void SalGenericInstance::jobStartedPrinterUpdate()
1265 PrinterUpdate::jobStarted();
1268 void PrinterUpdate::jobEnded()
1270 nActiveJobs--;
1271 if( nActiveJobs < 1 )
1273 if( pPrinterUpdateIdle )
1275 pPrinterUpdateIdle->Stop();
1276 delete pPrinterUpdateIdle;
1277 pPrinterUpdateIdle = nullptr;
1278 doUpdate();
1283 void SalGenericInstance::jobEndedPrinterUpdate()
1285 PrinterUpdate::jobEnded();
1288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */