From 7012d882fe7b8a4ea2104cbcf81cb019bd54efe9 Mon Sep 17 00:00:00 2001 From: Luboš Luňák Date: Thu, 26 Feb 2015 15:56:24 +0100 Subject: support fast MM printing in non-single-file mode only for CUPS As said in the comment, the non-single-file mode could create way too many print jobs, so enable this only for the CUPS backend, which has been modified to send them as a single batch. Change-Id: I4c02ca0e8b91323b1d02f004c7b4813433064a11 --- config_host/config_cups.h.in | 6 ++ configure.ac | 3 +- include/vcl/jobdata.hxx | 6 ++ include/vcl/printerinfomanager.hxx | 10 +++ sw/source/ui/dbui/dbmgr.cxx | 55 +++++++++--- vcl/inc/cupsmgr.hxx | 23 +++++ vcl/unx/generic/printer/cupsmgr.cxx | 113 ++++++++++++++++++++----- vcl/unx/generic/printer/jobdata.cxx | 18 ++++ vcl/unx/generic/printer/printerinfomanager.cxx | 15 ++++ 9 files changed, 217 insertions(+), 32 deletions(-) create mode 100644 config_host/config_cups.h.in diff --git a/config_host/config_cups.h.in b/config_host/config_cups.h.in new file mode 100644 index 000000000000..6794703664d9 --- /dev/null +++ b/config_host/config_cups.h.in @@ -0,0 +1,6 @@ +#ifndef CONFIG_CUPS_H +#define CONFIG_CUPS_H + +#define ENABLE_CUPS 0 + +#endif diff --git a/configure.ac b/configure.ac index 0f492d217767..bb5f11146c74 100644 --- a/configure.ac +++ b/configure.ac @@ -4348,7 +4348,7 @@ if test "$test_cups" = "yes"; then if test "$ac_cv_lib_cups_cupsPrintFiles" != "yes" -a "$ac_cv_header_cups_cups_h" != "yes"; then AC_MSG_ERROR([Could not find CUPS. Install libcupsys2-dev or cups-devel.]) fi - + AC_DEFINE(ENABLE_CUPS) else AC_MSG_RESULT([no]) fi @@ -12066,6 +12066,7 @@ fi AC_CONFIG_FILES([config_host.mk Makefile lo.xcent instsetoo_native/util/openoffice.lst]) AC_CONFIG_HEADERS([config_host/config_buildid.h]) AC_CONFIG_HEADERS([config_host/config_clang.h]) +AC_CONFIG_HEADERS([config_host/config_cups.h]) AC_CONFIG_HEADERS([config_host/config_features.h]) AC_CONFIG_HEADERS([config_host/config_gcc.h]) AC_CONFIG_HEADERS([config_host/config_global.h]) diff --git a/include/vcl/jobdata.hxx b/include/vcl/jobdata.hxx index f6fb94fccab2..94c164f89848 100644 --- a/include/vcl/jobdata.hxx +++ b/include/vcl/jobdata.hxx @@ -77,6 +77,12 @@ struct VCL_DLLPUBLIC JobData static bool constructFromStreamBuffer( void* pData, int bytes, JobData& rJobData ); }; +bool operator==(const psp::JobData& rLeft, const psp::JobData& rRight); +inline bool operator!=(const psp::JobData& rLeft, const psp::JobData& rRight) +{ + return !( rLeft == rRight ); +} + } // namespace diff --git a/include/vcl/printerinfomanager.hxx b/include/vcl/printerinfomanager.hxx index 108b31504de1..d93f43dac82c 100644 --- a/include/vcl/printerinfomanager.hxx +++ b/include/vcl/printerinfomanager.hxx @@ -229,6 +229,16 @@ public: // gets m_bDisableCUPS, initialized from printer config bool isCUPSDisabled() const; + // Starts printing in a batch mode, in which all printing will be done together instead of separate jobs. + // If the implementation supports it, calls to endSpool() will only delay the printing until flushBatchPrint() + // is called to print all delayed jobs. + // Returns false if failed or not supported (in which case endSpool() will print normally). + virtual bool startBatchPrint(); + // Actually spools all delayed print jobs, if enabled, and disables batch mode. + virtual bool flushBatchPrint(); + // Returns true batch printing is supported at all. + virtual bool supportsBatchPrint() const; + virtual ~PrinterInfoManager(); }; diff --git a/sw/source/ui/dbui/dbmgr.cxx b/sw/source/ui/dbui/dbmgr.cxx index 50cce9236861..e263a1dfb1d3 100644 --- a/sw/source/ui/dbui/dbmgr.cxx +++ b/sw/source/ui/dbui/dbmgr.cxx @@ -132,6 +132,9 @@ #include #include +#include +#include + using namespace ::osl; using namespace ::svx; using namespace ::com::sun::star; @@ -888,6 +891,27 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, sal_Bool bNoError = sal_True; const bool bEMail = rMergeDescriptor.nMergeType == DBMGR_MERGE_EMAIL; const bool bMergeShell = rMergeDescriptor.nMergeType == DBMGR_MERGE_SHELL; + bool bCreateSingleFile = rMergeDescriptor.bCreateSingleFile; + + if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER ) + { + // It is possible to do MM printing in both modes for the same result, but the singlefile mode + // is slower because of all the temporary document copies and merging them together + // into the single file, while the other mode simply updates fields and prints for every record. + // However, this would cause one print job for every record, and e.g. CUPS refuses new jobs + // if it has many jobs enqueued (500 by default), and with the current printing framework + // (which uses a pull model) it's rather complicated to create a single print job + // in steps. + // To handle this, CUPS backend has been changed to cache all the documents to print + // and send them to CUPS only as one job at the very end. Therefore, with CUPS, it's ok + // to use the faster mode. As I have no idea about other platforms, keep them using + // the slower singlefile mode (or feel free to check them, or rewrite the printing code). +#if ENABLE_CUPS + bCreateSingleFile = !psp::PrinterInfoManager::get().supportsBatchPrint(); +#else + bCreateSingleFile = true; +#endif + } ::rtl::Reference< MailDispatcher > xMailDispatcher; OUString sBodyMimeType; @@ -1006,7 +1030,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, Application::Reschedule(); } - if(rMergeDescriptor.bCreateSingleFile) + if(bCreateSingleFile) { // create a target docshell to put the merged document into xTargetDocShell = new SwDocShell( SFX_CREATE_MODE_STANDARD ); @@ -1105,7 +1129,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, } // create a new temporary file name - only done once in case of bCreateSingleFile - if( createTempFile && ( 1 == nDocNo || !rMergeDescriptor.bCreateSingleFile )) + if( createTempFile && ( 1 == nDocNo || !bCreateSingleFile )) { INetURLObject aEntry(sPath); String sLeading; @@ -1155,7 +1179,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, // Create a copy of the source document and work with that one instead of the source. // If we're not in the single file mode (which requires modifying the document for the merging), // it is enough to do this just once. - if( 1 == nDocNo || rMergeDescriptor.bCreateSingleFile ) + if( 1 == nDocNo || bCreateSingleFile ) { assert( !xWorkDocSh.Is()); // copy the source document @@ -1186,7 +1210,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, rWorkShell.ViewShell::UpdateFlds(); SFX_APP()->NotifyEvent(SfxEventHint(SW_EVENT_FIELD_MERGE_FINISHED, SwDocShell::GetEventName(STR_SW_EVENT_FIELD_MERGE_FINISHED), xWorkDocSh)); - if( rMergeDescriptor.bCreateSingleFile ) + if( bCreateSingleFile ) pWorkDoc->RemoveInvisibleContent(); // launch MailMergeEvent if required @@ -1198,7 +1222,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, pEvtSrc->LaunchMailMergeEvent( aEvt ); } - if(rMergeDescriptor.bCreateSingleFile) + if(bCreateSingleFile) { OSL_ENSURE( pTargetShell, "no target shell available!" ); // copy created file into the target document @@ -1256,6 +1280,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, } else if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER ) { + assert(!bCreateSingleFile); if( 1 == nDocNo ) // set up printing only once at the beginning { // printing should be done synchronously otherwise the document @@ -1285,6 +1310,9 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, SfxPrinter* pDocPrt = pWorkView->GetPrinter(false); JobSetup aJobSetup = pDocPrt ? pDocPrt->GetJobSetup() : pWorkView->GetJobSetup(); Printer::PreparePrintJob( pWorkView->GetPrinterController(), aJobSetup ); +#if ENABLE_CUPS + psp::PrinterInfoManager::get().startBatchPrint(); +#endif } if( !Printer::ExecutePrintJob( pWorkView->GetPrinterController())) bCancel = true; @@ -1396,7 +1424,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, } } } - if( rMergeDescriptor.bCreateSingleFile ) + if( bCreateSingleFile ) { pWorkDoc->SetNewDBMgr( pOldDBManager ); xWorkDocSh->DoClose(); @@ -1409,7 +1437,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, // Freeze the layouts of the target document after the first inserted // sub-document, to get the correct PageDesc. - if(!bFreezedLayouts && (rMergeDescriptor.bCreateSingleFile)) + if(!bFreezedLayouts && bCreateSingleFile) { std::set aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts(); std::for_each( aAllLayouts.begin(), aAllLayouts.end(), @@ -1419,15 +1447,20 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, } while( !bCancel && (bSynchronizedDoc && (nStartRow != nEndRow)? ExistsNextRecord() : ToNextMergeRecord())); - if( !rMergeDescriptor.bCreateSingleFile ) + if( !bCreateSingleFile ) { if( rMergeDescriptor.nMergeType == DBMGR_MERGE_PRINTER ) + { Printer::FinishPrintJob( pWorkView->GetPrinterController()); +#if ENABLE_CUPS + psp::PrinterInfoManager::get().flushBatchPrint(); +#endif + } pWorkDoc->SetNewDBMgr( pOldDBManager ); xWorkDocSh->DoClose(); } - if (rMergeDescriptor.bCreateSingleFile) + if (bCreateSingleFile) { // sw::DocumentLayoutManager::CopyLayoutFmt() did not generate // unique fly names, do it here once. @@ -1439,7 +1472,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, Application::Reschedule(); // Unfreeze target document layouts and correct all PageDescs. - if(rMergeDescriptor.bCreateSingleFile) + if(bCreateSingleFile) { pTargetShell->CalcLayout(); std::set aAllLayouts = pTargetShell->GetDoc()->GetAllLayouts(); @@ -1455,7 +1488,7 @@ sal_Bool SwNewDBMgr::MergeMailFiles(SwWrtShell* pSourceShell, { rMergeDescriptor.pMailMergeConfigItem->SetTargetView( pTargetView ); } - else if(rMergeDescriptor.bCreateSingleFile) + else if(bCreateSingleFile) { if( rMergeDescriptor.nMergeType != DBMGR_MERGE_PRINTER ) { diff --git a/vcl/inc/cupsmgr.hxx b/vcl/inc/cupsmgr.hxx index 9a84b7790889..79c1ae70501b 100644 --- a/vcl/inc/cupsmgr.hxx +++ b/vcl/inc/cupsmgr.hxx @@ -58,6 +58,22 @@ class CUPSManager : public PrinterInfoManager osl::Mutex m_aGetPPDMutex; bool m_bPPDThreadRunning; + struct PendingJob + { + OUString printerName; + OUString jobTitle; + JobData jobData; + bool banner; + OString file; + PendingJob( const OUString& printerName_, const OUString& jobTitle_, const JobData& jobData_, + bool banner_, const OString& file_ ) + : printerName( printerName_ ), jobTitle( jobTitle_ ), jobData( jobData_ ), banner( banner_ ), file( file_ ) + {} + PendingJob() : banner( false ) {} + }; + std::list< PendingJob > pendingJobs; + bool batchMode; + CUPSManager(); virtual ~CUPSManager(); @@ -66,6 +82,9 @@ class CUPSManager : public PrinterInfoManager void getOptionsFromDocumentSetup( const JobData& rJob, bool bBanner, int& rNumOptions, void** rOptions ) const; void runDests(); OString threadedCupsGetPPD(const char* pPrinter); + + bool processPendingJobs(); + bool printJobs( const PendingJob& job, const std::vector< OString >& files ); public: // public for stub static void runDestThread(void* pMgr); @@ -81,6 +100,10 @@ public: virtual int endSpool( const OUString& rPrinterName, const OUString& rJobTitle, FILE* pFile, const JobData& rDocumentJobData, bool bBanner ); virtual void setupJobContextData( JobData& rData ); + virtual bool startBatchPrint() SAL_OVERRIDE; + virtual bool flushBatchPrint() SAL_OVERRIDE; + virtual bool supportsBatchPrint() const SAL_OVERRIDE; + // changes the info about a named printer virtual void changePrinterInfo( const OUString& rPrinter, const PrinterInfo& rNewInfo ); diff --git a/vcl/unx/generic/printer/cupsmgr.cxx b/vcl/unx/generic/printer/cupsmgr.cxx index 6b06596df614..5169d29199a4 100644 --- a/vcl/unx/generic/printer/cupsmgr.cxx +++ b/vcl/unx/generic/printer/cupsmgr.cxx @@ -646,8 +646,6 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit rDocumentJobData.m_nCopies ); - int nJobID = 0; - osl::MutexGuard aGuard( m_aCUPSMutex ); boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it = @@ -659,25 +657,98 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit } boost::unordered_map< FILE*, OString, FPtrHash >::const_iterator it = m_aSpoolFiles.find( pFile ); - if( it != m_aSpoolFiles.end() ) + if( it == m_aSpoolFiles.end() ) + return false; + fclose( pFile ); + PendingJob job( rPrintername, rJobTitle, rDocumentJobData, bBanner, it->second ); + m_aSpoolFiles.erase( pFile ); + pendingJobs.push_back( job ); + if( !batchMode ) // process immediately, otherwise will be handled by flushBatchPrint() + return processPendingJobs(); + return true; +} + +bool CUPSManager::startBatchPrint() +{ + batchMode = true; + return true; +} + +bool CUPSManager::supportsBatchPrint() const +{ + return true; +} + +bool CUPSManager::flushBatchPrint() +{ + osl::MutexGuard aGuard( m_aCUPSMutex ); + return processPendingJobs(); +} + +bool CUPSManager::processPendingJobs() +{ + // Print all jobs that have the same data using one CUPS call (i.e. merge all jobs that differ only in files to print). + PendingJob currentJobData; + bool first = true; + std::vector< OString > files; + bool ok = true; + while( !pendingJobs.empty()) + { + if( first ) + { + currentJobData = pendingJobs.front(); + first = false; + } + else if( currentJobData.printerName != pendingJobs.front().printerName + || currentJobData.jobTitle != pendingJobs.front().jobTitle + || currentJobData.jobData != pendingJobs.front().jobData + || currentJobData.banner != pendingJobs.front().banner ) + { + if( !printJobs( currentJobData, files )) + ok = false; + files.clear(); + currentJobData = pendingJobs.front(); + } + files.push_back( pendingJobs.front().file ); + pendingJobs.pop_front(); + } + if( !first ) { - fclose( pFile ); - rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + if( !printJobs( currentJobData, files )) // print the last batch + ok = false; + } + return ok; +} - // setup cups options - int nNumOptions = 0; - cups_option_t* pOptions = NULL; - getOptionsFromDocumentSetup( rDocumentJobData, bBanner, nNumOptions, (void**)&pOptions ); +bool CUPSManager::printJobs( const PendingJob& job, const std::vector< OString >& files ) +{ + boost::unordered_map< OUString, int, OUStringHash >::iterator dest_it = + m_aCUPSDestMap.find( job.printerName ); + + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + + // setup cups options + int nNumOptions = 0; + cups_option_t* pOptions = NULL; + getOptionsFromDocumentSetup( job.jobData, job.banner, nNumOptions, reinterpret_cast(&pOptions) ); + + cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; - cups_dest_t* pDest = ((cups_dest_t*)m_pDests) + dest_it->second; - nJobID = cupsPrintFile( pDest->name, - it->second.getStr(), - OUStringToOString( rJobTitle, aEnc ).getStr(), - nNumOptions, pOptions ); + std::vector< const char* > fnames; + for( std::vector< OString >::const_iterator it = files.begin(); + it != files.end(); + ++it ) + fnames.push_back( it->getStr()); + + int nJobID = cupsPrintFiles(pDest->name, + fnames.size(), + fnames.data(), + OUStringToOString( job.jobTitle, aEnc ).getStr(), + nNumOptions, pOptions); #if OSL_DEBUG_LEVEL > 1 fprintf( stderr, "cupsPrintFile( %s, %s, %s, %d, %p ) returns %d\n", pDest->name, - it->second.getStr(), + ( fnames.size() == 1 ? files.front() : OString::number( fnames.size()) ).getStr(), OUStringToOString( rJobTitle, aEnc ).getStr(), nNumOptions, pOptions, @@ -691,11 +762,13 @@ int CUPSManager::endSpool( const OUString& rPrintername, const OUString& rJobTit system( aCmd.getStr() ); #endif - unlink( it->second.getStr() ); - m_aSpoolFiles.erase( pFile ); - if( pOptions ) - cupsFreeOptions( nNumOptions, pOptions ); - } + for( std::vector< OString >::const_iterator it = files.begin(); + it != files.end(); + ++it ) + unlink( it->getStr()); + + if( pOptions ) + cupsFreeOptions( nNumOptions, pOptions ); return nJobID; } diff --git a/vcl/unx/generic/printer/jobdata.cxx b/vcl/unx/generic/printer/jobdata.cxx index d40fffd913a7..16adb96b4b90 100644 --- a/vcl/unx/generic/printer/jobdata.cxx +++ b/vcl/unx/generic/printer/jobdata.cxx @@ -52,6 +52,24 @@ JobData& JobData::operator=(const JobData& rRight) return *this; } +bool psp::operator==(const psp::JobData& rLeft, const psp::JobData& rRight) +{ + return rLeft.m_nCopies == rRight.m_nCopies +// && rLeft.m_bCollate == rRight.m_bCollate + && rLeft.m_nLeftMarginAdjust == rRight.m_nLeftMarginAdjust + && rLeft.m_nRightMarginAdjust == rRight.m_nRightMarginAdjust + && rLeft.m_nTopMarginAdjust == rRight.m_nTopMarginAdjust + && rLeft.m_nBottomMarginAdjust == rRight.m_nBottomMarginAdjust + && rLeft.m_nColorDepth == rRight.m_nColorDepth + && rLeft.m_eOrientation == rRight.m_eOrientation + && rLeft.m_aPrinterName == rRight.m_aPrinterName + && rLeft.m_pParser == rRight.m_pParser +// && rLeft.m_aContext == rRight.m_aContext + && rLeft.m_nPSLevel == rRight.m_nPSLevel + && rLeft.m_nPDFDevice == rRight.m_nPDFDevice + && rLeft.m_nColorDevice == rRight.m_nColorDevice; +} + void JobData::setCollate( bool bCollate ) { const PPDParser* pParser = m_aContext.getParser(); diff --git a/vcl/unx/generic/printer/printerinfomanager.cxx b/vcl/unx/generic/printer/printerinfomanager.cxx index 7def80fd5564..b895f8208f98 100644 --- a/vcl/unx/generic/printer/printerinfomanager.cxx +++ b/vcl/unx/generic/printer/printerinfomanager.cxx @@ -1180,6 +1180,21 @@ void PrinterInfoManager::setDefaultPaper( PPDContext& rContext ) const // ----------------------------------------------------------------- +bool PrinterInfoManager::startBatchPrint() +{ + return false; // not implemented +} + +bool PrinterInfoManager::supportsBatchPrint() const +{ + return false; +} + +bool PrinterInfoManager::flushBatchPrint() +{ + return false; +} + SystemQueueInfo::SystemQueueInfo() : m_bChanged( false ) { -- cgit v1.2.3