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 }