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(*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 (!filter)
00354 {
00355 printer->setErrorMessage(i18n("<p>Could not load filter description for <b>%1</b>.</p>").arg(flist[i]));
00356 return -1;
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));
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
00467
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
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
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() + "}");
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"