kdeprint Library API Documentation

kprinterimpl.cpp

00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (c) 2001 Michael Goffioul <kdeprint@swing.be> 00004 * 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00018 * Boston, MA 02111-1307, USA. 00019 **/ 00020 00021 #include "kprinterimpl.h" 00022 #include "kprinter.h" 00023 #include "kmfactory.h" 00024 #include "kmmanager.h" 00025 #include "kmuimanager.h" 00026 #include "kxmlcommand.h" 00027 #include "kmspecialmanager.h" 00028 #include "kmthreadjob.h" 00029 #include "kmprinter.h" 00030 #include "driver.h" 00031 00032 #include <qfile.h> 00033 #include <qregexp.h> 00034 #include <kinputdialog.h> 00035 #include <klocale.h> 00036 #include <dcopclient.h> 00037 #include <kapplication.h> 00038 #include <kstandarddirs.h> 00039 #include <kdatastream.h> 00040 #include <kdebug.h> 00041 #include <kmimemagic.h> 00042 #include <kmessagebox.h> 00043 #include <kprocess.h> 00044 #include <kconfig.h> 00045 00046 #include <stdlib.h> 00047 00048 void dumpOptions(const QMap<QString,QString>&); 00049 void initEditPrinter(KMPrinter *p) 00050 { 00051 if (!p->isEdited()) 00052 { 00053 p->setEditedOptions(p->defaultOptions()); 00054 p->setEdited(true); 00055 } 00056 } 00057 00058 //**************************************************************************************** 00059 00060 KPrinterImpl::KPrinterImpl(QObject *parent, const char *name) 00061 : QObject(parent,name) 00062 { 00063 loadAppOptions(); 00064 } 00065 00066 KPrinterImpl::~KPrinterImpl() 00067 { 00068 } 00069 00070 void KPrinterImpl::preparePrinting(KPrinter *printer) 00071 { 00072 // page size -> try to find page size and margins from driver file 00073 // use "PageSize" as option name to find the wanted page size. It's 00074 // up to the driver loader to use that option name. 00075 KMManager *mgr = KMFactory::self()->manager(); 00076 DrMain *driver = mgr->loadPrinterDriver(mgr->findPrinter(printer->printerName()), false); 00077 if (driver) 00078 { 00079 // Find the page size: 00080 // 1) print option 00081 // 2) default driver option 00082 QString psname = printer->option("PageSize"); 00083 if (psname.isEmpty()) 00084 { 00085 DrListOption *opt = (DrListOption*)driver->findOption("PageSize"); 00086 if (opt) psname = opt->get("default"); 00087 } 00088 if (!psname.isEmpty()) 00089 { 00090 printer->setOption("kde-pagesize",QString::number((int)pageNameToPageSize(psname))); 00091 DrPageSize *ps = driver->findPageSize(psname); 00092 if (ps) 00093 { 00094 printer->setRealPageSize( ps ); 00095 } 00096 } 00097 00098 // Find the numerical resolution 00099 // 1) print option (Resolution) 00100 // 2) default driver option (Resolution) 00101 // 3) default printer resolution 00102 // The resolution must have the format: XXXdpi or XXXxYYYdpi. In the second 00103 // case the YYY value is used as resolution. 00104 QString res = printer->option( "Resolution" ); 00105 if ( res.isEmpty() ) 00106 { 00107 DrBase *opt = driver->findOption( "Resolution" ); 00108 if ( opt ) 00109 res = opt->get( "default" ); 00110 if ( res.isEmpty() ) 00111 res = driver->get( "resolution" ); 00112 } 00113 if ( !res.isEmpty() ) 00114 { 00115 QRegExp re( "(\\d+)(?:x(\\d+))?dpi" ); 00116 if ( re.search( res ) != -1 ) 00117 { 00118 if ( !re.cap( 2 ).isEmpty() ) 00119 printer->setOption( "kde-resolution", re.cap( 2 ) ); 00120 else 00121 printer->setOption( "kde-resolution", re.cap( 1 ) ); 00122 } 00123 } 00124 00125 // Find the supported fonts 00126 QString fonts = driver->get( "fonts" ); 00127 if ( !fonts.isEmpty() ) 00128 printer->setOption( "kde-fonts", fonts ); 00129 00130 delete driver; 00131 } 00132 00133 } 00134 00135 bool KPrinterImpl::setupCommand(QString&, KPrinter*) 00136 { 00137 return false; 00138 } 00139 00140 bool KPrinterImpl::printFiles(KPrinter *p, const QStringList& f, bool flag) 00141 { 00142 QString cmd; 00143 if (p->option("kde-isspecial") == "1") 00144 { 00145 if (p->option("kde-special-command").isEmpty() && p->outputToFile()) 00146 { 00147 KURL url( p->outputFileName() ); 00148 if ( !url.isLocalFile() ) 00149 { 00150 cmd = ( flag ? "mv" : "cp" ) + ( " %in $out{" + p->outputFileName() + "}" ); 00151 } 00152 else 00153 { 00154 if (f.count() > 1) 00155 { 00156 p->setErrorMessage(i18n("Cannot copy multiple files into one file.")); 00157 return false; 00158 } 00159 else 00160 { 00161 KProcess proc; 00162 proc << (flag?"mv":"cp") << f[0] << p->outputFileName(); 00163 if (!proc.start(KProcess::Block) || !proc.normalExit() || proc.exitStatus() != 0) 00164 { 00165 p->setErrorMessage(i18n("Cannot save print file to %1. Check that you have write access to it.").arg(p->outputFileName())); 00166 return false; 00167 } 00168 } 00169 return true; 00170 } 00171 } 00172 else if (!setupSpecialCommand(cmd,p,f)) 00173 return false; 00174 } 00175 else if (!setupCommand(cmd,p)) 00176 return false; 00177 return startPrinting(cmd,p,f,flag); 00178 } 00179 00180 void KPrinterImpl::broadcastOption(const QString& key, const QString& value) 00181 { 00182 // force printer listing if not done yet (or reload needed) 00183 QPtrList<KMPrinter> *printers = KMFactory::self()->manager()->printerListComplete(false); 00184 if (printers) 00185 { 00186 QPtrListIterator<KMPrinter> it(*printers); 00187 for (;it.current();++it) 00188 { 00189 initEditPrinter(it.current()); 00190 it.current()->setEditedOption(key,value); 00191 } 00192 } 00193 } 00194 00195 int KPrinterImpl::dcopPrint(const QString& cmd, const QStringList& files, bool removeflag) 00196 { 00197 kdDebug(500) << "kdeprint: print command: " << cmd << endl; 00198 00199 int result = 0; 00200 DCOPClient *dclient = kapp->dcopClient(); 00201 if (!dclient || (!dclient->isAttached() && !dclient->attach())) 00202 { 00203 return result; 00204 } 00205 00206 QByteArray data, replyData; 00207 QCString replyType; 00208 QDataStream arg( data, IO_WriteOnly ); 00209 arg << cmd; 00210 arg << files; 00211 arg << removeflag; 00212 if (dclient->call( "kded", "kdeprintd", "print(QString,QStringList,bool)", data, replyType, replyData )) 00213 { 00214 if (replyType == "int") 00215 { 00216 QDataStream _reply_stream( replyData, IO_ReadOnly ); 00217 _reply_stream >> result; 00218 } 00219 } 00220 return result; 00221 } 00222 00223 void KPrinterImpl::statusMessage(const QString& msg, KPrinter *printer) 00224 { 00225 kdDebug(500) << "kdeprint: status message: " << msg << endl; 00226 KConfig *conf = KMFactory::self()->printConfig(); 00227 conf->setGroup("General"); 00228 if (!conf->readBoolEntry("ShowStatusMsg", true)) 00229 return; 00230 00231 QString message(msg); 00232 if (printer && !msg.isEmpty()) 00233 message.prepend(i18n("Printing document: %1").arg(printer->docName())+"\n"); 00234 00235 DCOPClient *dclient = kapp->dcopClient(); 00236 if (!dclient || (!dclient->isAttached() && !dclient->attach())) 00237 { 00238 return; 00239 } 00240 00241 QByteArray data; 00242 QDataStream arg( data, IO_WriteOnly ); 00243 arg << message; 00244 arg << (int)getpid(); 00245 arg << kapp->caption(); 00246 dclient->send( "kded", "kdeprintd", "statusMessage(QString,int,QString)", data ); 00247 } 00248 00249 bool KPrinterImpl::startPrinting(const QString& cmd, KPrinter *printer, const QStringList& files, bool flag) 00250 { 00251 statusMessage(i18n("Sending print data to printer: %1").arg(printer->printerName()), printer); 00252 00253 QString command(cmd), filestr; 00254 QStringList printfiles; 00255 if (command.find("%in") == -1) command.append(" %in"); 00256 00257 for (QStringList::ConstIterator it=files.begin(); it!=files.end(); ++it) 00258 if (QFile::exists(*it)) 00259 { 00260 // quote and encode filenames 00261 filestr.append(quote(QFile::encodeName(*it))).append(" "); 00262 printfiles.append(*it); 00263 } 00264 else 00265 kdDebug(500) << "File not found: " << (*it) << endl; 00266 00267 if (printfiles.count() > 0) 00268 { 00269 command.replace("%in",filestr); 00270 int pid = dcopPrint(command,files,flag); 00271 if (pid > 0) 00272 { 00273 if (printer) 00274 KMThreadJob::createJob(pid,printer->printerName(),printer->docName(),getenv("USER"),0); 00275 return true; 00276 } 00277 else 00278 { 00279 QString msg = i18n("Unable to start child print process. "); 00280 if (pid == 0) 00281 msg += i18n("The KDE print server (<b>kdeprintd</b>) could not be contacted. Check that this server is running."); 00282 else 00283 msg += i18n("1 is the command that <files> is given to", "Check the command syntax:\n%1 <files>").arg(cmd); 00284 printer->setErrorMessage(msg); 00285 return false; 00286 } 00287 } 00288 //else 00289 //{ 00290 printer->setErrorMessage(i18n("No valid file was found for printing. Operation aborted.")); 00291 return false; 00292 //} 00293 } 00294 00295 QString KPrinterImpl::tempFile() 00296 { 00297 QString f; 00298 // be sure the file doesn't exist 00299 do f = locateLocal("tmp","kdeprint_") + KApplication::randomString(8); while (QFile::exists(f)); 00300 return f; 00301 } 00302 00303 int KPrinterImpl::filterFiles(KPrinter *printer, QStringList& files, bool flag) 00304 { 00305 QStringList flist = QStringList::split(',',printer->option("_kde-filters"),false); 00306 QMap<QString,QString> opts = printer->options(); 00307 00308 // generic page selection mechanism (using psselect filter) 00309 // do it only if: 00310 // - using system-side page selection 00311 // - special printer or regular printer without page selection support in current plugin 00312 // - one of the page selection option has been selected to non default value 00313 // Action -> add the psselect filter to the filter chain. 00314 if (printer->pageSelection() == KPrinter::SystemSide && 00315 (printer->option("kde-isspecial") == "1" || !(KMFactory::self()->uiManager()->pluginPageCap() & KMUiManager::PSSelect)) && 00316 (printer->pageOrder() == KPrinter::LastPageFirst || 00317 !printer->option("kde-range").isEmpty() || 00318 printer->pageSet() != KPrinter::AllPages)) 00319 { 00320 if (flist.findIndex("psselect") == -1) 00321 { 00322 int index = KXmlCommandManager::self()->insertCommand(flist, "psselect", false); 00323 if (index == -1 || !KXmlCommandManager::self()->checkCommand("psselect")) 00324 { 00325 printer->setErrorMessage(i18n("<p>Unable to perform the requested page selection. The filter <b>psselect</b> " 00326 "cannot be inserted in the current filter chain. See <b>Filter</b> tab in the " 00327 "printer properties dialog for further information.</p>")); 00328 return -1; 00329 } 00330 } 00331 if (printer->pageOrder() == KPrinter::LastPageFirst) 00332 opts["_kde-psselect-order"] = "r"; 00333 if (!printer->option("kde-range").isEmpty()) 00334 opts["_kde-psselect-range"] = printer->option("kde-range"); 00335 if (printer->pageSet() != KPrinter::AllPages) 00336 opts["_kde-psselect-set"] = (printer->pageSet() == KPrinter::OddPages ? "-o" : "-e"); 00337 } 00338 00339 return doFilterFiles(printer, files, flist, opts, flag); 00340 } 00341 00342 int KPrinterImpl::doFilterFiles(KPrinter *printer, QStringList& files, const QStringList& flist, const QMap<QString,QString>& opts, bool flag) 00343 { 00344 // nothing to do 00345 if (flist.count() == 0) 00346 return 0; 00347 00348 QString filtercmd; 00349 QStringList inputMimeTypes; 00350 for (uint i=0;i<flist.count();i++) 00351 { 00352 KXmlCommand *filter = KXmlCommandManager::self()->loadCommand(flist[i]); 00353 if (!filter) 00354 { 00355 printer->setErrorMessage(i18n("<p>Could not load filter description for <b>%1</b>.</p>").arg(flist[i])); 00356 return -1; // Error 00357 } 00358 if (i == 0) 00359 inputMimeTypes = filter->inputMimeTypes(); 00360 00361 QString subcmd = filter->buildCommand(opts,(i>0),(i<(flist.count()-1))); 00362 delete filter; 00363 if (!subcmd.isEmpty()) 00364 { 00365 filtercmd.append(subcmd); 00366 if (i < flist.count()-1) 00367 filtercmd.append("| "); 00368 } 00369 else 00370 { 00371 printer->setErrorMessage(i18n("<p>Error while reading filter description for <b>%1</b>. Empty command line received.</p>").arg(flist[i])); 00372 return -1; 00373 } 00374 } 00375 kdDebug(500) << "kdeprint: filter command: " << filtercmd << endl; 00376 00377 QString rin("%in"), rout("%out"), rpsl("%psl"), rpsu("%psu"); 00378 QString ps = pageSizeToPageName( printer->option( "kde-printsize" ).isEmpty() ? printer->pageSize() : ( KPrinter::PageSize )printer->option( "kde-printsize" ).toInt() ); 00379 for (QStringList::Iterator it=files.begin(); it!=files.end(); ++it) 00380 { 00381 QString mime = KMimeMagic::self()->findFileType(*it)->mimeType(); 00382 if (inputMimeTypes.find(mime) == inputMimeTypes.end()) 00383 { 00384 if (KMessageBox::warningContinueCancel(0, 00385 "<p>" + i18n("The MIME type %1 is not supported as input of the filter chain " 00386 "(this may happen with non-CUPS spoolers when performing page selection " 00387 "on a non-PostScript file). Do you want KDE to convert the file to a supported " 00388 "format?</p>").arg(mime), 00389 QString::null, i18n("Convert")) == KMessageBox::Continue) 00390 { 00391 QStringList ff; 00392 int done(0); 00393 00394 ff << *it; 00395 while (done == 0) 00396 { 00397 bool ok(false); 00398 QString targetMime = KInputDialog::getItem( 00399 i18n("Select MIME Type"), 00400 i18n("Select the target format for the conversion:"), 00401 inputMimeTypes, 0, false, &ok); 00402 if (!ok) 00403 { 00404 printer->setErrorMessage(i18n("Operation aborted.")); 00405 return -1; 00406 } 00407 QStringList filters = KXmlCommandManager::self()->autoConvert(mime, targetMime); 00408 if (filters.count() == 0) 00409 { 00410 KMessageBox::error(0, i18n("No appropriate filter found. Select another target format.")); 00411 } 00412 else 00413 { 00414 int result = doFilterFiles(printer, ff, filters, QMap<QString,QString>(), flag); 00415 if (result == 1) 00416 { 00417 *it = ff[0]; 00418 done = 1; 00419 } 00420 else 00421 { 00422 KMessageBox::error(0, 00423 i18n("<qt>Operation failed with message:<br>%1<br>Select another target format.</qt>").arg(printer->errorMessage())); 00424 } 00425 } 00426 } 00427 } 00428 else 00429 { 00430 printer->setErrorMessage(i18n("Operation aborted.")); 00431 return -1; 00432 } 00433 } 00434 00435 QString tmpfile = tempFile(); 00436 QString cmd(filtercmd); 00437 cmd.replace(rout,quote(tmpfile)); 00438 cmd.replace(rpsl,ps.lower()); 00439 cmd.replace(rpsu,ps); 00440 cmd.replace(rin,quote(*it)); // Replace as last, filename could contain "%psl" 00441 statusMessage(i18n("Filtering print data"), printer); 00442 int status = system(QFile::encodeName(cmd)); 00443 if (status < 0 || WEXITSTATUS(status) == 127) 00444 { 00445 printer->setErrorMessage(i18n("Error while filtering. Command was: <b>%1</b>.").arg(filtercmd)); 00446 return -1; 00447 } 00448 if (flag) QFile::remove(*it); 00449 *it = tmpfile; 00450 } 00451 return 1; 00452 } 00453 00454 int KPrinterImpl::autoConvertFiles(KPrinter *printer, QStringList& files, bool flag) 00455 { 00456 QString primaryMimeType = "application/postscript"; 00457 QStringList mimeTypes( primaryMimeType ); 00458 if ( printer->option( "kde-isspecial" ) == "1" ) 00459 { 00460 if ( !printer->option( "kde-special-command" ).isEmpty() ) 00461 { 00462 KXmlCommand *cmd = KXmlCommandManager::self()->loadCommand( printer->option( "kde-special-command" ), true ); 00463 if ( cmd ) 00464 { 00465 mimeTypes = cmd->inputMimeTypes(); 00466 // FIXME: the XML command description should now contain a primiary 00467 // mime type as well. This is a temporary-only solution. 00468 primaryMimeType = mimeTypes[ 0 ]; 00469 } 00470 } 00471 } 00472 else 00473 { 00474 KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem()); 00475 mimeTypes = info.mimeTypes; 00476 primaryMimeType = info.primaryMimeType; 00477 } 00478 KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem()); 00479 int status(0), result; 00480 for (QStringList::Iterator it=files.begin(); it!=files.end(); ) 00481 { 00482 QString mime = KMimeMagic::self()->findFileType(*it)->mimeType(); 00483 if ( mime == "application/x-zerosize" ) 00484 { 00485 // special case of empty file 00486 KMessageBox::information( NULL, 00487 i18n( "<qt>The print file is empty and will be ignored:<p>%1</p></qt>" ).arg( *it ), 00488 QString::null, "emptyFileNotPrinted" ); 00489 if ( flag ) 00490 QFile::remove( *it ); 00491 it = files.remove( it ); 00492 continue; 00493 } 00494 else if (mimeTypes.findIndex(mime) == -1) 00495 { 00496 if ((result=KMessageBox::warningYesNoCancel(NULL, 00497 i18n("<qt>The file format <em> %1 </em> is not directly supported by the current print system. You " 00498 "now have 3 options: " 00499 "<ul> " 00500 "<li> KDE can attempt to convert this file automatically to a supported format. " 00501 "(Select <em>Convert</em>) </li>" 00502 "<li> You can try to send the file to the printer without any conversion. " 00503 "(Select <em>Keep</em>) </li>" 00504 "<li> You can cancel the printjob. " 00505 "(Select <em>Cancel</em>) </li>" 00506 "</ul> " 00507 "Do you want KDE to attempt and convert this file to %2?</qt>").arg(mime).arg(primaryMimeType), 00508 QString::null, 00509 i18n("Convert"), 00510 i18n("Keep"), 00511 QString::fromLatin1("kdeprintAutoConvert"))) == KMessageBox::Yes) 00512 { 00513 // find the filter chain 00514 QStringList flist = KXmlCommandManager::self()->autoConvert(mime, primaryMimeType); 00515 if (flist.count() == 0) 00516 { 00517 KMessageBox::error(NULL, 00518 i18n("<qt>No appropriate filter was found to convert the file format %1 into %2.<br>" 00519 "<ul>" 00520 "<li>Go to <i>System Options -> Commands</i> to look through the list of " 00521 "possible filters. Each filter executes an external program.</li>" 00522 "<li> See if the required external program is available.on your " 00523 "system.</li>" 00524 "</ul>" 00525 "</qt>").arg(mime).arg(primaryMimeType), 00526 i18n("Print")); 00527 if (flag) 00528 QFile::remove(*it); 00529 it = files.remove(it); 00530 continue; 00531 } 00532 QStringList l(*it); 00533 switch (doFilterFiles(printer, l, flist, QMap<QString,QString>(), flag)) 00534 { 00535 case -1: 00536 return -1; 00537 case 0: 00538 break; 00539 case 1: 00540 status = 1; 00541 *it = l[0]; 00542 break; 00543 } 00544 } 00545 else if (result == KMessageBox::Cancel) 00546 { 00547 files.clear(); 00548 return 0; 00549 } 00550 } 00551 ++it; 00552 } 00553 return status; 00554 } 00555 00556 bool KPrinterImpl::setupSpecialCommand(QString& cmd, KPrinter *p, const QStringList&) 00557 { 00558 QString s(p->option("kde-special-command")); 00559 if (s.isEmpty()) 00560 { 00561 p->setErrorMessage("Empty command."); 00562 return false; 00563 } 00564 00565 s = KMFactory::self()->specialManager()->setupCommand(s, p->options()); 00566 00567 QString ps = pageSizeToPageName( p->option( "kde-printsize" ).isEmpty() ? p->pageSize() : ( KPrinter::PageSize )p->option( "kde-printsize" ).toInt() ); 00568 s.replace("%psl", ps.lower()); 00569 s.replace("%psu", ps); 00570 s.replace("%out", "$out{" + p->outputFileName() + "}"); // Replace as last 00571 cmd = s; 00572 return true; 00573 } 00574 00575 QString KPrinterImpl::quote(const QString& s) 00576 { return KProcess::quote(s); } 00577 00578 void KPrinterImpl::saveOptions(const QMap<QString,QString>& opts) 00579 { 00580 m_options = opts; 00581 saveAppOptions(); 00582 } 00583 00584 void KPrinterImpl::loadAppOptions() 00585 { 00586 KConfig *conf = KGlobal::config(); 00587 conf->setGroup("KPrinter Settings"); 00588 QStringList opts = conf->readListEntry("ApplicationOptions"); 00589 for (uint i=0; i<opts.count(); i+=2) 00590 if (opts[i].startsWith("app-")) 00591 m_options[opts[i]] = opts[i+1]; 00592 } 00593 00594 void KPrinterImpl::saveAppOptions() 00595 { 00596 QStringList optlist; 00597 for (QMap<QString,QString>::ConstIterator it=m_options.begin(); it!=m_options.end(); ++it) 00598 if (it.key().startsWith("app-")) 00599 optlist << it.key() << it.data(); 00600 00601 KConfig *conf = KGlobal::config(); 00602 conf->setGroup("KPrinter Settings"); 00603 conf->writeEntry("ApplicationOptions", optlist); 00604 } 00605 00606 #include "kprinterimpl.moc"
KDE Logo
This file is part of the documentation for kdeprint Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 23:26:38 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003