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