TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

607 lines
18KB

  1. /*
  2. * This file is part of the KDE libraries
  3. * Copyright (c) 2001 Michael Goffioul <tdeprint@swing.be>
  4. *
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Library General Public
  8. * License version 2 as published by the Free Software Foundation.
  9. *
  10. * This library is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * Library General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Library General Public License
  16. * along with this library; see the file COPYING.LIB. If not, write to
  17. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  18. * Boston, MA 02110-1301, USA.
  19. **/
  20. #include "kprinterimpl.h"
  21. #include "kprinter.h"
  22. #include "kmfactory.h"
  23. #include "kmmanager.h"
  24. #include "kmuimanager.h"
  25. #include "kxmlcommand.h"
  26. #include "kmspecialmanager.h"
  27. #include "kmthreadjob.h"
  28. #include "kmprinter.h"
  29. #include "driver.h"
  30. #include <tqfile.h>
  31. #include <tqregexp.h>
  32. #include <kinputdialog.h>
  33. #include <klocale.h>
  34. #include <dcopclient.h>
  35. #include <kapplication.h>
  36. #include <kstandarddirs.h>
  37. #include <kdatastream.h>
  38. #include <kdebug.h>
  39. #include <kmimemagic.h>
  40. #include <kmessagebox.h>
  41. #include <kprocess.h>
  42. #include <kconfig.h>
  43. #include <stdlib.h>
  44. void dumpOptions(const TQMap<TQString,TQString>&);
  45. void initEditPrinter(KMPrinter *p)
  46. {
  47. if (!p->isEdited())
  48. {
  49. p->setEditedOptions(p->defaultOptions());
  50. p->setEdited(true);
  51. }
  52. }
  53. //****************************************************************************************
  54. KPrinterImpl::KPrinterImpl(TQObject *parent, const char *name)
  55. : TQObject(parent,name)
  56. {
  57. loadAppOptions();
  58. }
  59. KPrinterImpl::~KPrinterImpl()
  60. {
  61. }
  62. void KPrinterImpl::preparePrinting(KPrinter *printer)
  63. {
  64. // page size -> try to find page size and margins from driver file
  65. // use "PageSize" as option name to find the wanted page size. It's
  66. // up to the driver loader to use that option name.
  67. KMManager *mgr = KMFactory::self()->manager();
  68. DrMain *driver = mgr->loadPrinterDriver(mgr->findPrinter(printer->printerName()), false);
  69. if (driver)
  70. {
  71. // Find the page size:
  72. // 1) print option
  73. // 2) default driver option
  74. QString psname = printer->option("PageSize");
  75. if (psname.isEmpty())
  76. {
  77. DrListOption *opt = (DrListOption*)driver->findOption("PageSize");
  78. if (opt) psname = opt->get("default");
  79. }
  80. if (!psname.isEmpty())
  81. {
  82. printer->setOption("kde-pagesize",TQString::number((int)pageNameToPageSize(psname)));
  83. DrPageSize *ps = driver->findPageSize(psname);
  84. if (ps)
  85. {
  86. printer->setRealPageSize( ps );
  87. }
  88. }
  89. // Find the numerical resolution
  90. // 1) print option (Resolution)
  91. // 2) default driver option (Resolution)
  92. // 3) default printer resolution
  93. // The resolution must have the format: XXXdpi or XXXxYYYdpi. In the second
  94. // case the YYY value is used as resolution.
  95. TQString res = printer->option( "Resolution" );
  96. if ( res.isEmpty() )
  97. {
  98. DrBase *opt = driver->findOption( "Resolution" );
  99. if ( opt )
  100. res = opt->get( "default" );
  101. if ( res.isEmpty() )
  102. res = driver->get( "resolution" );
  103. }
  104. if ( !res.isEmpty() )
  105. {
  106. TQRegExp re( "(\\d+)(?:x(\\d+))?dpi" );
  107. if ( re.search( res ) != -1 )
  108. {
  109. if ( !re.cap( 2 ).isEmpty() )
  110. printer->setOption( "kde-resolution", re.cap( 2 ) );
  111. else
  112. printer->setOption( "kde-resolution", re.cap( 1 ) );
  113. }
  114. }
  115. // Find the supported fonts
  116. TQString fonts = driver->get( "fonts" );
  117. if ( !fonts.isEmpty() )
  118. printer->setOption( "kde-fonts", fonts );
  119. delete driver;
  120. }
  121. }
  122. bool KPrinterImpl::setupCommand(TQString&, KPrinter*)
  123. {
  124. return false;
  125. }
  126. bool KPrinterImpl::printFiles(KPrinter *p, const TQStringList& f, bool flag)
  127. {
  128. TQString cmd;
  129. if (p->option("kde-isspecial") == "1")
  130. {
  131. if (p->option("kde-special-command").isEmpty() && p->outputToFile())
  132. {
  133. KURL url( p->outputFileName() );
  134. if ( !url.isLocalFile() )
  135. {
  136. cmd = ( flag ? "mv" : "cp" ) + ( " %in $out{" + p->outputFileName() + "}" );
  137. }
  138. else
  139. {
  140. if (f.count() > 1)
  141. {
  142. p->setErrorMessage(i18n("Cannot copy multiple files into one file."));
  143. return false;
  144. }
  145. else
  146. {
  147. KProcess proc;
  148. proc << (flag?"mv":"cp") << f[0] << p->outputFileName();
  149. if (!proc.start(KProcess::Block) || !proc.normalExit() || proc.exitStatus() != 0)
  150. {
  151. p->setErrorMessage(i18n("Cannot save print file to %1. Check that you have write access to it.").arg(p->outputFileName()));
  152. return false;
  153. }
  154. }
  155. return true;
  156. }
  157. }
  158. else if (!setupSpecialCommand(cmd,p,f))
  159. return false;
  160. }
  161. else if (!setupCommand(cmd,p))
  162. return false;
  163. return startPrinting(cmd,p,f,flag);
  164. }
  165. void KPrinterImpl::broadcastOption(const TQString& key, const TQString& value)
  166. {
  167. // force printer listing if not done yet (or reload needed)
  168. TQPtrList<KMPrinter> *printers = KMFactory::self()->manager()->printerListComplete(false);
  169. if (printers)
  170. {
  171. TQPtrListIterator<KMPrinter> it(*printers);
  172. for (;it.current();++it)
  173. {
  174. initEditPrinter(it.current());
  175. it.current()->setEditedOption(key,value);
  176. }
  177. }
  178. }
  179. int KPrinterImpl::dcopPrint(const TQString& cmd, const TQStringList& files, bool removeflag)
  180. {
  181. kdDebug(500) << "tdeprint: print command: " << cmd << endl;
  182. int result = 0;
  183. DCOPClient *dclient = kapp->dcopClient();
  184. if (!dclient || (!dclient->isAttached() && !dclient->attach()))
  185. {
  186. return result;
  187. }
  188. TQByteArray data, replyData;
  189. TQCString replyType;
  190. TQDataStream arg( data, IO_WriteOnly );
  191. arg << cmd;
  192. arg << files;
  193. arg << removeflag;
  194. if (dclient->call( "kded", "tdeprintd", "print(TQString,TQStringList,bool)", data, replyType, replyData ))
  195. {
  196. if (replyType == "int")
  197. {
  198. TQDataStream _reply_stream( replyData, IO_ReadOnly );
  199. _reply_stream >> result;
  200. }
  201. }
  202. return result;
  203. }
  204. void KPrinterImpl::statusMessage(const TQString& msg, KPrinter *printer)
  205. {
  206. kdDebug(500) << "tdeprint: status message: " << msg << endl;
  207. KConfig *conf = KMFactory::self()->printConfig();
  208. conf->setGroup("General");
  209. if (!conf->readBoolEntry("ShowStatusMsg", true))
  210. return;
  211. TQString message(msg);
  212. if (printer && !msg.isEmpty())
  213. message.prepend(i18n("Printing document: %1").arg(printer->docName())+"\n");
  214. DCOPClient *dclient = kapp->dcopClient();
  215. if (!dclient || (!dclient->isAttached() && !dclient->attach()))
  216. {
  217. return;
  218. }
  219. TQByteArray data;
  220. TQDataStream arg( data, IO_WriteOnly );
  221. arg << message;
  222. arg << (int)getpid();
  223. arg << kapp->caption();
  224. dclient->send( "kded", "tdeprintd", "statusMessage(TQString,int,TQString)", data );
  225. }
  226. bool KPrinterImpl::startPrinting(const TQString& cmd, KPrinter *printer, const TQStringList& files, bool flag)
  227. {
  228. statusMessage(i18n("Sending print data to printer: %1").arg(printer->printerName()), printer);
  229. TQString command(cmd), filestr;
  230. TQStringList printfiles;
  231. if (command.find("%in") == -1) command.append(" %in");
  232. for (TQStringList::ConstIterator it=files.begin(); it!=files.end(); ++it)
  233. if (TQFile::exists(*it))
  234. {
  235. // quote filenames
  236. filestr.append(quote(*it)).append(" ");
  237. printfiles.append(*it);
  238. }
  239. else
  240. kdDebug(500) << "File not found: " << (*it) << endl;
  241. if (printfiles.count() > 0)
  242. {
  243. command.replace("%in",filestr);
  244. int pid = dcopPrint(command,files,flag);
  245. if (pid > 0)
  246. {
  247. if (printer)
  248. KMThreadJob::createJob(pid,printer->printerName(),printer->docName(),getenv("USER"),0);
  249. return true;
  250. }
  251. else
  252. {
  253. TQString msg = i18n("Unable to start child print process. ");
  254. if (pid == 0)
  255. msg += i18n("The TDE print server (<b>tdeprintd</b>) could not be contacted. Check that this server is running.");
  256. else
  257. msg += i18n("1 is the command that <files> is given to", "Check the command syntax:\n%1 <files>").arg(cmd);
  258. printer->setErrorMessage(msg);
  259. return false;
  260. }
  261. }
  262. //else
  263. //{
  264. printer->setErrorMessage(i18n("No valid file was found for printing. Operation aborted."));
  265. return false;
  266. //}
  267. }
  268. TQString KPrinterImpl::tempFile()
  269. {
  270. TQString f;
  271. // be sure the file doesn't exist
  272. do f = locateLocal("tmp","tdeprint_") + KApplication::randomString(8); while (TQFile::exists(f));
  273. return f;
  274. }
  275. int KPrinterImpl::filterFiles(KPrinter *printer, TQStringList& files, bool flag)
  276. {
  277. TQStringList flist = TQStringList::split(',',printer->option("_kde-filters"),false);
  278. TQMap<TQString,TQString> opts = printer->options();
  279. // generic page selection mechanism (using psselect filter)
  280. // do it only if:
  281. // - using system-side page selection
  282. // - special printer or regular printer without page selection support in current plugin
  283. // - one of the page selection option has been selected to non default value
  284. // Action -> add the psselect filter to the filter chain.
  285. if (printer->pageSelection() == KPrinter::SystemSide &&
  286. (printer->option("kde-isspecial") == "1" || !(KMFactory::self()->uiManager()->pluginPageCap() & KMUiManager::PSSelect)) &&
  287. (printer->pageOrder() == KPrinter::LastPageFirst ||
  288. !printer->option("kde-range").isEmpty() ||
  289. printer->pageSet() != KPrinter::AllPages))
  290. {
  291. if (flist.findIndex("psselect") == -1)
  292. {
  293. int index = KXmlCommandManager::self()->insertCommand(flist, "psselect", false);
  294. if (index == -1 || !KXmlCommandManager::self()->checkCommand("psselect"))
  295. {
  296. printer->setErrorMessage(i18n("<p>Unable to perform the requested page selection. The filter <b>psselect</b> "
  297. "cannot be inserted in the current filter chain. See <b>Filter</b> tab in the "
  298. "printer properties dialog for further information.</p>"));
  299. return -1;
  300. }
  301. }
  302. if (printer->pageOrder() == KPrinter::LastPageFirst)
  303. opts["_kde-psselect-order"] = "r";
  304. if (!printer->option("kde-range").isEmpty())
  305. opts["_kde-psselect-range"] = printer->option("kde-range");
  306. if (printer->pageSet() != KPrinter::AllPages)
  307. opts["_kde-psselect-set"] = (printer->pageSet() == KPrinter::OddPages ? "-o" : "-e");
  308. }
  309. return doFilterFiles(printer, files, flist, opts, flag);
  310. }
  311. int KPrinterImpl::doFilterFiles(KPrinter *printer, TQStringList& files, const TQStringList& flist, const TQMap<TQString,TQString>& opts, bool flag)
  312. {
  313. // nothing to do
  314. if (flist.count() == 0)
  315. return 0;
  316. TQString filtercmd;
  317. TQStringList inputMimeTypes;
  318. for (uint i=0;i<flist.count();i++)
  319. {
  320. KXmlCommand *filter = KXmlCommandManager::self()->loadCommand(flist[i]);
  321. if (!filter)
  322. {
  323. printer->setErrorMessage(i18n("<p>Could not load filter description for <b>%1</b>.</p>").arg(flist[i]));
  324. return -1; // Error
  325. }
  326. if (i == 0)
  327. inputMimeTypes = filter->inputMimeTypes();
  328. TQString subcmd = filter->buildCommand(opts,(i>0),(i<(flist.count()-1)));
  329. delete filter;
  330. if (!subcmd.isEmpty())
  331. {
  332. filtercmd.append(subcmd);
  333. if (i < flist.count()-1)
  334. filtercmd.append("| ");
  335. }
  336. else
  337. {
  338. printer->setErrorMessage(i18n("<p>Error while reading filter description for <b>%1</b>. Empty command line received.</p>").arg(flist[i]));
  339. return -1;
  340. }
  341. }
  342. kdDebug(500) << "tdeprint: filter command: " << filtercmd << endl;
  343. TQString rin("%in"), rout("%out"), rpsl("%psl"), rpsu("%psu");
  344. TQString ps = pageSizeToPageName( printer->option( "kde-printsize" ).isEmpty() ? printer->pageSize() : ( KPrinter::PageSize )printer->option( "kde-printsize" ).toInt() );
  345. for (TQStringList::Iterator it=files.begin(); it!=files.end(); ++it)
  346. {
  347. TQString mime = KMimeMagic::self()->findFileType(*it)->mimeType();
  348. if (inputMimeTypes.find(mime) == inputMimeTypes.end())
  349. {
  350. if (KMessageBox::warningContinueCancel(0,
  351. "<p>" + i18n("The MIME type %1 is not supported as input of the filter chain "
  352. "(this may happen with non-CUPS spoolers when performing page selection "
  353. "on a non-PostScript file). Do you want TDE to convert the file to a supported "
  354. "format?</p>").arg(mime),
  355. TQString::null, i18n("Convert")) == KMessageBox::Continue)
  356. {
  357. TQStringList ff;
  358. int done(0);
  359. ff << *it;
  360. while (done == 0)
  361. {
  362. bool ok(false);
  363. TQString targetMime = KInputDialog::getItem(
  364. i18n("Select MIME Type"),
  365. i18n("Select the target format for the conversion:"),
  366. inputMimeTypes, 0, false, &ok);
  367. if (!ok)
  368. {
  369. printer->setErrorMessage(i18n("Operation aborted."));
  370. return -1;
  371. }
  372. TQStringList filters = KXmlCommandManager::self()->autoConvert(mime, targetMime);
  373. if (filters.count() == 0)
  374. {
  375. KMessageBox::error(0, i18n("No appropriate filter found. Select another target format."));
  376. }
  377. else
  378. {
  379. int result = doFilterFiles(printer, ff, filters, TQMap<TQString,TQString>(), flag);
  380. if (result == 1)
  381. {
  382. *it = ff[0];
  383. done = 1;
  384. }
  385. else
  386. {
  387. KMessageBox::error(0,
  388. i18n("<qt>Operation failed with message:<br>%1<br>Select another target format.</qt>").arg(printer->errorMessage()));
  389. }
  390. }
  391. }
  392. }
  393. else
  394. {
  395. printer->setErrorMessage(i18n("Operation aborted."));
  396. return -1;
  397. }
  398. }
  399. TQString tmpfile = tempFile();
  400. TQString cmd(filtercmd);
  401. cmd.replace(rout,quote(tmpfile));
  402. cmd.replace(rpsl,ps.lower());
  403. cmd.replace(rpsu,ps);
  404. cmd.replace(rin,quote(*it)); // Replace as last, filename could contain "%psl"
  405. statusMessage(i18n("Filtering print data"), printer);
  406. int status = system(TQFile::encodeName(cmd));
  407. if (status < 0 || WEXITSTATUS(status) == 127)
  408. {
  409. printer->setErrorMessage(i18n("Error while filtering. Command was: <b>%1</b>.").arg(filtercmd));
  410. return -1;
  411. }
  412. if (flag) TQFile::remove(*it);
  413. *it = tmpfile;
  414. }
  415. return 1;
  416. }
  417. int KPrinterImpl::autoConvertFiles(KPrinter *printer, TQStringList& files, bool flag)
  418. {
  419. TQString primaryMimeType = "application/postscript";
  420. TQStringList mimeTypes( primaryMimeType );
  421. if ( printer->option( "kde-isspecial" ) == "1" )
  422. {
  423. if ( !printer->option( "kde-special-command" ).isEmpty() )
  424. {
  425. KXmlCommand *cmd = KXmlCommandManager::self()->loadCommand( printer->option( "kde-special-command" ), true );
  426. if ( cmd )
  427. {
  428. mimeTypes = cmd->inputMimeTypes();
  429. // FIXME: the XML command description should now contain a primiary
  430. // mime type as well. This is a temporary-only solution.
  431. primaryMimeType = mimeTypes[ 0 ];
  432. }
  433. }
  434. }
  435. else
  436. {
  437. KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem());
  438. mimeTypes = info.mimeTypes;
  439. primaryMimeType = info.primaryMimeType;
  440. }
  441. KMFactory::PluginInfo info = KMFactory::self()->pluginInfo(KMFactory::self()->printSystem());
  442. int status(0), result;
  443. for (TQStringList::Iterator it=files.begin(); it!=files.end(); )
  444. {
  445. TQString mime = KMimeMagic::self()->findFileType(*it)->mimeType();
  446. if ( mime == "application/x-zerosize" )
  447. {
  448. // special case of empty file
  449. KMessageBox::information( NULL,
  450. i18n( "<qt>The print file is empty and will be ignored:<p>%1</p></qt>" ).arg( *it ),
  451. TQString::null, "emptyFileNotPrinted" );
  452. if ( flag )
  453. TQFile::remove( *it );
  454. it = files.remove( it );
  455. continue;
  456. }
  457. else if (mimeTypes.findIndex(mime) == -1)
  458. {
  459. if ((result=KMessageBox::warningYesNoCancel(NULL,
  460. i18n("<qt>The file format <em> %1 </em> is not directly supported by the current print system. You "
  461. "now have 3 options: "
  462. "<ul> "
  463. "<li> TDE can attempt to convert this file automatically to a supported format. "
  464. "(Select <em>Convert</em>) </li>"
  465. "<li> You can try to send the file to the printer without any conversion. "
  466. "(Select <em>Keep</em>) </li>"
  467. "<li> You can cancel the printjob. "
  468. "(Select <em>Cancel</em>) </li>"
  469. "</ul> "
  470. "Do you want TDE to attempt and convert this file to %2?</qt>").arg(mime).arg(primaryMimeType),
  471. TQString::null,
  472. i18n("Convert"),
  473. i18n("Keep"),
  474. TQString::fromLatin1("tdeprintAutoConvert"))) == KMessageBox::Yes)
  475. {
  476. // find the filter chain
  477. TQStringList flist = KXmlCommandManager::self()->autoConvert(mime, primaryMimeType);
  478. if (flist.count() == 0)
  479. {
  480. KMessageBox::error(NULL,
  481. i18n("<qt>No appropriate filter was found to convert the file format %1 into %2.<br>"
  482. "<ul>"
  483. "<li>Go to <i>System Options -> Commands</i> to look through the list of "
  484. "possible filters. Each filter executes an external program.</li>"
  485. "<li> See if the required external program is available.on your "
  486. "system.</li>"
  487. "</ul>"
  488. "</qt>").arg(mime).arg(primaryMimeType),
  489. i18n("Print"));
  490. if (flag)
  491. TQFile::remove(*it);
  492. it = files.remove(it);
  493. continue;
  494. }
  495. TQStringList l(*it);
  496. switch (doFilterFiles(printer, l, flist, TQMap<TQString,TQString>(), flag))
  497. {
  498. case -1:
  499. return -1;
  500. case 0:
  501. break;
  502. case 1:
  503. status = 1;
  504. *it = l[0];
  505. break;
  506. }
  507. }
  508. else if (result == KMessageBox::Cancel)
  509. {
  510. files.clear();
  511. return 0;
  512. }
  513. }
  514. ++it;
  515. }
  516. return status;
  517. }
  518. bool KPrinterImpl::setupSpecialCommand(TQString& cmd, KPrinter *p, const TQStringList&)
  519. {
  520. TQString s(p->option("kde-special-command"));
  521. if (s.isEmpty())
  522. {
  523. p->setErrorMessage("Empty command.");
  524. return false;
  525. }
  526. s = KMFactory::self()->specialManager()->setupCommand(s, p->options());
  527. TQString ps = pageSizeToPageName( p->option( "kde-printsize" ).isEmpty() ? p->pageSize() : ( KPrinter::PageSize )p->option( "kde-printsize" ).toInt() );
  528. s.replace("%psl", ps.lower());
  529. s.replace("%psu", ps);
  530. s.replace("%out", "$out{" + p->outputFileName() + "}"); // Replace as last
  531. cmd = s;
  532. return true;
  533. }
  534. TQString KPrinterImpl::quote(const TQString& s)
  535. { return KProcess::quote(s); }
  536. void KPrinterImpl::saveOptions(const TQMap<TQString,TQString>& opts)
  537. {
  538. m_options = opts;
  539. saveAppOptions();
  540. }
  541. void KPrinterImpl::loadAppOptions()
  542. {
  543. KConfig *conf = KGlobal::config();
  544. conf->setGroup("KPrinter Settings");
  545. TQStringList opts = conf->readListEntry("ApplicationOptions");
  546. for (uint i=0; i<opts.count(); i+=2)
  547. if (opts[i].startsWith("app-"))
  548. m_options[opts[i]] = opts[i+1];
  549. }
  550. void KPrinterImpl::saveAppOptions()
  551. {
  552. TQStringList optlist;
  553. for (TQMap<TQString,TQString>::ConstIterator it=m_options.begin(); it!=m_options.end(); ++it)
  554. if (it.key().startsWith("app-"))
  555. optlist << it.key() << it.data();
  556. KConfig *conf = KGlobal::config();
  557. conf->setGroup("KPrinter Settings");
  558. conf->writeEntry("ApplicationOptions", optlist);
  559. }
  560. #include "kprinterimpl.moc"