/*************************************************************************** cprinter.cpp - description ------------------- begin : Fri Aug 30 2002 reworked : Sun Jul 27 2003 reworked : Thu Mar 27 2005 copyright : (C) 2002-2005 by Serghei Amelian email : serghei.amelian@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include "../config.h" #ifndef HAVE_ROUND // this function working only for positive numbers double round(double x) { double t = ceil(x); if(t - x > 0.5) t -= 1.0; return (t); } #endif #include #include #include #include #include #include #include #include #include #include #include "cprinter.h" #include "csettings.h" #include "qfrpostscript.h" #include "qfrspinbox.h" #include "qfrtiffio.h" #include "../pix/page1.img" #include "../pix/page2.img" #include "../pix/page4.img" static int pageSizes[3][2] = { { 595, 842 }, { 612, 1008 }, { 612, 792 } }; static const char *pageTypes[] = { "ISO A4", "Legal", "Letter", NULL }; static int findPageType(const QString &pageType) { for(int i = 0; pageTypes[i]; i++) if(pageTypes[i] == pageType) return i; return 0; } QfrPrinterProp::QfrPrinterProp(QWidget *parent) : DlgPrinterProp(parent) { connect(unit, SIGNAL(activated(const QString&)), this, SLOT(changeUnit(const QString&))); connect(pslevel, SIGNAL(activated(const QString&)), this, SLOT(changeLevel(const QString&))); } void QfrPrinterProp::changeUnit(const QString &un) { if(currentUnit == un) return; if(un == "cm") { top->setValue((int)(round(top->value() * 2.54))); bottom->setValue((int)(round(bottom->value() * 2.54))); left->setValue((int)(round(left->value() * 2.54))); right->setValue((int)(round(right->value() * 2.54))); } else { top->setValue((int)(round(top->value() / 2.54))); bottom->setValue((int)(round(bottom->value() / 2.54))); left->setValue((int)(round(left->value() / 2.54))); right->setValue((int)(round(right->value() / 2.54))); } currentUnit = un; } void QfrPrinterProp::changeLevel(const QString &str) { if("1" != str) QMessageBox::warning(0, tr("Postscript Level"), tr("NOTE: This postscript level is experimental.")); } QfrPrinterDlg::QfrPrinterDlg() { QVBoxLayout *bl1 = new QVBoxLayout(this, 11, 6); gb1 = new QGroupBox(tr("Printer"), this); gb1->setColumnLayout(0, Qt::Vertical); gb1->layout()->setSpacing(6); gb1->layout()->setMargin(11); bl1->addWidget(gb1); QHBoxLayout *bl2 = new QHBoxLayout(gb1->layout()); cb_printers = new QComboBox(gb1); bl2->addWidget(cb_printers); pb_properties = new QPushButton(tr("&Properties"), gb1); pb_properties->setSizePolicy(QSizePolicy((QSizePolicy::SizeType)0, (QSizePolicy::SizeType)0, 0, 0, pb_properties->sizePolicy().hasHeightForWidth())); bl2->addWidget(pb_properties); QHBoxLayout *bl3 = new QHBoxLayout(0, 0, 6); bl1->addLayout(bl3); // range printing section gb2 = new QButtonGroup(tr("Range"), this); gb2->setColumnLayout(0, Qt::Vertical); gb2->layout()->setSpacing(6); gb2->layout()->setMargin(11); bl3->addWidget(gb2); QRadioButton *rbAll = new QRadioButton(tr("&All"), gb2); rbAll->toggle(); gb2->layout()->add(rbAll); QRadioButton *rbCurrent = new QRadioButton(tr("&Current"), gb2); gb2->layout()->add(rbCurrent); // range (from - to) QHBoxLayout *bl5 = new QHBoxLayout(0,0,15); gb2->layout()->addItem(bl5); QRadioButton *rbRange = new QRadioButton(tr("&From"), gb2); bl5->addWidget(rbRange); sbFrom = new QSpinBox(gb2); bl5->addWidget(sbFrom); sbFrom->setMinValue(1); bl5->addWidget(new QLabel(tr("to"), gb2)); sbTo = new QSpinBox(gb2); bl5->addWidget(sbTo); sbTo->setMinValue(1); bl5->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); // pages per sheet gb3 = new QButtonGroup(tr("Pages per sheet"), this); bl3->addWidget(gb3); gb3->setColumnLayout(0, Qt::Vertical); gb3->layout()->setSpacing(8); gb3->layout()->setMargin(11); QGridLayout *gl1 = new QGridLayout(gb3->layout()); gl1->setAlignment(Qt::AlignTop); QVBoxLayout *bl4 = new QVBoxLayout(0, 0, 6); gl1->addLayout(bl4, 0, 0); QRadioButton *rb1 = new QRadioButton(tr("&1"), gb3); bl4->addWidget(rb1); rb1->toggle(); QRadioButton *rb2 = new QRadioButton(tr("&2"), gb3); bl4->addWidget(rb2); QRadioButton *rb4 = new QRadioButton(tr("&4"), gb3); bl4->addWidget(rb4); gb4 = new QGroupBox(tr("Miscellaneous"), this); bl1->addWidget(gb4); gb4->setColumnLayout(0, Qt::Vertical); gb4->layout()->setSpacing(6); gb4->layout()->setMargin(11); cb1 = new QCheckBox(tr("&Remember \"Pages Per Sheet\" option"), gb4); gb4->layout()->add(cb1); cb2 = new QCheckBox(tr("&Draw border around pages"), gb4); gb4->layout()->add(cb2); gl1->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 1); l1 = new QLabel(gb3); gl1->addWidget(l1, 0, 2); l1->setScaledContents(false); pages(0); gl1->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 3); bl1->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); QHBoxLayout *bl6 = new QHBoxLayout(0, 0, 6); bl1->addLayout(bl6); bl6->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); pb_print = new QPushButton(tr("Print"), this); bl6->addWidget(pb_print); QPushButton *pb_cancel = new QPushButton(tr("Cancel"), this); bl6->addWidget(pb_cancel); bl6->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); if(pb_cancel->sizeHint().width() > pb_print->sizeHint().width()) pb_print->setFixedSize(pb_cancel->sizeHint()); else pb_cancel->setFixedSize(pb_print->sizeHint()); gb1->setEnabled(false); gb2->setEnabled(false); gb3->setEnabled(false); gb4->setEnabled(false); pb_print->setEnabled(false); clearWState(WState_Polished); connect(pb_cancel, SIGNAL(clicked()), this, SLOT(reject())); QString lpccmd = lpcpath(); if(lpccmd.isEmpty()) { QMessageBox::critical(this, tr("Error"), tr("\"lpc\" command not found in standard locations.\nThis mean that we can't read which printers are installed on your system.")); return; } lpc = new QProcess(this); connect(pb_properties, SIGNAL(clicked()), this, SLOT(properties())); connect(gb3, SIGNAL(clicked(int)), this, SLOT(pages(int))); connect(sbFrom, SIGNAL(valueChanged(int)), this, SLOT(fromChanged(int))); connect(sbTo, SIGNAL(valueChanged(int)), this, SLOT(toChanged(int))); connect(lpc, SIGNAL(readyReadStdout()), this, SLOT(readStdout())); connect(lpc, SIGNAL(readyReadStderr()), this, SLOT(readStderr())); connect(lpc, SIGNAL(processExited()), this, SLOT(donelpc()) ); connect(pb_print, SIGNAL(clicked()), this, SLOT(accept())); connect(cb_printers, SIGNAL(activated(const QString&)), this, SLOT(loadPrinterSettings(const QString&))); //lpc->addArgument("lpstat"); //lpc->addArgument("-v"); lpc->addArgument(lpccmd); lpc->addArgument("status"); // first we trying "lpc status" (CUPS, lpr-ng) // if lpc raise an error we will try "lpc status all" (BSD style lpr) firstLine = true; tryBsdStyle = false; if(!lpc->start()) { } } void QfrPrinterDlg::setNumberOfDirectories(int dirs) { sbFrom->setMaxValue(dirs); sbTo->setMaxValue(dirs); sbTo->setValue(dirs); } void QfrPrinterDlg::pages(int button) { QPixmap pix; switch(button) { case 0: pix.loadFromData(page1_png, len_page1_png); break; case 1: pix.loadFromData(page2_png, len_page2_png); break; case 2: pix.loadFromData(page4_png, len_page4_png); break; } l1->setPixmap(pix); } void QfrPrinterDlg::properties() { QfrPrinterProp dlg; dlg.cmd->setText(cmd); int idx = 0; while(idx < dlg.paper->count() && dlg.paper->text(idx) != paper) idx++; dlg.paper->setCurrentItem(idx < dlg.paper->count() ? idx : 0); idx = 0; while(idx < dlg.unit->count() && dlg.unit->text(idx) != unit) idx++; dlg.unit->setCurrentItem(idx < dlg.unit->count() ? idx : 0); dlg.currentUnit = unit; dlg.pslevel->setCurrentItem(pslevel - 1); dlg.top->setValue(int(round(top * 10))); dlg.bottom->setValue(int(round(bottom * 10))); dlg.left->setValue(int(round(left * 10))); dlg.right->setValue(int(round(right * 10))); if(dlg.exec()) { cmd = dlg.cmd->text(); pslevel = dlg.pslevel->currentItem() + 1; unit = dlg.unit->currentText(); paper = dlg.paper->currentText(); top = dlg.top->value() / 10.; bottom = dlg.bottom->value() / 10.; left = dlg.left->value() / 10.; right = dlg.right->value() / 10.; CSettings s; s.beginGroup("/printer-" + cb_printers->currentText()); s.writeEntry("cmd", cmd); s.writeEntry("paper", paper); s.writeEntry("unit", unit); s.writeEntry("pslevel", pslevel); s.writeEntry("top", top); s.writeEntry("bottom", bottom); s.writeEntry("left", left); s.writeEntry("right", right); } } void QfrPrinterDlg::fromChanged(int val) { if(val > sbTo->value()) sbTo->setValue(val); } void QfrPrinterDlg::toChanged(int val) { if(val < sbFrom->value()) sbFrom->setValue(val); } void QfrPrinterDlg::readStdout() { if(lpc->canReadLineStdout()) { while(lpc->canReadLineStdout()) { QString line = lpc->readLineStdout(); if(firstLine) { firstLine = false; if("usage:" == line.left(6)) // checking for BSD-style lpr { lpc->addArgument("all"); tryBsdStyle = true; return; } else if("Printer" == line.stripWhiteSpace().left(7)) // checking for lpr-ng { rx.setPattern("^(\\S+)@"); continue; } else rx.setPattern("^(\\S+.*):$"); //rx.setPattern( "^device for (\\S+.):" ); // cups lpstat } if(-1 != rx.search(line)) cb_printers->insertItem(rx.cap(1)); } } } void QfrPrinterDlg::readStderr() { if(lpc->canReadLineStderr()) { qWarning(lpc->readLineStderr()); } } void QfrPrinterDlg::donelpc() { if(tryBsdStyle) { firstLine = true; tryBsdStyle = false; lpc->start(); } else if(cb_printers->count()) { CSettings s; s.beginGroup("/printer"); QString defaultPrinter = s.readEntry("default"); int pps = s.readNumEntry("pagespersheet", 0); if(pps > 0) { cb1->setChecked(true); switch(pps) { case 1: gb3->setButton(0); pages(0); break; case 2: gb3->setButton(1); pages(1); break; case 4: gb3->setButton(2); pages(2); break; } } cb2->setChecked(s.readBoolEntry("border", true)); int idx = 0; while(idx < cb_printers->count() && cb_printers->text(idx) != defaultPrinter) idx++; cb_printers->setCurrentItem(idx < cb_printers->count() ? idx : 0); loadPrinterSettings(cb_printers->currentText()); gb1->setEnabled(true); gb2->setEnabled(true); gb3->setEnabled(true); gb4->setEnabled(true); pb_print->setEnabled(true); pb_print->setFocus(); } else QMessageBox::critical(this, tr("Error"), tr("I can't find any printer installed on your system.\nCheck if printing service (CUPS) is started.")); } void QfrPrinterDlg::loadPrinterSettings(const QString &string) { CSettings s; s.beginGroup("/printer-" + string); cmd = s.readEntry("cmd", "lp -d {PRINTER}"); paper = s.readEntry("paper", "ISO A4"); unit = s.readEntry("unit", "cm"); pslevel = s.readNumEntry("pslevel", 1); top = s.readDoubleEntry("top", 0.5); bottom = s.readDoubleEntry("bottom", 0.5); left = s.readDoubleEntry("left", 0.5); right = s.readDoubleEntry("right", 0.5); } QString QfrPrinterDlg::lpcpath() const { const char *paths[] = { "/usr/sbin", "/usr/bin", "/usr/local/sbin", "/usr/local/bin", NULL }; for(int i = 0; paths[i]; i++) if(QFile::exists(paths[i] + QString("/lpc"))) return paths[i] + QString("/lpc"); else if(QFile::exists(paths[i] + QString("/lpc.cups"))) return paths[i] + QString("/lpc.cups"); return ""; } CPrinter::CPrinter() { } void CPrinter::start(QfrTiffIO &tif) { QfrPrinterDlg dlg; dlg.setNumberOfDirectories(tif.NumberOfDirectories()); LOOP: if(dlg.exec()) { // photometric interpretation short pmi = 0; tif.GetField(TIFFTAG_PHOTOMETRIC, &pmi); // samples per pixel short samples = 1; tif.GetField(TIFFTAG_SAMPLESPERPIXEL, &samples); if((3 < pmi || 1 != samples) && 1 == dlg.pslevel) { QMessageBox::warning(0, QObject::tr("Print"), QObject::tr("Sorry, printing color images is not yet finished for Postscript Level 1.\nIf you want to print color tiffs chose Postscript Level 2 or 3.\nAnyway, note that Postscript Level 2/3 is experimental and you can found some bugs.")); goto LOOP; } QString printer = dlg.cb_printers->currentText(); CSettings s; s.beginGroup("/printer"); s.writeEntry("default", printer); int pagesPerSheet = 0; //switch(dlg.gb3->selectedId()) // pages per sheet switch(dlg.gb3->id(dlg.gb3->selected())) // compatibility with Qt 3.1 { case 0: pagesPerSheet = 1; break; case 1: pagesPerSheet = 2; break; case 2: pagesPerSheet = 4; break; } if(dlg.cb1->isChecked()) s.writeEntry("pagespersheet", pagesPerSheet); else s.removeEntry("pagespersheet"); s.writeEntry("border", dlg.cb2->isChecked()); QString cmd = dlg.cmd; cmd.replace("{PRINTER}", "\"" + printer + "\""); FILE *lpr = popen(cmd, "w"); if(!lpr) return; int from = 0; int to = 0; //switch(dlg.gb2->selectedId()) // range switch(dlg.gb2->id(dlg.gb2->selected())) // compatibility with Qt 3.1 { case 0: from = 1; to = tif.NumberOfDirectories(); break; case 1: from = to = tif.CurrentDirectory(); break; case 2: from = dlg.sbFrom->value(); to = dlg.sbTo->value(); break; } int pageSize = findPageType(dlg.paper); QfrPostscript ps(lpr); float conv = 72 / (dlg.unit == "cm" ? 2.54 : 1.); if(!ps.beginDocument( tif.FileName(), pagesPerSheet, pageSizes[pageSize][0], pageSizes[pageSize][1], (int)round(dlg.top * conv), (int)round(dlg.bottom * conv), (int)round(dlg.left * conv), (int)round(dlg.right * conv), dlg.pslevel, dlg.cb2->isChecked())) { qWarning("Error writing to spooler: %s", strerror(errno)); pclose(lpr); return; } for(int i = from - 1; i < to; i++) { tif.SetDirectory(i); if(!ps.pushImage(tif)) { qWarning("Error writing to spooler: %s", strerror(errno)); pclose(lpr); return; } } ps.endDocument(); pclose(lpr); } }