File indexing completed on 2024-04-14 14:35:55
0001 /************************************************************************ 0002 * * 0003 * This file is part of Kooka, a scanning/OCR application using * 0004 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. * 0005 * * 0006 * Copyright (C) 2003-2016 Klaas Freitag <freitag@suse.de> * 0007 * Jonathan Marten <jjm@keelhaul.me.uk> * 0008 * * 0009 * Kooka is free software; you can redistribute it and/or modify it * 0010 * under the terms of the GNU Library General Public License as * 0011 * published by the Free Software Foundation and appearing in the * 0012 * file COPYING included in the packaging of this file; either * 0013 * version 2 of the License, or (at your option) any later version. * 0014 * * 0015 * As a special exception, permission is given to link this program * 0016 * with any version of the KADMOS OCR/ICR engine (a product of * 0017 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0018 * executable without including the source code for KADMOS in the * 0019 * source distribution. * 0020 * * 0021 * This program is distributed in the hope that it will be useful, * 0022 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0024 * GNU General Public License for more details. * 0025 * * 0026 * You should have received a copy of the GNU General Public * 0027 * License along with this program; see the file COPYING. If * 0028 * not, see <http://www.gnu.org/licenses/>. * 0029 * * 0030 ************************************************************************/ 0031 0032 #include "imgprintdialog.h" 0033 0034 #include <qlayout.h> 0035 #include <qlabel.h> 0036 #include <qbuttongroup.h> 0037 #include <qcheckbox.h> 0038 #include <qcombobox.h> 0039 #include <qradiobutton.h> 0040 #include <qgroupbox.h> 0041 #include <qspinbox.h> 0042 #include <qlineedit.h> 0043 #include <qtimer.h> 0044 #include <qgridlayout.h> 0045 0046 #include <QSignalBlocker> 0047 0048 #include <klocalizedstring.h> 0049 0050 #include "scanimage.h" 0051 0052 #include "kookaprint.h" 0053 #include "kscandevice.h" 0054 #include "dialogbase.h" 0055 #include "kooka_logging.h" 0056 0057 0058 ImgPrintDialog::ImgPrintDialog(ScanImage::Ptr img, KookaPrint *prt, QWidget *pnt) 0059 : QWidget(pnt) 0060 { 0061 m_image = img; // record the image 0062 qCDebug(KOOKA_LOG) << "image size" << img->size(); 0063 mPrinter = prt; // record the printer 0064 0065 setWindowTitle(i18nc("@title:tab", "Image")); // used as tab title 0066 0067 // Timer for delayed/combined updates of print parameters 0068 mUpdateTimer = new QTimer(this); 0069 mUpdateTimer->setSingleShot(true); 0070 // The default button auto-repeat delay is set to 300 0071 // in qtbase/src/widgets/widgets/qabstractbutton.cpp 0072 mUpdateTimer->setInterval(320); // longer than button auto-repeat 0073 connect(mUpdateTimer, &QTimer::timeout, this, &ImgPrintDialog::updatePrintParameters); 0074 0075 QVBoxLayout *layout = new QVBoxLayout(this); 0076 0077 // Each control (or group of controls) created here that affect the printer 0078 // page or layout needs to be connected to mUpdateTimer->start(). This will 0079 // call updatePrintParameters() on a delay to combine updates from auto-repeating 0080 // controls. Those that do not affect the printer page/layout do not need to 0081 // do this, the caller will perform a final updatePrintParameters() before 0082 // starting to print. 0083 0084 // "Scaling" group box 0085 QGroupBox *grp = new QGroupBox(i18nc("@title:group", "Scaling"), this); 0086 QGridLayout *gl = new QGridLayout(grp); 0087 0088 m_scaleRadios = new QButtonGroup(this); 0089 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) 0090 connect(m_scaleRadios, &QButtonGroup::idClicked, this, &ImgPrintDialog::slotScaleChanged); 0091 connect(m_scaleRadios, &QButtonGroup::idClicked, mUpdateTimer, QOverload<>::of(&QTimer::start)); 0092 #else 0093 connect(m_scaleRadios, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), 0094 this, &ImgPrintDialog::slotScaleChanged); 0095 connect(m_scaleRadios, static_cast<void (QButtonGroup::*)(int)>(&QButtonGroup::buttonClicked), 0096 mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0097 #endif 0098 0099 // Option 1: ScaleScreen 0100 QRadioButton *rb = new QRadioButton(i18nc("@option:radio", "Size as on screen"), this); 0101 rb->setToolTip(i18nc("@info:tooltip", "<div>Print at the same size as seen on screen, determined by the screen resolution.</div>")); 0102 m_scaleRadios->addButton(rb, KookaPrint::ScaleScreen); 0103 gl->addWidget(rb, 0, 0, Qt::AlignLeft); 0104 0105 QLabel *l = new QLabel(i18n("Screen resolution:"), this); 0106 gl->addWidget(l, 0, 2, Qt::AlignRight); 0107 0108 m_screenRes = new QLineEdit(this); // for a consistent appearance 0109 m_screenRes->setReadOnly(true); 0110 m_screenRes->setToolTip(i18nc("@info:tooltip", "<div>This is the current screen resolution. It cannot be changed here.</div>")); 0111 gl->addWidget(m_screenRes, 0, 3); 0112 0113 // Option 2: ScaleScan 0114 rb = new QRadioButton(i18nc("@option:radio", "Size as scanned"), this); 0115 rb->setToolTip(i18nc("@info:tooltip", "<div>Print at a size determined by the scan resolution. The resolution is saved with the image and used if available; if it is not, it can be entered here.</div>")); 0116 m_scaleRadios->addButton(rb, KookaPrint::ScaleScan); 0117 gl->addWidget(rb, 1, 0, Qt::AlignLeft); 0118 0119 l = new QLabel(i18n("Scan resolution:"), this); 0120 gl->addWidget(l, 1, 2, Qt::AlignRight); 0121 0122 m_dpi = new QSpinBox(this); 0123 m_dpi->setRange(50, 1200); 0124 m_dpi->setSuffix(i18nc("@item:intext abbreviation for 'dots per inch'", " dpi")); 0125 m_dpi->setToolTip(i18nc("@info:tooltip", "<div>This is the scan resolution as saved with the image. It can be changed if necessary.</div>")); 0126 connect(m_dpi, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0127 mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0128 l->setBuddy(m_dpi); 0129 gl->addWidget(m_dpi, 1, 3); 0130 0131 // Option 3: ScaleCustom 0132 rb = new QRadioButton(i18nc("@option:radio", "Custom size"), this); 0133 rb->setToolTip(i18nc("@info:tooltip", "<div>Print scaled to the specified size. The image is centered on the paper.</div>")); 0134 m_scaleRadios->addButton(rb, KookaPrint::ScaleCustom); 0135 gl->addWidget(rb, 2, 0, Qt::AlignLeft); 0136 0137 l = new QLabel(i18nc("@label:spinbox", "Image width:"), this); 0138 gl->addWidget(l, 2, 2, Qt::AlignRight); 0139 0140 m_sizeW = new QSpinBox(this); 0141 m_sizeW->setRange(10, 1000); 0142 m_sizeW->setSuffix(i18nc("@item:intext abbreviation for 'millimetres'", " mm")); 0143 m_sizeW->setToolTip(i18nc("@info:tooltip", "<div>The width at which the image will be printed.</div>")); 0144 connect(m_sizeW, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0145 this, &ImgPrintDialog::slotCustomWidthChanged); 0146 connect(m_sizeW, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0147 mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0148 l->setBuddy(m_sizeW); 0149 gl->addWidget(m_sizeW, 2, 3); 0150 0151 l = new QLabel(i18nc("@label:spinbox", "Image height:"), this); 0152 gl->addWidget(l, 3, 2, Qt::AlignRight); 0153 0154 m_sizeH = new QSpinBox(this); 0155 m_sizeH->setRange(10, 1000); 0156 m_sizeH->setSuffix(i18nc("@item:intext abbreviation for 'millimetres'", " mm")); 0157 m_sizeH->setToolTip(i18nc("@info:tooltip", "<div>The height at which the image will be printed.</div>")); 0158 connect(m_sizeH, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0159 this, &ImgPrintDialog::slotCustomHeightChanged); 0160 connect(m_sizeH, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), 0161 mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0162 l->setBuddy(m_sizeH); 0163 gl->addWidget(m_sizeH, 3, 3); 0164 0165 // Option 4: ScaleFitPage 0166 rb = new QRadioButton(i18nc("@option:radio", "Fit to page"), this); 0167 rb->setToolTip(i18nc("@info:tooltip", "<div>Print using as much of the available space on the paper as possible. The aspect ratio can be maintained.</div>")); 0168 m_scaleRadios->addButton(rb, KookaPrint::ScaleFitPage); 0169 gl->addWidget(rb, 3, 0, Qt::AlignLeft); 0170 0171 gl->setColumnMinimumWidth(1, 2*DialogBase::horizontalSpacing()); 0172 layout->addWidget(grp); 0173 0174 // Horizontal box for "Other Options" and "Print Layout" 0175 QHBoxLayout *hbox = new QHBoxLayout; 0176 0177 // "Other Options" group box 0178 grp = new QGroupBox(i18nc("@title:group", "Other Options"), this); 0179 QVBoxLayout *vbl = new QVBoxLayout(grp); 0180 0181 // Maintain Aspect option 0182 m_ratio = new QCheckBox(i18nc("@option:check", "Maintain aspect ratio"), this); 0183 m_ratio->setToolTip(i18nc("@info:tooltip", "<div>Adjust the height or width to maintain the original aspect ratio of the printed image.</div>")); 0184 connect(m_ratio, &QCheckBox::toggled, this, &ImgPrintDialog::slotAdjustCustomSize); 0185 connect(m_ratio, &QCheckBox::toggled, mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0186 vbl->addWidget(m_ratio); 0187 0188 // Draft Print option 0189 m_psDraft = new QCheckBox(i18nc("@option:check", "Low resolution (fast draft print)"), this); 0190 m_psDraft->setToolTip(i18nc("@info:tooltip", "<div>Print at as low a resolution as possible. This may not work as intended on all printers.</div>")); 0191 vbl->addWidget(m_psDraft); 0192 0193 // Cut Marks option 0194 QHBoxLayout *chb = new QHBoxLayout(this); 0195 0196 l = new QLabel(i18nc("@label:listbox", "Cut marks:"), this); 0197 chb->addWidget(l); 0198 0199 m_cutsCombo = new QComboBox(this); 0200 m_cutsCombo->addItem(i18nc("@item:inlistbox", "None"), KookaPrint::CutMarksNone); 0201 m_cutsCombo->addItem(i18nc("@item:inlistbox", "For multiple pages"), KookaPrint::CutMarksMultiple); 0202 m_cutsCombo->addItem(i18nc("@item:inlistbox", "Always"), KookaPrint::CutMarksAlways); 0203 m_cutsCombo->setToolTip(i18nc("@info:tooltip", "<div>Select whether cut/join marks are printed. The marks will reduce the available printable area.</div>")); 0204 connect(m_cutsCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), 0205 mUpdateTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); 0206 l->setBuddy(m_cutsCombo); 0207 chb->addWidget(m_cutsCombo); 0208 vbl->addLayout(chb); 0209 0210 vbl->addStretch(1); 0211 hbox->addWidget(grp); 0212 0213 // "Print Layout" group box 0214 grp = new QGroupBox(i18nc("@title:group", "Print Layout"), this); 0215 gl = new QGridLayout(grp); 0216 int row = 0; 0217 0218 // The image size in pixels 0219 l = new QLabel(i18nc("@label:textbox", "Image size:"), this); 0220 gl->addWidget(l, row, 0); 0221 mImageSize = new QLabel(i18nc("@info:status width,height pixels", 0222 "%1 x %2 pix", m_image->width(), m_image->height()), this); 0223 gl->addWidget(mImageSize, row, 1); 0224 ++row; 0225 0226 // The available print area on the paper 0227 l = new QLabel(i18nc("@label:textbox", "Available print area:"), this); 0228 gl->addWidget(l, row, 0); 0229 mPrintArea = new QLabel("-", this); 0230 gl->addWidget(mPrintArea, row, 1); 0231 ++row; 0232 0233 // The image print area, allowing for scaling and margins 0234 l = new QLabel(i18nc("@label:textbox", "Image print area:"), this); 0235 gl->addWidget(l, row, 0); 0236 mImageArea = new QLabel("-", this); 0237 gl->addWidget(mImageArea, row, 1); 0238 ++row; 0239 0240 // How many pages will be printed 0241 l = new QLabel(i18nc("@label:textbox", "Pages required:"), this); 0242 gl->addWidget(l, row, 0); 0243 mPrintPages = new QLabel("-", this); 0244 gl->addWidget(mPrintPages, row, 1); 0245 ++row; 0246 0247 gl->setRowStretch(row, 1); 0248 hbox->addWidget(grp); 0249 hbox->setStretchFactor(grp, 1); // want this one to stretch 0250 0251 layout->addLayout(hbox); 0252 layout->addStretch(1); 0253 0254 // The initial default values come from the printer. 0255 initOptions(); // print parameters to GUI 0256 mUpdateTimer->stop(); // don't want a delayed update 0257 updatePrintParameters(); // but do it immediately 0258 } 0259 0260 0261 void ImgPrintDialog::initOptions() 0262 { 0263 KookaPrint::ScaleOption scale = mPrinter->scaleOption(); 0264 if (mPrinter->isCopyMode()) 0265 { 0266 if (scale==KookaPrint::ScaleScreen) scale = KookaPrint::ScaleScan; 0267 m_scaleRadios->button(KookaPrint::ScaleScreen)->setEnabled(false); 0268 m_screenRes->setEnabled(false); 0269 } 0270 m_scaleRadios->button(scale)->setChecked(true); 0271 slotScaleChanged(scale); 0272 0273 const int scanRes = mPrinter->scanResolution(); 0274 if (scanRes!=-1) m_dpi->setValue(scanRes); // custom resolution provided? 0275 else // if not, get from image 0276 { 0277 // Get the scan resolution from the image 0278 const int imgResX = DPM_TO_DPI(m_image->dotsPerMeterX()); 0279 const int imgResY = DPM_TO_DPI(m_image->dotsPerMeterY()); 0280 if (imgResX!=imgResY) qCWarning(KOOKA_LOG) << "Different image resolutions" << imgResX << imgResY; 0281 if (imgResX!=0) 0282 { 0283 qCDebug(KOOKA_LOG) << "Resolution from image" << imgResX; 0284 m_dpi->setValue(imgResX); 0285 } 0286 } 0287 0288 QSize printSize = mPrinter->printSize(); 0289 m_sizeW->setValue(printSize.width()); 0290 m_sizeH->setValue(printSize.height()); 0291 0292 int screenDpi = mPrinter->screenResolution(); 0293 if (screenDpi==-1) // screen resolution not provided 0294 { 0295 int resX = logicalDpiX(); 0296 int resY = logicalDpiY(); 0297 0298 // TODO: check whether they differ by more than, say, 5% 0299 // and warn the user if so - scaling by screen resolution 0300 // in that case will not preserve the aspect ratio. 0301 screenDpi = (resX+resY)/2; 0302 mPrinter->setScreenResolution(screenDpi); // pass our value to printer 0303 } 0304 m_screenRes->setText(i18nc("@info:status", "%1 dpi", QString::number(screenDpi))); 0305 0306 m_ratio->setChecked(mPrinter->maintainAspect()); 0307 m_psDraft->setChecked(mPrinter->lowResDraft()); 0308 0309 int idx = m_cutsCombo->findData(mPrinter->cutMarksOption()); 0310 if (idx!=-1) m_cutsCombo->setCurrentIndex(idx); 0311 0312 slotAdjustCustomSize(); // adjust height for aspect 0313 } 0314 0315 0316 QString ImgPrintDialog::checkValid() const 0317 { 0318 const int id = m_scaleRadios->checkedId(); 0319 if (id==KookaPrint::ScaleScan && m_dpi->value()==0) 0320 { 0321 return (i18n("The scan resolution must be specified for scaling to it.")); 0322 } 0323 0324 if (id==KookaPrint::ScaleCustom && (m_sizeW->value()==0 || m_sizeH->value()==0)) 0325 { 0326 return (i18n("A valid size must be specified for custom scaling. One or both of the specified dimensions is zero.")); 0327 } 0328 0329 return (QString()); // no problems 0330 } 0331 0332 0333 void ImgPrintDialog::slotScaleChanged(int id) 0334 { 0335 m_dpi->setEnabled(id==KookaPrint::ScaleScan); 0336 m_ratio->setEnabled(id==KookaPrint::ScaleCustom || id==KookaPrint::ScaleFitPage); 0337 m_sizeW->setEnabled(id==KookaPrint::ScaleCustom); 0338 m_sizeH->setEnabled(id==KookaPrint::ScaleCustom); 0339 m_screenRes->setEnabled(id==KookaPrint::ScaleScreen); 0340 } 0341 0342 0343 void ImgPrintDialog::slotCustomWidthChanged(int val) 0344 { 0345 slotAdjustCustomSize(); 0346 } 0347 0348 0349 void ImgPrintDialog::slotCustomHeightChanged(int val) 0350 { 0351 if (!m_ratio->isChecked()) return; // only if maintaining aspect 0352 0353 QSignalBlocker blocker(m_sizeW); 0354 m_sizeW->setValue(qRound(double(val)*m_image->width()/m_image->height())); 0355 } 0356 0357 0358 void ImgPrintDialog::slotAdjustCustomSize() 0359 { 0360 if (!m_ratio->isChecked()) return; // only if maintaining aspect 0361 0362 const double val = m_sizeW->value(); // current width setting 0363 QSignalBlocker blocker(m_sizeH); // adjust height to suit 0364 m_sizeH->setValue(qRound(double(val)*m_image->height()/m_image->width())); 0365 } 0366 0367 0368 void ImgPrintDialog::updatePrintParameters() 0369 { 0370 // get options from GUI and update the printer 0371 const KookaPrint::ScaleOption scale = static_cast<KookaPrint::ScaleOption>(m_scaleRadios->checkedId()); 0372 qCDebug(KOOKA_LOG) << "scale option" << scale; 0373 mPrinter->setScaleOption(scale); 0374 0375 QSize size(m_sizeW->value(), m_sizeH->value()); 0376 qCDebug(KOOKA_LOG) << "print size" << size; 0377 mPrinter->setPrintSize(size); 0378 0379 const bool asp = m_ratio->isChecked(); 0380 qCDebug(KOOKA_LOG) << "maintain aspect?" << asp; 0381 mPrinter->setMaintainAspect(asp); 0382 0383 const bool draft = m_psDraft->isChecked(); 0384 qCDebug(KOOKA_LOG) << "low res draft?" << draft; 0385 mPrinter->setLowResDraft(draft); 0386 0387 // No need to setScreenResolution() here, that has already been done 0388 // in initOptions() and it never changes. 0389 0390 const int scanRes = m_dpi->value(); 0391 qCDebug(KOOKA_LOG) << "scan res" << scanRes; 0392 mPrinter->setScanResolution(scanRes); 0393 0394 const KookaPrint::CutMarksOption cuts = static_cast<KookaPrint::CutMarksOption>(m_cutsCombo->currentData().toInt()); 0395 qCDebug(KOOKA_LOG) << "cut marks" << cuts; 0396 mPrinter->setCutMarks(cuts); 0397 0398 // ask the printer to recalculate page parameters 0399 mPrinter->recalculatePrintParameters(); 0400 0401 // reflect them in the preview GUI 0402 size = mPrinter->availablePageArea(); 0403 mPrintArea->setText(i18nc("@info:status width,height millimetres", 0404 "%1 x %2 mm", size.width(), size.height())); 0405 0406 size = mPrinter->imagePrintArea(); 0407 mImageArea->setText(i18nc("@info:status width,height millimetres", 0408 "%1 x %2 mm", size.width(), size.height())); 0409 0410 size = mPrinter->pageCount(); // width=columns, height=rows 0411 int totalPages = size.height()*size.width(); 0412 if (totalPages==1) mPrintPages->setText(i18nc("@info:status total", "%1", totalPages)); 0413 else mPrintPages->setText(i18nc("@info:status total(rows,cols)", 0414 "%1 (%2 x %3)", totalPages, size.height(), size.width())); 0415 }