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 // TODO: better error message 00356 printer->setErrorMessage(i18n("<p>Error while reading filter description for <b>%1</b>. Empty command line received.</p>").arg(flist[i])); 00357 return -1; // Error 00358 } 00359 if (i == 0) 00360 inputMimeTypes = filter->inputMimeTypes(); 00361 00362 QString subcmd = filter->buildCommand(opts,(i>0),(i<(flist.count()-1))); 00363 delete filter; 00364 if (!subcmd.isEmpty()) 00365 { 00366 filtercmd.append(subcmd); 00367 if (i < flist.count()-1) 00368 filtercmd.append("| "); 00369 } 00370 else 00371 { 00372 printer->setErrorMessage(i18n("<p>Error while reading filter description for <b>%1</b>. Empty command line received.</p>").arg(flist[i])); 00373 return -1; 00374 } 00375 } 00376 kdDebug(500) << "kdeprint: filter command: " << filtercmd << endl; 00377 00378 QString rin("%in"), rout("%out"), rpsl("%psl"), rpsu("%psu"); 00379 QString ps = pageSizeToPageName( printer->option( "kde-printsize" ).isEmpty() ? printer->pageSize() : ( KPrinter::PageSize )printer->option( "kde-printsize" ).toInt() ); 00380 for (QStringList::Iterator it=files.begin(); it!=files.end(); ++it) 00381 { 00382 QString mime = KMimeMagic::self()->findFileType(*it)->mimeType(); 00383 if (inputMimeTypes.find(mime) == inputMimeTypes.end()) 00384 { 00385 if (KMessageBox::warningContinueCancel(0, 00386 "<p>" + i18n("The MIME type %1 is not supported as input of the filter chain " 00387 "(this may happen with non-CUPS spoolers when performing page selection " 00388 "on a non-PostScript file). Do you want KDE to convert the file to a supported " 00389 "format?</p>").arg(mime), 00390 QString::null, i18n("Convert")) == KMessageBox::Continue) 00391 { 00392 QStringList ff; 00393 int done(0); 00394 00395 ff << *it; 00396 while (done == 0) 00397 { 00398 bool ok(false); 00399 QString targetMime = KInputDialog::getItem( 00400 i18n("Select MIME Type"), 00401 i18n("Select the target format for the conversion:"), 00402 inputMimeTypes, 0, false, &ok); 00403 if (!ok) 00404 { 00405 printer->setErrorMessage(i18n("Operation aborted.")); 00406 return -1; 00407 } 00408 QStringList filters = KXmlCommandManager::self()->autoConvert(mime, targetMime); 00409 if (filters.count() == 0) 00410 { 00411 KMessageBox::error(0, i18n("No appropriate filter found. Select another target format.")); 00412 } 00413 else 00414 { 00415 int result = doFilterFiles(printer, ff, filters, QMap<QString,QString>(), flag); 00416 if (result == 1) 00417 { 00418 *it = ff[0]; 00419 done = 1; 00420 } 00421 else 00422 { 00423 KMessageBox::error(0, 00424 i18n("<qt>Operation failed with message:<br>%1<br>Select another target format.</qt>").arg(printer->errorMessage())); 00425 } 00426 } 00427 } 00428 } 00429 else 00430 { 00431 printer->setErrorMessage(i18n("Operation aborted.")); 00432 return -1; 00433 } 00434 } 00435 00436 QString tmpfile = tempFile(); 00437 QString cmd(filtercmd); 00438 cmd.replace(rout,quote(tmpfile)); 00439 cmd.replace(rpsl,ps.lower()); 00440 cmd.replace(rpsu,ps); 00441 cmd.replace(rin,quote(*it)); // Replace as last, filename could contain "%psl" 00442 statusMessage(i18n("Filtering print data"), printer); 00443 int status = system(QFile::encodeName(cmd)); 00444 if (status < 0 || WEXITSTATUS(status) == 127) 00445 { 00446 printer->setErrorMessage(i18n("Error while filtering. Command was: <b>%1</b>.").arg(filtercmd)); 00447 return -1; 00448 } 00449 if (flag) QFile::remove(*it); 00450 *it = tmpfile; 00451 } 00452 return 1; 00453 } 00454 00455 int KPrinterImpl::autoConvertFiles(KPrinter *printer, QStringList& files, bool flag) 00456 { 00457 QString primaryMimeType = "application/postscript"; 00458 QStringList mimeTypes( primaryMimeType ); 00459 if ( printer->option( "kde-isspecial" ) == "1" ) 00460 { 00461 if ( !printer->option( "kde-special-command" ).isEmpty() ) 00462 { 00463 KXmlCommand *cmd = KXmlCommandManager::self()->loadCommand( printer->option( "kde-special-command" ), true ); 00464 if ( cmd ) 00465 { 00466 mimeTypes = cmd->inputMimeTypes(); 00467 // FIXME: the XML command description should now contain a primiary 00468 // mime type as well. This is a temporary-only solution. 00469 primaryMimeType = mimeTypes[ 0 ]; 00470 } 00471 } 00472 } 00473 else 00474 { 00475 KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem()); 00476 mimeTypes = info.mimeTypes; 00477 primaryMimeType = info.primaryMimeType; 00478 } 00479 KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem()); 00480 int status(0), result; 00481 for (QStringList::Iterator it=files.begin(); it!=files.end(); ) 00482 { 00483 QString mime = KMimeMagic::self()->findFileType(*it)->mimeType(); 00484 if (mimeTypes.findIndex(mime) == -1) 00485 { 00486 if ((result=KMessageBox::warningYesNoCancel(NULL, 00487 i18n("The file format %1 is not directly supported by the current print system. " 00488 "KDE can try to convert this file automatically to a supported format. But you can " 00489 "still try to send the file to the printer without any conversion. Do you want KDE " 00490 "to try to convert this file to %2?").arg(mime).arg(primaryMimeType), 00491 QString::null, 00492 i18n("Convert"), 00493 i18n("Keep"), 00494 QString::fromLatin1("kdeprintAutoConvert"))) == KMessageBox::Yes) 00495 { 00496 // find the filter chain 00497 QStringList flist = KXmlCommandManager::self()->autoConvert(mime, primaryMimeType); 00498 if (flist.count() == 0) 00499 { 00500 if (KMessageBox::warningYesNo(NULL, 00501 i18n("No appropriate filter was found to convert the file " 00502 "format %1 into %2. Do you want to print the " 00503 "file using its original format?").arg(mime).arg(primaryMimeType), 00504 QString::null, 00505 i18n("Print"), 00506 i18n("Skip")) == KMessageBox::No) 00507 { 00508 if (flag) 00509 QFile::remove(*it); 00510 it = files.remove(it); 00511 } 00512 else 00513 ++it; 00514 continue; 00515 } 00516 QStringList l(*it); 00517 switch (doFilterFiles(printer, l, flist, QMap<QString,QString>(), flag)) 00518 { 00519 case -1: 00520 return -1; 00521 case 0: 00522 break; 00523 case 1: 00524 status = 1; 00525 *it = l[0]; 00526 break; 00527 } 00528 } 00529 else if (result == KMessageBox::Cancel) 00530 { 00531 files.clear(); 00532 return 0; 00533 } 00534 } 00535 ++it; 00536 } 00537 return status; 00538 } 00539 00540 bool KPrinterImpl::setupSpecialCommand(QString& cmd, KPrinter *p, const QStringList&) 00541 { 00542 QString s(p->option("kde-special-command")); 00543 if (s.isEmpty()) 00544 { 00545 p->setErrorMessage("Empty command."); 00546 return false; 00547 } 00548 00549 s = KMFactory::self()->specialManager()->setupCommand(s, p->options()); 00550 00551 QString ps = pageSizeToPageName( p->option( "kde-printsize" ).isEmpty() ? p->pageSize() : ( KPrinter::PageSize )p->option( "kde-printsize" ).toInt() ); 00552 s.replace("%psl", ps.lower()); 00553 s.replace("%psu", ps); 00554 s.replace("%out", "$out{" + p->outputFileName() + "}"); // Replace as last 00555 cmd = s; 00556 return true; 00557 } 00558 00559 QString KPrinterImpl::quote(const QString& s) 00560 { return KProcess::quote(s); } 00561 00562 void KPrinterImpl::saveOptions(const QMap<QString,QString>& opts) 00563 { 00564 m_options = opts; 00565 saveAppOptions(); 00566 } 00567 00568 void KPrinterImpl::loadAppOptions() 00569 { 00570 KConfig *conf = KGlobal::config(); 00571 conf->setGroup("KPrinter Settings"); 00572 QStringList opts = conf->readListEntry("ApplicationOptions"); 00573 for (uint i=0; i<opts.count(); i+=2) 00574 if (opts[i].startsWith("app-")) 00575 m_options[opts[i]] = opts[i+1]; 00576 } 00577 00578 void KPrinterImpl::saveAppOptions() 00579 { 00580 QStringList optlist; 00581 for (QMap<QString,QString>::ConstIterator it=m_options.begin(); it!=m_options.end(); ++it) 00582 if (it.key().startsWith("app-")) 00583 optlist << it.key() << it.data(); 00584 00585 KConfig *conf = KGlobal::config(); 00586 conf->setGroup("KPrinter Settings"); 00587 conf->writeEntry("ApplicationOptions", optlist); 00588 } 00589 00590 #include "kprinterimpl.moc"
KDE Logo
This file is part of the documentation for kdeprint Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 30 22:55:50 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003