File indexing completed on 2024-04-21 15:12:11

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) 1999-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 "kscandevice.h"
0033 
0034 #include <qimage.h>
0035 #include <qfileinfo.h>
0036 #include <qapplication.h>
0037 #include <qsocketnotifier.h>
0038 #include <qstandardpaths.h>
0039 
0040 #include <klocalizedstring.h>
0041 #include <kconfig.h>
0042 #include <kpassworddialog.h>
0043 
0044 #include "scanglobal.h"
0045 #include "scandevices.h"
0046 #include "kgammatable.h"
0047 #include "kscancontrols.h"
0048 #include "kscanoption.h"
0049 #include "kscanoptset.h"
0050 #include "deviceselector.h"
0051 #include "scansettings.h"
0052 #include "libkookascan_logging.h"
0053 
0054 extern "C" {
0055 #include <sane/saneopts.h>
0056 }
0057 
0058 #define MIN_PREVIEW_DPI     75
0059 #define MAX_PROGRESS        100
0060 
0061 
0062 // Debugging options
0063 #define DEBUG_OPTIONS
0064 #undef DEBUG_RELOAD
0065 #undef DEBUG_CREATE
0066 #define DEBUG_PARAMS
0067 
0068 #ifdef DEBUG_OPTIONS
0069 #include <iostream>
0070 #endif // DEBUG_OPTIONS
0071 
0072 
0073 //  Accessing GUI options
0074 //  ---------------------
0075 
0076 // Used only by ScanParams::slotVirtScanModeSelect()
0077 void KScanDevice::guiSetEnabled(const QByteArray &name, bool state)
0078 {
0079     KScanOption *so = getExistingGuiElement(name);
0080     if (so==nullptr) return;
0081 
0082     QWidget *w = so->widget();
0083     if (w==nullptr) return;
0084 
0085     w->setEnabled(state && so->isSoftwareSettable());
0086 }
0087 
0088 
0089 KScanOption *KScanDevice::getOption(const QByteArray &name, bool create)
0090 {
0091     QByteArray alias = aliasName(name);
0092 
0093     if (mCreatedOptions.contains(alias))
0094     {
0095 #ifdef DEBUG_CREATE
0096         qCDebug(LIBKOOKASCAN_LOG) << "already exists" << alias;
0097 #endif // DEBUG_CREATE
0098         return (mCreatedOptions.value(alias));
0099     }
0100 
0101     if (!create)
0102     {
0103 #ifdef DEBUG_CREATE
0104         qCDebug(LIBKOOKASCAN_LOG) << "does not exist" << alias;
0105 #endif // DEBUG_CREATE
0106         return (nullptr);
0107     }
0108 
0109 #ifdef DEBUG_CREATE
0110     qCDebug(LIBKOOKASCAN_LOG) << "creating new" << alias;
0111 #endif // DEBUG_CREATE
0112     KScanOption *so = new KScanOption(alias, this);
0113     mCreatedOptions.insert(alias, so);
0114     return (so);
0115 }
0116 
0117 
0118 KScanOption *KScanDevice::getExistingGuiElement(const QByteArray &name) const
0119 {
0120     KScanOption *ret = nullptr;
0121     QByteArray alias = aliasName(name);
0122 
0123     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
0124          it!=mCreatedOptions.constEnd(); ++it)
0125     {
0126         KScanOption *opt = it.value();
0127         if (opt->isGuiElement() && opt->getName()==alias)
0128         {
0129             ret = opt;
0130             break;
0131         }
0132     }
0133 
0134     return (ret);
0135 }
0136 
0137 
0138 KScanOption *KScanDevice::getGuiElement(const QByteArray &name, QWidget *parent)
0139 {
0140     if (name.isEmpty()) return (nullptr);
0141     if (!optionExists(name)) return (nullptr);
0142 
0143     //qCDebug(LIBKOOKASCAN_LOG) << "for" << name;
0144 
0145     KScanOption *so = getExistingGuiElement(name);  // see if already exists
0146     if (so!=nullptr) return (so);           // if so, just return that
0147 
0148     so = getOption(name);               // create a new scan option
0149     if (so->isValid())                  // option was created
0150     {
0151         QWidget *w = so->createWidget(parent);      // create widget for option
0152         if (w!=nullptr) w->setEnabled(so->isActive() && so->isSoftwareSettable());
0153         else qCDebug(LIBKOOKASCAN_LOG) << "no widget created for" << name;
0154     }
0155     else                        // option not valid
0156     {                           // (not known by scanner?)
0157         qCDebug(LIBKOOKASCAN_LOG) << "option invalid" << name;
0158         so = nullptr;
0159     }
0160 
0161     return (so);
0162 }
0163 
0164 
0165 //  Constructor & destructor
0166 //  ------------------------
0167 
0168 KScanDevice::KScanDevice(QObject *parent)
0169    : QObject(parent)
0170 {
0171     qCDebug(LIBKOOKASCAN_LOG);
0172 
0173     ScanGlobal::self()->init();             // do sane_init() first of all
0174 
0175     mScannerHandle = nullptr;
0176     mScannerInitialised = false;            // is device opened yet?
0177     mScannerName = "";
0178 
0179     mScanningState = KScanDevice::ScanIdle;
0180 
0181     mScanBuf = nullptr;                 // image data buffer while scanning
0182     mScanImage.clear();                 // temporary image to scan into
0183     mTestFormat = ScanImage::None;          // format deduced from scan
0184 
0185     mSocketNotifier = nullptr;              // socket notifier for async scanning
0186 
0187     mBytesRead = 0;
0188     mBytesUsed = 0;
0189     mPixelX = 0;
0190     mPixelY = 0;
0191 }
0192 
0193 
0194 KScanDevice::~KScanDevice()
0195 {
0196 // TODO: need to check and do closeDevice() here?
0197     ScanGlobal::self()->setScanDevice(nullptr);     // going away, don't call me
0198 
0199     qCDebug(LIBKOOKASCAN_LOG) << "done";
0200 }
0201 
0202 
0203 //  Opening/closing the scanner device
0204 //  ----------------------------------
0205 
0206 KScanDevice::Status KScanDevice::openDevice(const QByteArray &backend)
0207 {
0208     KScanDevice::Status stat = KScanDevice::Ok;
0209 
0210     qCDebug(LIBKOOKASCAN_LOG) << "backend" << backend;
0211 
0212     mSaneStatus = SANE_STATUS_UNSUPPORTED;
0213     if (backend.isEmpty()) return (KScanDevice::ParamError);
0214 
0215     // search for scanner
0216     if (ScanDevices::self()->deviceInfo(backend)==nullptr) return (KScanDevice::NoDevice);
0217 
0218     mScannerName = backend;             // set now for authentication
0219     QApplication::setOverrideCursor(Qt::WaitCursor);    // potential lengthy operation
0220     ScanGlobal::self()->setScanDevice(this);        // for possible authentication
0221     mSaneStatus = sane_open(backend.constData(), &mScannerHandle);
0222 
0223     if (mSaneStatus==SANE_STATUS_ACCESS_DENIED)     // authentication failed?
0224     {
0225         clearSavedAuth();               // clear any saved password
0226         qCDebug(LIBKOOKASCAN_LOG) << "retrying authentication"; // try again once more
0227         mSaneStatus = sane_open(backend.constData(), &mScannerHandle);
0228     }
0229 
0230     if (mSaneStatus==SANE_STATUS_GOOD)
0231     {
0232         stat = findOptions();               // fill dictionary with options
0233         mScannerInitialised = true;         // note scanner opened OK
0234     }
0235     else
0236     {
0237         stat = KScanDevice::OpenDevice;
0238         mScannerName = "";
0239     }
0240 
0241     QApplication::restoreOverrideCursor();
0242     return (stat);
0243 }
0244 
0245 
0246 void KScanDevice::closeDevice()
0247 {
0248     emit sigCloseDevice();              // tell callers we're closing
0249 
0250     //qCDebug(LIBKOOKASCAN_LOG) << "Saving default scan settings";
0251     saveStartupConfig();                // save config for next startup
0252 
0253     if (mScannerHandle!=nullptr)
0254     {
0255         if (mScanningState!=KScanDevice::ScanIdle)
0256         {
0257             qCDebug(LIBKOOKASCAN_LOG) << "Scanner is still active, calling sane_cancel()";
0258             sane_cancel(mScannerHandle);
0259         }
0260         sane_close(mScannerHandle);         // close the SANE device
0261         mScannerHandle = nullptr;           // scanner no longer open
0262     }
0263 
0264     // clear lists of options
0265     QList<KScanOption *> opts = mCreatedOptions.values();
0266     while (!opts.isEmpty()) delete opts.takeFirst();
0267     mCreatedOptions.clear();
0268     mKnownOptions.clear();
0269 
0270     mScannerName = "";
0271     mScannerInitialised = false;
0272 }
0273 
0274 
0275 //  Scanner and image information
0276 //  -----------------------------
0277 
0278 QString KScanDevice::scannerDescription() const
0279 {
0280     QString ret;
0281 
0282     if (!mScannerName.isNull() && mScannerInitialised)
0283     {
0284         ret = ScanDevices::self()->deviceDescription(mScannerName);
0285     }
0286     else
0287     {
0288         ret = i18n("No scanner selected");
0289     }
0290 
0291     return (ret);
0292 }
0293 
0294 
0295 QSize KScanDevice::getMaxScanSize()
0296 {
0297     QSize s;
0298     double min, max;
0299 
0300     KScanOption *so_w = getOption(SANE_NAME_SCAN_BR_X);
0301     so_w->getRange(&min, &max);
0302     s.setWidth(static_cast<int>(max));
0303 
0304     KScanOption *so_h = getOption(SANE_NAME_SCAN_BR_Y);
0305     so_h->getRange(&min, &max);
0306     s.setHeight(static_cast<int>(max));
0307 
0308     return (s);
0309 }
0310 
0311 
0312 void KScanDevice::getCurrentFormat(int *format, int *depth)
0313 {
0314     sane_get_parameters(mScannerHandle, &mSaneParameters);
0315     *format = mSaneParameters.format;
0316     *depth = mSaneParameters.depth;
0317 }
0318 
0319 
0320 //  Listing the available options
0321 //  -----------------------------
0322 
0323 KScanDevice::Status KScanDevice::findOptions()
0324 {
0325     SANE_Int n;
0326     SANE_Int opt;
0327 
0328     if (sane_control_option(mScannerHandle, 0, SANE_ACTION_GET_VALUE,
0329                             &n, &opt)!=SANE_STATUS_GOOD)
0330     {
0331         qCWarning(LIBKOOKASCAN_LOG) << "cannot read option 0 (count)";
0332         return (KScanDevice::ControlError);
0333     }
0334 
0335     mKnownOptions.clear();
0336     for (int i = 1; i<n; i++)
0337     {
0338         const SANE_Option_Descriptor *d = sane_get_option_descriptor(mScannerHandle, i);
0339         if (d==nullptr) continue;           // could not get descriptor
0340 
0341         QByteArray name;
0342         if (d->name!=nullptr && strlen(d->name)>0) name = d->name;
0343 
0344         if (d->type==SANE_TYPE_GROUP)           // option is a group,
0345         {                       // give it a dummy name
0346             name = "group-";
0347             name += QByteArray::number(i);
0348         }
0349 
0350         if (!name.isEmpty())                // must now have a name
0351         {
0352 #ifdef DEBUG_OPTIONS
0353             qCDebug(LIBKOOKASCAN_LOG) << "Option" << i << "is" << name;
0354 #endif // DEBUG_OPTIONS
0355             mKnownOptions.insert(i, name);
0356         }
0357         else qCWarning(LIBKOOKASCAN_LOG) << "Invalid option" << i << "(no name and not a group)";
0358     }
0359 
0360     return (KScanDevice::Ok);
0361 }
0362 
0363 
0364 QList<QByteArray> KScanDevice::getAllOptions() const
0365 {
0366     return (mKnownOptions.values());
0367 }
0368 
0369 
0370 QList<QByteArray> KScanDevice::getCommonOptions() const
0371 {
0372     QList<QByteArray> opts;
0373 
0374     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
0375          it!=mCreatedOptions.constEnd(); ++it)
0376     {
0377         KScanOption *so = it.value();
0378         if (so->isCommonOption()) opts.append(it.key());
0379     }
0380 
0381     return (opts);
0382 }
0383 
0384 
0385 QList<QByteArray> KScanDevice::getAdvancedOptions() const
0386 {
0387     QList<QByteArray> opts;
0388 
0389     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
0390          it!=mCreatedOptions.constEnd(); ++it)
0391     {
0392         KScanOption *so = it.value();
0393         if (!so->isCommonOption()) opts.append(it.key());
0394     }
0395 
0396     return (opts);
0397 }
0398 
0399 
0400 //  Controlling options
0401 //  -------------------
0402 
0403 int KScanDevice::getOptionIndex(const QByteArray &name) const
0404 {
0405     return (mKnownOptions.key(name));
0406 }
0407 
0408 
0409 bool KScanDevice::optionExists(const QByteArray &name) const
0410 {
0411    if (name.isEmpty()) return (false);
0412    QByteArray alias = aliasName(name);
0413    return (mKnownOptions.key(alias)!=0);
0414 }
0415 
0416 
0417 /* This function tries to find name aliases which appear from backend to backend.
0418  *  Example: Custom-Gamma is for epson backends 'gamma-correction' - not a really
0419  *  cool thing :-|
0420  *  Maybe this helps us out ?
0421  */
0422 QByteArray KScanDevice::aliasName( const QByteArray& name ) const
0423 {
0424     if (mCreatedOptions.contains(name)) return (name);
0425 
0426     QByteArray ret = name;
0427     if (name == SANE_NAME_CUSTOM_GAMMA)
0428     {
0429         if (mCreatedOptions.contains("gamma-correction")) ret = "gamma-correction";
0430     }
0431 
0432     if (ret!=name) qCDebug(LIBKOOKASCAN_LOG) << "Found alias for" << name << "which is" << ret;
0433     return (ret);
0434 }
0435 
0436 
0437 void KScanDevice::applyOption(KScanOption *opt)
0438 {
0439     bool reload = true;                 // is a reload needed?
0440 
0441     if (opt!=nullptr)                   // an option is specified
0442     {
0443 #ifdef DEBUG_RELOAD
0444         qCDebug(LIBKOOKASCAN_LOG) << "option" << opt->getName();
0445 #endif // DEBUG_APPLY
0446         reload = opt->apply();              // apply this option
0447     }
0448 
0449     if (!reload)                    // need to reload now?
0450     {
0451 #ifdef DEBUG_RELOAD
0452         qCDebug(LIBKOOKASCAN_LOG) << "Reload of others not needed";
0453 #endif // DEBUG_RELOAD
0454         return;
0455     }
0456                             // reload of all others needed
0457     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
0458          it!=mCreatedOptions.constEnd(); ++it)
0459     {
0460         KScanOption *so = it.value();
0461         if (!so->isGuiElement()) continue;
0462         if (opt==nullptr || so!=opt)
0463         {
0464 #ifdef DEBUG_RELOAD
0465             qCDebug(LIBKOOKASCAN_LOG) << "Reloading" << so->getName();
0466 #endif // DEBUG_RELOAD
0467             so->reload();
0468             so->redrawWidget();
0469         }
0470     }
0471 
0472 #ifdef DEBUG_RELOAD
0473     qCDebug(LIBKOOKASCAN_LOG) << "Finished";
0474 #endif // DEBUG_RELOAD
0475 }
0476 
0477 
0478 void KScanDevice::reloadAllOptions()
0479 {
0480 #ifdef DEBUG_RELOAD
0481     qCDebug(LIBKOOKASCAN_LOG);
0482 #endif // DEBUG_RELOAD
0483     applyOption(nullptr);
0484 }
0485 
0486 
0487 //  Scanning control
0488 //  ----------------
0489 
0490 void KScanDevice::slotStopScanning()
0491 {
0492     qCDebug(LIBKOOKASCAN_LOG);
0493     mScanningState = KScanDevice::ScanStopNow;
0494 }
0495 
0496 
0497 //  Preview image
0498 //  -------------
0499 
0500 const QString KScanDevice::previewFile() const
0501 {
0502     // TODO: this doesn't work if that directory doesn't exist,
0503     // and nothing ever creates that directory!
0504     //
0505     // Do we want this feature to work?
0506     // If so, create the directory in savePreviewImage() below
0507     QString dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)+"/previews/";
0508     QString sname(scannerDescription());
0509     sname.replace('/', "_");
0510     return (dir+sname);
0511 }
0512 
0513 
0514 QImage KScanDevice::loadPreviewImage() const
0515 {
0516     const QString prevFile = previewFile();
0517     qCDebug(LIBKOOKASCAN_LOG) << "Loading preview from" << prevFile;
0518     return (QImage(prevFile));
0519 }
0520 
0521 
0522 // TODO: take a ScanImage::Ptr
0523 bool KScanDevice::savePreviewImage(const QImage &image) const
0524 {
0525     if (image.isNull()) return (false);
0526 
0527     const QString prevFile = previewFile();
0528     qCDebug(LIBKOOKASCAN_LOG) << "Saving preview to" << prevFile;
0529     return (image.save(prevFile, "BMP"));
0530 }
0531 
0532 
0533 //  Displaying scan options
0534 //  -----------------------
0535 //
0536 //  For debugging.  Originally showOptions() was called prepareScan() and had
0537 //  the comment:
0538 //
0539 //    prepareScan tries to set as much as parameters as possible.
0540 //
0541 //    Function which applies all Options which need to be applied.
0542 //    See SANE-Documentation Table 4.5, description for SANE_CAP_SOFT_DETECT.
0543 //    The function sets the options which have SANE_CAP_AUTOMATIC set
0544 //    to automatic adjust.
0545 //
0546 //  But this wasn't true - it only reports the current state of the options.
0547 
0548 #ifdef DEBUG_OPTIONS
0549 
0550 inline const char *optionNotifyString(int opt)
0551 {
0552     return (opt!=0 ? " X |" : " - |");
0553 }
0554 
0555 
0556 void KScanDevice::showOptions()
0557 {
0558     qCDebug(LIBKOOKASCAN_LOG) << "for" << mScannerName;
0559     std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
0560     std::cerr << " Option                           |SSL|HSL|SDT|EMU|AUT|INA|ADV|PRI| Value" << std::endl;
0561     std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
0562 
0563     QList<QByteArray> optionNames = mCreatedOptions.keys();
0564     std::sort(optionNames.begin(), optionNames.end());
0565     for (QList<QByteArray>::const_iterator it = optionNames.constBegin();
0566          it!=optionNames.constEnd(); ++it)
0567     {
0568         QByteArray optionName = (*it);
0569         const KScanOption *so = mCreatedOptions.value(optionName);
0570         if (so->isGroup()) continue;
0571 
0572         int cap = so->getCapabilities();
0573         std::cerr <<
0574             " " << qPrintable(optionName.left(31).leftJustified(32)) << " |" <<
0575             optionNotifyString((cap & SANE_CAP_SOFT_SELECT)) << 
0576             optionNotifyString((cap & SANE_CAP_HARD_SELECT)) << 
0577             optionNotifyString((cap & SANE_CAP_SOFT_DETECT)) << 
0578             optionNotifyString((cap & SANE_CAP_EMULATED)) << 
0579             optionNotifyString((cap & SANE_CAP_AUTOMATIC)) << 
0580             optionNotifyString((cap & SANE_CAP_INACTIVE)) << 
0581             optionNotifyString((cap & SANE_CAP_ADVANCED)) <<
0582             optionNotifyString(so->isPriorityOption()) <<
0583             " " << qPrintable(so->get()) << std::endl;
0584     }
0585     std::cerr << "----------------------------------+---+---+---+---+---+---+---+---+-------" << std::endl;
0586 }
0587 
0588 #endif // DEBUG_OPTIONS
0589 
0590 
0591 //  ------------------------------------------------
0592 
0593 // Deduce the scanned image type from the SANE parameters.
0594 // The logic is very similar to ScanImage::imageType(),
0595 // except that the image type cannot be LowColour.
0596 static ScanImage::ImageType getImageFormat(const SANE_Parameters *p)
0597 {
0598     if (p==nullptr) return (ScanImage::None);
0599 
0600     if (p->depth==1)                    // Line art (bitmap)
0601     {
0602         return (ScanImage::BlackWhite);
0603     }
0604     else if (p->depth==8)               // 8 bit RGB
0605     {
0606         if (p->format==SANE_FRAME_GRAY)         // Grey scale
0607         {
0608             return (ScanImage::Greyscale);
0609         }
0610         else                        // True colour
0611         {
0612             return (ScanImage::HighColour);
0613         }
0614     }
0615     else                        // Error, no others supported
0616     {
0617         qCWarning(LIBKOOKASCAN_LOG) << "Only bit depths 1 or 8 supported!";
0618         return (ScanImage::None);
0619     }
0620 }
0621 
0622 
0623 //  Create a new image to receive the scan or preview
0624 KScanDevice::Status KScanDevice::createNewImage(const SANE_Parameters *p)
0625 {
0626     QImage::Format fmt;
0627     ScanImage::ImageType itype = getImageFormat(p); // what format should this be?
0628     switch (itype)                  // choose QImage option for that
0629     {
0630 default:
0631 case ScanImage::None:       return (KScanDevice::ParamError);
0632 case ScanImage::BlackWhite: fmt = QImage::Format_Mono;      break;
0633 case ScanImage::Greyscale:  fmt = QImage::Format_Indexed8;      break;
0634 case ScanImage::HighColour: fmt = QImage::Format_RGB32;     break;
0635     }
0636 
0637     // mScanImage is the master allocated image for the result of the scan.
0638     // Once the QSharedPointer has been reset() to point to the image, it
0639     // must only be copied or passed around using the QSharedPointer.  The
0640     // image will automatically be deleted when the last QSharedPointer to
0641     // it is clear()'ed or destroyed.
0642     //
0643     // An image can also be allocated in acquireScan() in virtual scanner
0644     // mode.  The same restriction applies.
0645     ScanImage *newImage = new ScanImage(p->pixels_per_line, p->lines, fmt);
0646     if (newImage==nullptr) return (KScanDevice::NoMemory);
0647 
0648     mScanImage.clear();                 // remove reference to previous
0649     mScanImage.reset(newImage);             // capture the new image pointer
0650     mScanImage->setImageType(itype);            // remember the image format
0651 
0652     if (itype==ScanImage::BlackWhite)           // line art (bitmap)
0653     {
0654         mScanImage->setColor(0,qRgb(0x00,0x00,0x00));   // set black/white palette
0655         mScanImage->setColor(1,qRgb(0xFF,0xFF,0xFF));
0656     }
0657     else if (itype==ScanImage::Greyscale)       // 8 bit grey
0658     {                           // set grey scale palette
0659         for (int i = 0; i<256; i++) mScanImage->setColor(i,qRgb(i,i,i));
0660     }
0661 
0662     return (KScanDevice::Ok);
0663 }
0664 
0665 
0666 //  Acquiring preview/scan image
0667 //  ----------------------------
0668 
0669 KScanDevice::Status KScanDevice::acquirePreview(bool forceGray, int dpi)
0670 {
0671     KScanOptSet savedOptions("SavedForPreview");
0672 
0673     /* set Preview = ON if exists */
0674     KScanOption *prev = getOption(SANE_NAME_PREVIEW, false);
0675     if (prev!=nullptr)
0676     {
0677         prev->set(true);
0678         prev->apply();
0679         prev->set(false);               // Ensure that it gets restored
0680         savedOptions.backupOption(prev);        // back to 'false' after previewing
0681     }
0682 
0683     // TODO: this block doesn't make sense (set the option to true if
0684     // it is already true?)
0685     /* Gray-Preview only done by widget? */
0686     KScanOption *so = getOption(SANE_NAME_GRAY_PREVIEW, false);
0687     if (so!=nullptr)
0688     {
0689         if (so->get()=="true")
0690         {
0691             /* Gray preview on */
0692             so->set(true);
0693             qCDebug(LIBKOOKASCAN_LOG) << "Setting GrayPreview ON";
0694         }
0695         else
0696         {
0697             so->set(false);
0698             qCDebug(LIBKOOKASCAN_LOG) << "Setting GrayPreview OFF";
0699         }
0700         so->apply();
0701     }
0702 
0703     KScanOption *mode = getOption(SANE_NAME_SCAN_MODE, false);
0704     if (mode!=nullptr)
0705     {
0706         qCDebug(LIBKOOKASCAN_LOG) << "Scan mode before preview is" << mode->get();
0707         savedOptions.backupOption(mode);
0708         /* apply if it has a widget, or apply always? */
0709         if (mode->isGuiElement()) mode->apply();
0710     }
0711 
0712     /* Some sort of Scan Resolution option should always exist */
0713     KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false);
0714     if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false);
0715     if (xres!=nullptr)
0716     {
0717         qCDebug(LIBKOOKASCAN_LOG) << "Scan resolution before preview is" << xres->get();
0718         savedOptions.backupOption(xres);
0719 
0720         int preview_dpi = dpi;
0721         if (dpi==0)                 // preview DPI not specified
0722         {
0723             double min, max;
0724             if (!xres->getRange(&min, &max))
0725             {
0726                 qCDebug(LIBKOOKASCAN_LOG) << "Could not retrieve resolution range!";
0727                 min = 75.0;             // hope every scanner can do this
0728             }
0729 
0730             preview_dpi = (int) min;
0731             if (preview_dpi<MIN_PREVIEW_DPI) preview_dpi = MIN_PREVIEW_DPI;
0732             qCDebug(LIBKOOKASCAN_LOG) << "Resolution range" << min << "-" << max << "preview at" << preview_dpi;
0733         }
0734 
0735         KScanOption *yres = getOption(SANE_NAME_SCAN_Y_RESOLUTION, false);
0736         if (yres!=nullptr)
0737         {
0738             /* if active ? */
0739             savedOptions.backupOption(yres);
0740             yres->set(preview_dpi);
0741             yres->apply();
0742             yres->get(&mCurrScanResolutionY);
0743         }
0744         else mCurrScanResolutionY = 0;
0745 
0746         /* Resolution bind switch? */
0747         KScanOption *bind = getOption(SANE_NAME_RESOLUTION_BIND, false);
0748         if (bind!=nullptr)
0749         {
0750             /* Switch binding on if available */
0751             savedOptions.backupOption(bind);
0752             bind->set(true);
0753             bind->apply();
0754         }
0755 
0756         xres->set(preview_dpi);
0757         xres->apply();
0758 
0759         /* Store the resulting preview for additional image information */
0760         xres->get(&mCurrScanResolutionX);
0761         if (mCurrScanResolutionY==0) mCurrScanResolutionY = mCurrScanResolutionX;
0762     }
0763 
0764     KScanDevice::Status status = acquireData(true); // perform the preview
0765     loadOptionSet(&savedOptions);           // restore original scan settings
0766     return (status);
0767 }
0768 
0769 
0770 void KScanDevice::applyAllOptions(bool prio)
0771 {
0772     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
0773          it!=mCreatedOptions.constEnd(); ++it)
0774     {
0775             KScanOption *so = it.value();
0776             if (!so->isGuiElement()) continue;
0777             if (so->isPriorityOption() ^ prio) continue;
0778             if (so->isActive() && so->isSoftwareSettable()) so->apply();
0779     }
0780 }
0781 
0782 
0783 /* Starts scanning
0784  *  depending on if a filename is given or not, the function tries to open
0785  *  the file using the Qt-Image-IO or really scans the image.
0786  */
0787 KScanDevice::Status KScanDevice::acquireScan(const QString &filename)
0788 {
0789     if (filename.isEmpty())             // real scan
0790     {
0791         applyAllOptions(true);              // apply priority options
0792         applyAllOptions(false);             // apply non-priority options
0793 
0794         // Some scanners seem to forget the scan area options after certain
0795         // other options have been applied (which means that they should be
0796         // considered to be priority options or flagged as 'reload', but this
0797         // is not always the case).  The area options do not get applied above
0798         // because they will have already been applied (so their KScanOption's
0799         // are clean) when the scan area is selected.  Ensure that they are
0800         // applied after all other options.
0801         //
0802         // Seen with the HP OfficeJet 8610 via HPLIP.
0803         KScanOption *opt;
0804         opt = getOption(SANE_NAME_SCAN_TL_X, false); if (opt!=nullptr) opt->apply();
0805         opt = getOption(SANE_NAME_SCAN_TL_Y, false); if (opt!=nullptr) opt->apply();
0806         opt = getOption(SANE_NAME_SCAN_BR_X, false); if (opt!=nullptr) opt->apply();
0807         opt = getOption(SANE_NAME_SCAN_BR_Y, false); if (opt!=nullptr) opt->apply();
0808 
0809         // One of the Scan Resolution parameters should always exist.
0810         KScanOption *xres = getOption(SANE_NAME_SCAN_X_RESOLUTION, false);
0811         if (xres==nullptr) xres = getOption(SANE_NAME_SCAN_RESOLUTION, false);
0812         if (xres!=nullptr)
0813         {
0814             xres->get(&mCurrScanResolutionX);
0815 
0816             KScanOption *yres = getOption(SANE_NAME_SCAN_Y_RESOLUTION, false);
0817             if (yres!=nullptr) yres->get(&mCurrScanResolutionY);
0818             else mCurrScanResolutionY = mCurrScanResolutionX;
0819         }
0820 
0821         return (acquireData(false));            // perform the scan
0822     }
0823     else                        // virtual scan from image file
0824     {
0825         QFileInfo file(filename);
0826         if (!file.exists())
0827         {
0828             qCWarning(LIBKOOKASCAN_LOG) << "virtual file" << filename << "does not exist";
0829             return (KScanDevice::ParamError);
0830         }
0831 
0832         QImage img(filename);
0833         if (img.isNull())
0834         {
0835             qCWarning(LIBKOOKASCAN_LOG) << "virtual file" << filename << "could not load";
0836             return (KScanDevice::ParamError);
0837         }
0838 
0839         // See createNewImage() for further information regarding the
0840         // allocated image and shared pointer.
0841         ScanImage *newImage = new ScanImage(img);
0842         if (newImage==nullptr) return (KScanDevice::NoMemory);
0843 
0844         mScanImage.clear();             // remove reference to previous
0845         mScanImage.reset(newImage);         // capture the new image pointer
0846 
0847         // Copy the resolution from the original image
0848         mScanImage->setXResolution(DPM_TO_DPI(img.dotsPerMeterX()));
0849         mScanImage->setYResolution(DPM_TO_DPI(img.dotsPerMeterY()));
0850         // Set the original image file name as the scanner name
0851         mScanImage->setScannerName(QFile::encodeName(filename));
0852         emit sigNewImage(mScanImage);
0853         return (KScanDevice::Ok);
0854     }
0855 }
0856 
0857 
0858 #ifdef DEBUG_PARAMS
0859 static void dumpParams(const QString &msg, const SANE_Parameters *p)
0860 {
0861     QByteArray formatName("UNKNOWN");
0862     switch (p->format)
0863     {
0864 case SANE_FRAME_GRAY:   formatName = "GREY";    break;
0865 case SANE_FRAME_RGB:    formatName = "RGB"; break;
0866 case SANE_FRAME_RED:    formatName = "RED"; break;
0867 case SANE_FRAME_GREEN:  formatName = "GREEN";   break;
0868 case SANE_FRAME_BLUE:   formatName = "BLUE";    break;
0869     }
0870 
0871     qCDebug(LIBKOOKASCAN_LOG) << msg.toLatin1().constData();
0872     qCDebug(LIBKOOKASCAN_LOG) << "  format:          " << p->format << "=" << formatName.constData();
0873     qCDebug(LIBKOOKASCAN_LOG) << "  last_frame:      " << p->last_frame;
0874     qCDebug(LIBKOOKASCAN_LOG) << "  lines:           " << p->lines;
0875     qCDebug(LIBKOOKASCAN_LOG) << "  depth:           " << p->depth;
0876     qCDebug(LIBKOOKASCAN_LOG) << "  pixels_per_line: " << p->pixels_per_line;
0877     qCDebug(LIBKOOKASCAN_LOG) << "  bytes_per_line:  " << p->bytes_per_line;
0878 }
0879 #endif // DEBUG_PARAMS
0880 
0881 
0882 KScanDevice::Status KScanDevice::acquireData(bool isPreview)
0883 {
0884     KScanDevice::Status stat = KScanDevice::Ok;
0885     int frames = 0;
0886 
0887 #ifdef DEBUG_OPTIONS
0888     showOptions();                  // dump the current noptions
0889 #endif
0890 
0891     mScanningPreview = isPreview;
0892     mScanningState = KScanDevice::ScanStarting;
0893     mBytesRead = 0;
0894     mBlocksRead = 0;
0895 
0896     ScanGlobal::self()->setScanDevice(this);        // for possible authentication
0897 
0898     // Tell the application that scanning is about to start.
0899     ScanImage::ImageType fmt = ScanImage::None;
0900     if (!isPreview)                 // scanning to eventually save
0901     {
0902         mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters);
0903         if (mSaneStatus==SANE_STATUS_GOOD)      // get pre-scan parameters
0904         {
0905 #ifdef DEBUG_PARAMS
0906             dumpParams("Before scan:", &mSaneParameters);
0907 #endif // DEBUG_PARAMS
0908 
0909             if (mSaneParameters.lines>=1 && mSaneParameters.pixels_per_line>0)
0910             {                       // check for a plausible image
0911                 fmt = getImageFormat(&mSaneParameters); // find format it will have
0912                 if (fmt==ScanImage::None)       // scan format not recognised?
0913                 {
0914                     stat = KScanDevice::ParamError; // no point starting scan
0915                     scanFinished(stat);         // clean up anything started
0916                     return (stat);
0917                 }
0918             }
0919         }
0920 
0921         if (mTestFormat!=ScanImage::None)
0922         {
0923             fmt = mTestFormat;
0924             qCDebug(LIBKOOKASCAN_LOG) << "Testing with image format" << fmt;
0925         }
0926     }
0927     else fmt = ScanImage::Preview;          // special to indicate preview
0928     emit sigScanStart(fmt);             // now tell the application
0929 
0930     // The application may have prompted for a file name.
0931     // If the user cancelled that, it will have called our
0932     // slotStopScanning() which sets mScanningState to
0933     // KScanDevice::ScanStopNow.  If that is the case,
0934     // then finish here.
0935     if (mScanningState==KScanDevice::ScanStopNow)
0936     {                           // user cancelled save dialogue
0937         qCDebug(LIBKOOKASCAN_LOG) << "user cancelled before start";
0938         stat = KScanDevice::Cancelled;
0939         scanFinished(stat);             // clean up anything started
0940         return (stat);
0941     }
0942 
0943     while (true)                    // loop while frames available
0944     {
0945         QApplication::setOverrideCursor(Qt::WaitCursor);
0946                             // potential lengthy operation
0947         mSaneStatus = sane_start(mScannerHandle);
0948         if (mSaneStatus==SANE_STATUS_ACCESS_DENIED) // authentication failed?
0949         {
0950             qCDebug(LIBKOOKASCAN_LOG) << "retrying authentication";
0951             clearSavedAuth();               // clear any saved password
0952             mSaneStatus = sane_start(mScannerHandle);   // try again once more
0953         }
0954 
0955         if (mSaneStatus==SANE_STATUS_GOOD)
0956         {
0957             mSaneStatus = sane_get_parameters(mScannerHandle, &mSaneParameters);
0958             if (mSaneStatus==SANE_STATUS_GOOD)
0959             {
0960 #ifdef DEBUG_PARAMS
0961                 dumpParams(QString("For frame %1:").arg(frames+1), &mSaneParameters);
0962 #endif // DEBUG_PARAMS
0963 
0964                 // TODO: implement "Hand Scanner" support
0965                 if (mSaneParameters.lines<1)
0966                 {
0967                     qCWarning(LIBKOOKASCAN_LOG) << "Hand Scanner not supported";
0968                     stat = KScanDevice::NotSupported;
0969                 }
0970                 else if (mSaneParameters.pixels_per_line==0)
0971                 {
0972                     qCWarning(LIBKOOKASCAN_LOG) << "Nothing to acquire!";
0973                     stat = KScanDevice::EmptyPic;
0974                 }
0975             }
0976             else
0977             {
0978                 stat = KScanDevice::OpenDevice;
0979                 qCDebug(LIBKOOKASCAN_LOG) << "sane_get_parameters() error" << lastSaneErrorMessage();
0980             }
0981         }
0982         else
0983         {
0984             stat = KScanDevice::OpenDevice;
0985             qCDebug(LIBKOOKASCAN_LOG) << "sane_start() error" << lastSaneErrorMessage();
0986         }
0987         QApplication::restoreOverrideCursor();
0988 
0989         if (stat==KScanDevice::Ok && mScanningState==KScanDevice::ScanStarting)
0990         {                       // first time through loop
0991             // Create image to receive scan, based on real SANE parameters
0992             stat = createNewImage(&mSaneParameters);
0993 
0994             // Create/reinitialise buffer for scanning one line
0995             if (stat==KScanDevice::Ok)
0996             {
0997                 if (mScanBuf!=nullptr) delete [] mScanBuf;
0998                 mScanBuf = new SANE_Byte[mSaneParameters.bytes_per_line+4];
0999                 if (mScanBuf==nullptr) stat = KScanDevice::NoMemory;
1000             }                       // can this ever happen?
1001 
1002             if (stat==KScanDevice::Ok)
1003             {
1004                 int fd = 0;
1005                 // Don't assume that sane_get_select_fd() will succeed even if
1006                 // sane_set_io_mode() successfully sets I/O mode to noblocking -
1007                 // bug 159300
1008                 if (sane_set_io_mode(mScannerHandle, SANE_TRUE)==SANE_STATUS_GOOD)
1009                 {
1010                     if (sane_get_select_fd(mScannerHandle, &fd)==SANE_STATUS_GOOD)
1011                     {
1012                         qCDebug(LIBKOOKASCAN_LOG) << "using read socket notifier";
1013                         mSocketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
1014                         connect(mSocketNotifier, &QSocketNotifier::activated, this, &KScanDevice::doProcessABlock);
1015                     }
1016                     else qCDebug(LIBKOOKASCAN_LOG) << "not using socket notifier (sane_get_select_fd() failed)";
1017                 }
1018                 else qCDebug(LIBKOOKASCAN_LOG) << "not using socket notifier (sane_set_io_mode() failed)";
1019             }
1020         }
1021 
1022         if (stat!=KScanDevice::Ok)          // some problem getting started
1023         {
1024             // Scanning could not start - give up now
1025             qCDebug(LIBKOOKASCAN_LOG) << "Scanning failed to start, status" << stat;
1026             scanFinished(stat);             // clean up anything started
1027             return (stat);
1028         }
1029 
1030         if (mScanningState==KScanDevice::ScanStarting)  // first time through loop
1031         {
1032             QApplication::setOverrideCursor(Qt::BusyCursor);
1033             emit sigAcquireStart();
1034         }
1035 
1036         emit sigScanProgress(0);            // signal the progress dialog
1037         qApp->processEvents();              // update the progress window
1038 
1039         mPixelX = 0;
1040         mPixelY = 0;
1041         mBytesUsed = 0;
1042 
1043         // Set internal status to indicate scanning in progress.
1044         // This status might be changed, e.g. by pressing Stop on a
1045         // GUI dialog displayed during scanning.
1046         mScanningState = KScanDevice::ScanInProgress;
1047 
1048         // As originally coded, if using the socket notifier
1049         // sane_get_parameters() was only called once at the beginning of
1050         // the scan - just after sane_start() above.  If not using the socket
1051         // notifier on the other hand, sane_get_parameters() was called after
1052         // each doProcessABlock() in the loop below.
1053         //
1054         // According to the SANE documentation text, sane_get_parameters()
1055         // needs to be called once after sane_start() to get the exact
1056         // parameters, but not necessarily in the reading loop that just
1057         // needs to call sane_read() repeatedly.  The diagram above, though,
1058         // seems to imply that sane_get_parameters() should be done in the
1059         // reading loop.
1060         //
1061         // Doing the sane_get_parameters() just once seems to work with all
1062         // of the scanners that I have available to test, both using the
1063         // socket notifier and not.  So that is what we now do, in this
1064         // much simpler loop.
1065 
1066         while (true)                    // loop for one scanned frame
1067         {
1068             if (mSocketNotifier!=nullptr)       // using the socket notifier
1069             {
1070                 qApp->processEvents();          // let it fire away
1071             }
1072             else                    // not using socket notifier
1073             {
1074                 doProcessABlock();          // may block while reading
1075             }
1076                             // exit loop when frame done
1077             if (mScanningState==KScanDevice::ScanIdle ||
1078                 mScanningState==KScanDevice::ScanStopNow ||
1079                 mScanningState==KScanDevice::ScanNextFrame) break;
1080         }
1081 
1082         ++frames;                   // count up this frame
1083                             // more frames to do
1084         if (mScanningState==KScanDevice::ScanNextFrame) continue;
1085         break;                      // scan done, exit loop
1086     }
1087 
1088     if (mScanningState==KScanDevice::ScanStopNow)
1089     {
1090         stat = KScanDevice::Cancelled;
1091     }
1092     else
1093     {
1094         if (mSaneStatus!=SANE_STATUS_GOOD && mSaneStatus!=SANE_STATUS_EOF)
1095         {
1096             stat = KScanDevice::ScanError;
1097         }
1098     }
1099 
1100     qCDebug(LIBKOOKASCAN_LOG) << "Scan read" << mBytesRead << "bytes in"
1101                               << mBlocksRead << "blocks," << frames << "frames, status" << stat;
1102 
1103     scanFinished(stat);                 // scan is now finished
1104     return (stat);
1105 }
1106 
1107 
1108 /* This function calls at least sane_read and converts the read data from the scanner
1109  * to the qimage.
1110  * The function needs:
1111  * QImage img valid
1112  * the data-buffer  set to a appropriate size
1113  **/
1114 
1115 // TODO: probably needs to be extended for 16-bit scanner support
1116 
1117 void KScanDevice::doProcessABlock()
1118 {
1119     int val,i;
1120     QRgb col, newCol;
1121 
1122     SANE_Byte *rptr = nullptr;
1123     SANE_Int bytes_read = 0;
1124     int chan = 0;
1125     mSaneStatus = SANE_STATUS_GOOD;
1126     uchar eight_pix = 0;
1127 
1128     if (mScanningState==KScanDevice::ScanIdle) return;  // scan finished, no more to do
1129                             // block notifications while working
1130     if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(false);
1131 
1132     while (true)
1133     {
1134         mSaneStatus = sane_read(mScannerHandle,
1135                                 (mScanBuf+mBytesUsed),
1136                                 mSaneParameters.bytes_per_line,
1137                                 &bytes_read);
1138         if (mSaneStatus!=SANE_STATUS_GOOD)
1139         {
1140             if (mSaneStatus!=SANE_STATUS_EOF)       // this is OK, just stop
1141             {                       // any other error
1142                 qCDebug(LIBKOOKASCAN_LOG) << "sane_read() error" << lastSaneErrorMessage()
1143                                           << "bytes read" << bytes_read;
1144             }
1145             break;
1146         }
1147 
1148         if (bytes_read<1) break;            // no data, finish loop
1149 
1150         ++mBlocksRead;
1151     mBytesRead += bytes_read;
1152 
1153         int red = 0;
1154         int green = 0;
1155         int blue = 0;
1156 
1157     rptr = mScanBuf;                // start of scan data
1158     switch (mSaneParameters.format)
1159     {
1160 case SANE_FRAME_RGB:
1161             if (mSaneParameters.lines<1) break;
1162             bytes_read += mBytesUsed;           // die übergebliebenen Bytes dazu
1163             mBytesUsed = bytes_read % 3;
1164 
1165             for (val = 0; val<((bytes_read-mBytesUsed)/3); val++)
1166             {
1167                 red   = *rptr++;
1168                 green = *rptr++;
1169                 blue  = *rptr++;
1170 
1171                 if (mPixelX>=mSaneParameters.pixels_per_line)
1172                 {                   // reached end of a row
1173                     mPixelX = 0;
1174                     mPixelY++;
1175                 }
1176                 if (mPixelY<mScanImage->height())   // within image height
1177                 {
1178             mScanImage->setPixel(mPixelX, mPixelY, qRgb(red, green, blue));
1179                 }
1180                 mPixelX++;
1181             }
1182 
1183             for (val = 0; val<mBytesUsed; val++)    // Copy the remaining bytes down
1184             {                       // to the beginning of the block
1185                 *(mScanBuf+val) = *rptr++;
1186             }
1187             break;
1188 
1189 case SANE_FRAME_GRAY:
1190             for (val = 0; val<bytes_read ; val++)
1191             {
1192                 if (mPixelY>=mSaneParameters.lines) break;
1193                 if (mSaneParameters.depth==8)       // Greyscale
1194                 {
1195             if (mPixelX>=mSaneParameters.pixels_per_line)
1196                     {                   // reached end of a row
1197                         mPixelX = 0;
1198                         mPixelY++;
1199                     }
1200             mScanImage->setPixel(mPixelX, mPixelY, *rptr++);
1201             mPixelX++;
1202                 }
1203                 else                    // Lineart (bitmap)
1204                 {                   // needs to be converted to byte
1205             eight_pix = *rptr++;
1206             for (i = 0; i<8; i++)
1207             {
1208                         if (mPixelY<mSaneParameters.lines)
1209                         {
1210                             chan = (eight_pix & 0x80)>0 ? 0 : 1;
1211                             eight_pix = eight_pix << 1;
1212                             mScanImage->setPixel(mPixelX, mPixelY, chan);
1213                             mPixelX++;
1214                             if( mPixelX>=mSaneParameters.pixels_per_line)
1215                             {
1216                                 mPixelX = 0;
1217                                 mPixelY++;
1218                                 break;
1219                             }
1220                         }
1221             }
1222                 }
1223             }
1224             break;
1225 
1226 case SANE_FRAME_RED:
1227 case SANE_FRAME_GREEN:
1228 case SANE_FRAME_BLUE:
1229             for (val = 0; val<bytes_read ; val++)
1230             {
1231                 if (mPixelX>=mSaneParameters.pixels_per_line)
1232                 {                   // reached end of a row
1233                     mPixelX = 0;
1234             mPixelY++;
1235                 }
1236 
1237                 if (mPixelY<mSaneParameters.lines)
1238                 {
1239             col = mScanImage->pixel(mPixelX, mPixelY);
1240 
1241             red   = qRed(col);
1242             green = qGreen(col);
1243             blue  = qBlue(col);
1244             chan  = *rptr++;
1245 
1246             switch (mSaneParameters.format)
1247             {
1248 case SANE_FRAME_RED:    newCol = qRgba(chan, green, blue, 0xFF);
1249                         break;
1250 
1251 case SANE_FRAME_GREEN:  newCol = qRgba(red, chan, blue, 0xFF);
1252                         break;
1253 
1254 case SANE_FRAME_BLUE:   newCol = qRgba(red, green, chan, 0xFF);
1255                         break;
1256 
1257 default:                newCol = qRgba(0xFF, 0xFF, 0xFF, 0xFF);
1258                         break;
1259             }
1260             mScanImage->setPixel(mPixelX, mPixelY, newCol);
1261             mPixelX++;
1262                 }
1263             }
1264             break;
1265 
1266 default:    qCWarning(LIBKOOKASCAN_LOG) << "Undefined SANE format" << mSaneParameters.format;
1267             break;
1268     }                       // switch of scan format
1269 
1270     if ((mSaneParameters.lines>0) && ((mSaneParameters.lines*mPixelY)>0))
1271     {
1272             int progress =  (int)(((double)MAX_PROGRESS)/mSaneParameters.lines*mPixelY);
1273             if (progress<MAX_PROGRESS) emit sigScanProgress(progress);
1274     }
1275 
1276         // cannot get here, bytes_read and EOF tested above
1277     //if( bytes_read == 0 || mSaneStatus == SANE_STATUS_EOF )
1278     //{
1279     //   //qCDebug(LIBKOOKASCAN_LOG) << "mSaneStatus not OK:" << sane_stat;
1280     //   break;
1281     //}
1282 
1283         if (mScanningState==KScanDevice::ScanStopNow)
1284         {
1285             /* mScanningState is set to ScanStopNow due to hitting slStopScanning   */
1286             /* Mostly that one is fired by the STOP-Button in the progress dialog. */
1287 
1288             /* This is also hit after the normal finish of the scan. Most probably,
1289              * the QSocketnotifier fires for a few times after the scan has been
1290              * cancelled.  Does it matter ? To see it, just uncomment the qDebug msg.
1291              */
1292             //qCDebug(LIBKOOKASCAN_LOG) << "Stopping the scan progress";
1293             //mScanningState = KScanDevice::ScanIdle;
1294             break;
1295         }
1296     }                           // end of main loop
1297 
1298     // Here when scanning is finished or has had an error
1299     if (mSaneStatus==SANE_STATUS_EOF)           // end of scan pass
1300     {
1301         if (mSaneParameters.last_frame)         // end of scanning run
1302         {
1303             /** Everything is okay, the picture is ready **/
1304             qCDebug(LIBKOOKASCAN_LOG) << "Last frame reached, scan successful";
1305             mScanningState = KScanDevice::ScanIdle;
1306         }
1307         else
1308         {
1309             /** EOF und nicht letzter Frame -> Parameter neu belegen und neu starten **/
1310             mScanningState = KScanDevice::ScanNextFrame;
1311             qCDebug(LIBKOOKASCAN_LOG) << "EOF, but another frame to scan";
1312         }
1313     }
1314     else if (mSaneStatus!=SANE_STATUS_GOOD)
1315     {
1316         mScanningState = KScanDevice::ScanIdle;
1317         qCDebug(LIBKOOKASCAN_LOG) << "Scan error or cancelled, status" << mSaneStatus;
1318     }
1319 
1320     if (mSocketNotifier!=nullptr) mSocketNotifier->setEnabled(true);
1321 }
1322 
1323 
1324 void KScanDevice::scanFinished(KScanDevice::Status status)
1325 {
1326     qCDebug(LIBKOOKASCAN_LOG) << "status" << status;
1327 
1328     emit sigScanProgress(MAX_PROGRESS);
1329     QApplication::restoreOverrideCursor();
1330 
1331     if (mSocketNotifier!=nullptr)           // clean up if in use
1332     {
1333     delete mSocketNotifier;
1334     mSocketNotifier = nullptr;
1335     }
1336 
1337     if (mScanBuf!=nullptr)
1338     {
1339     delete[] mScanBuf;
1340     mScanBuf = nullptr;
1341     }
1342 
1343     // If the scan succeeded, finish off the result image and tell the
1344     // application that it is ready.
1345     if (status==KScanDevice::Ok && !mScanImage.isNull())
1346     {
1347     mScanImage->setXResolution(mCurrScanResolutionX);
1348     mScanImage->setYResolution(mCurrScanResolutionY);
1349     mScanImage->setScannerName(mScannerName);
1350 
1351     if (mScanningPreview)
1352     {
1353         savePreviewImage(*mScanImage);
1354         emit sigNewPreview(mScanImage);
1355     }
1356     else
1357     {
1358         emit sigNewImage(mScanImage);
1359     }
1360     }
1361 
1362     // TODO: Should this be called here, even for normal scan termination?
1363     // It seems to have side effects, such as feeding through anything remaining
1364     // in the ADF even if only one page has been requested to be scanned.
1365     sane_cancel(mScannerHandle);
1366 
1367     // Tell the application that the scan has finished.
1368     emit sigScanFinished(status);
1369 
1370     // Nothing needs to be done to clean up the image that was allocated
1371     // in createNewImage(), the QSharedPointer will take care of reference
1372     // counting and deletion.
1373 
1374     mScanningState = KScanDevice::ScanIdle;
1375 }
1376 
1377 
1378 //  Configuration
1379 //  -------------
1380 
1381 void KScanDevice::saveStartupConfig()
1382 {
1383     if (mScannerName.isNull()) return;          // do not save for no scanner
1384 
1385     KScanOptSet optSet(KScanOptSet::startupSetName());
1386     getCurrentOptions(&optSet);
1387     optSet.saveConfig(mScannerName, i18n("Default startup configuration"));
1388 }
1389 
1390 
1391 void KScanDevice::loadOptionSetInternal(const KScanOptSet *optSet, bool prio)
1392 {
1393     for (KScanOptSet::const_iterator it = optSet->constBegin();
1394          it!=optSet->constEnd(); ++it)
1395     {
1396         const QByteArray name = it.key();
1397         if (!optionExists(name)) continue;      // only for options that exist
1398 
1399         KScanOption *so = getOption(name, false);
1400         if (so==nullptr) continue;          // we don't have this option
1401         if (so->isGroup()) continue;            // nothing to do here
1402         if (so->isPriorityOption() ^ prio) continue;    // check whether requested priority
1403 
1404         so->set(it.value());
1405         if (so->isInitialised() && so->isSoftwareSettable() && so->isActive()) so->apply();
1406     }
1407 }
1408 
1409 
1410 void KScanDevice::loadOptionSet(const KScanOptSet *optSet)
1411 {
1412     if (optSet==nullptr) return;
1413 
1414     qCDebug(LIBKOOKASCAN_LOG) << "Loading set" << optSet->getSetName() << "with" << optSet->count() << "options";
1415     loadOptionSetInternal(optSet, true);
1416     loadOptionSetInternal(optSet, false);
1417 }
1418 
1419 
1420 // Retrieve the current options from the scanner, i.e. all of those that
1421 // have an associated GUI element and also any others (e.g. the TL_X and
1422 // other scan area settings) that have been apply()'ed but do not have
1423 // a GUI.
1424 
1425 void KScanDevice::getCurrentOptions(KScanOptSet *optSet) const
1426 {
1427     if (optSet==nullptr) return;
1428 
1429     for (OptionHash::const_iterator it = mCreatedOptions.constBegin();
1430          it!=mCreatedOptions.constEnd(); ++it)
1431     {
1432         KScanOption *so = it.value();
1433         if (!so->isReadable()) continue;
1434 
1435         if (so->isGuiElement() || so->isApplied())
1436         {
1437             if (so->isActive()) optSet->backupOption(so);
1438             so->setApplied(false);
1439         }
1440     }
1441 }
1442 
1443 
1444 KConfigGroup KScanDevice::configGroup(const QString &groupName)
1445 {
1446     Q_ASSERT(!groupName.isEmpty());
1447     return (ScanSettings::self()->config()->group(groupName));
1448 }
1449 
1450 //  SANE Authentication
1451 //  -------------------
1452 //
1453 //  According to the SANE documentation, this may be requested for any use of
1454 //  sane_open(), sane_control_option() or sane_start() on a scanner device
1455 //  that requires authentication.
1456 //
1457 //  The only uses of sane_open() and sane_start() are here in this file, and
1458 //  they set the current scanner using setScanDevice() before performing the
1459 //  SANE operation.
1460 //
1461 //  This does not happen for all uses of sane_control_option(), either here or
1462 //  in KScanOption, so there is a slight possibility that if authentication is
1463 //  needed for those (and has not been previously requested by sane_open() or
1464 //  sane_start()) then it will use the wrong scanner device or will not prompt
1465 //  at all.  However, Kooka only supports one scanner open at a time, and does
1466 //  sane_open() before any use of sane_control_option().  So hopefully this
1467 //  will not be a problem.
1468 
1469 bool KScanDevice::authenticate(QByteArray *retuser, QByteArray *retpass)
1470 {
1471     qCDebug(LIBKOOKASCAN_LOG) << "for" << mScannerName;
1472 
1473     // TODO: use KWallet for username/password?
1474     KConfigGroup grp = configGroup(mScannerName);
1475     QByteArray user = QByteArray::fromBase64(grp.readEntry("user", QString()).toLocal8Bit());
1476     QByteArray pass = QByteArray::fromBase64(grp.readEntry("pass", QString()).toLocal8Bit());
1477 
1478     if (!user.isEmpty() && !pass.isEmpty())
1479     {
1480         qCDebug(LIBKOOKASCAN_LOG) << "have saved username/password";
1481     }
1482     else
1483     {
1484         qCDebug(LIBKOOKASCAN_LOG) << "asking for username/password";
1485 
1486         KPasswordDialog dlg(nullptr, KPasswordDialog::ShowKeepPassword|KPasswordDialog::ShowUsernameLine);
1487         dlg.setPrompt(xi18nc("@info", "The scanner<nl/><icode>%1</icode><nl/>requires authentication.", mScannerName.constData()));
1488         dlg.setWindowTitle(i18n("Scanner Authentication"));
1489 
1490         if (!user.isEmpty()) dlg.setUsername(user);
1491         if (!pass.isEmpty()) dlg.setPassword(pass);
1492 
1493         if (!dlg.exec()) return (false);
1494 
1495         user = dlg.username().toLocal8Bit();
1496         pass = dlg.password().toLocal8Bit();
1497         if (dlg.keepPassword())
1498         {
1499             grp.writeEntry("user", user.toBase64());
1500             grp.writeEntry("pass", pass.toBase64());
1501         }
1502     }
1503 
1504     *retuser = user;
1505     *retpass = pass;
1506     return (true);
1507 }
1508 
1509 
1510 void KScanDevice::clearSavedAuth()
1511 {
1512     KConfigGroup grp = configGroup(mScannerName);
1513     grp.deleteEntry("user");
1514     grp.deleteEntry("pass");
1515     grp.sync();
1516 }
1517 
1518 
1519 //  Error reporting
1520 //  ---------------
1521 
1522 QString KScanDevice::lastSaneErrorMessage() const
1523 {
1524     return (sane_strstatus(mSaneStatus));
1525 }
1526 
1527 
1528 QString KScanDevice::statusMessage(KScanDevice::Status stat)
1529 {
1530     switch (stat)
1531     {
1532 case KScanDevice::Ok:           return (i18n("OK"));        // shouldn't be reported
1533 case KScanDevice::NoDevice:     return (i18n("No device")); // never during scanning
1534 case KScanDevice::ParamError:       return (i18n("Bad parameter"));
1535 case KScanDevice::OpenDevice:       return (i18n("Cannot open device"));
1536 case KScanDevice::ControlError:     return (i18n("sane_control_option() failed"));
1537 case KScanDevice::ScanError:        return (i18n("Error while scanning"));
1538 case KScanDevice::EmptyPic:     return (i18n("Empty picture"));
1539 case KScanDevice::NoMemory:     return (i18n("Out of memory"));
1540 case KScanDevice::Reload:       return (i18n("Needs reload"));  // never during scanning
1541 case KScanDevice::Cancelled:        return (i18n("Cancelled")); // shouldn't be reported
1542 case KScanDevice::OptionNotActive:  return (i18n("Not active"));    // never during scanning
1543 case KScanDevice::NotSupported:     return (i18n("Not supported"));
1544 default:                return (i18n("Unknown status %1", stat));
1545     }
1546 }