File indexing completed on 2024-05-19 04:29:13

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 2001 Werner Trobin <trobin@kde.org>
0005     SPDX-FileCopyrightText: 2002 Werner Trobin <trobin@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "KisImportExportFilter.h"
0011 
0012 #include <QFile>
0013 #include <QFileInfo>
0014 #include <kis_debug.h>
0015 #include <QStack>
0016 #include "KisImportExportManager.h"
0017 #include <KoColorSpaceRegistry.h>
0018 #include <KoColorModelStandardIds.h>
0019 #include <KisExportCheckBase.h>
0020 #include <KisExportCheckRegistry.h>
0021 #include "KoUpdater.h"
0022 #include <klocalizedstring.h>
0023 #include "kis_config.h"
0024 #include <KoStore.h>
0025 #include <KisDocument.h>
0026 
0027 const QString KisImportExportFilter::ImageContainsTransparencyTag = "ImageContainsTransparency";
0028 const QString KisImportExportFilter::ColorModelIDTag = "ColorModelID";
0029 const QString KisImportExportFilter::ColorDepthIDTag = "ColorDepthID";
0030 const QString KisImportExportFilter::sRGBTag = "sRGB";
0031 const QString KisImportExportFilter::CICPPrimariesTag = "CICPCompatiblePrimaries";
0032 const QString KisImportExportFilter::CICPTransferCharacteristicsTag = "CICPCompatibleTransferFunction";
0033 const QString KisImportExportFilter::HDRTag = "HDRSupported";
0034 
0035 class Q_DECL_HIDDEN KisImportExportFilter::Private
0036 {
0037 public:
0038     QPointer<KoUpdater> updater;
0039     QByteArray mime;
0040     QString filename;
0041     QString realFilename;
0042     bool batchmode;
0043     KisImportUserFeedbackInterface *importUserFeedBackInterface {nullptr};
0044 
0045     QMap<QString, KisExportCheckBase*> capabilities;
0046 
0047     Private()
0048         : updater(0), mime("")
0049         , batchmode(false)
0050     {}
0051 
0052     ~Private()
0053     {
0054         qDeleteAll(capabilities);
0055     }
0056 
0057 };
0058 
0059 
0060 KisImportExportFilter::KisImportExportFilter(QObject *parent)
0061     : QObject(parent)
0062     , d(new Private)
0063 {
0064 }
0065 
0066 KisImportExportFilter::~KisImportExportFilter()
0067 {
0068     if (d->updater) {
0069         d->updater->setProgress(100);
0070     }
0071     delete d;
0072 }
0073 
0074 QString KisImportExportFilter::filename() const
0075 {
0076     return d->filename;
0077 }
0078 
0079 QString KisImportExportFilter::realFilename() const
0080 {
0081     return d->realFilename;
0082 }
0083 
0084 bool KisImportExportFilter::batchMode() const
0085 {
0086     return d->batchmode;
0087 }
0088 
0089 KisImportUserFeedbackInterface *KisImportExportFilter::importUserFeedBackInterface() const
0090 {
0091     return d->importUserFeedBackInterface;
0092 }
0093 
0094 void KisImportExportFilter::setBatchMode(bool batchmode)
0095 {
0096     d->batchmode = batchmode;
0097 }
0098 
0099 void KisImportExportFilter::setImportUserFeedBackInterface(KisImportUserFeedbackInterface *interface)
0100 {
0101     d->importUserFeedBackInterface = interface;
0102 }
0103 
0104 void KisImportExportFilter::setFilename(const QString &filename)
0105 {
0106     d->filename = filename;
0107 }
0108 
0109 void KisImportExportFilter::setRealFilename(const QString &filename)
0110 {
0111     d->realFilename = filename;
0112 }
0113 
0114 
0115 void KisImportExportFilter::setMimeType(const QString &mime)
0116 {
0117     d->mime = mime.toLatin1();
0118 }
0119 
0120 QByteArray KisImportExportFilter::mimeType() const
0121 {
0122     return d->mime;
0123 }
0124 
0125 KisPropertiesConfigurationSP KisImportExportFilter::defaultConfiguration(const QByteArray &from, const QByteArray &to) const
0126 {
0127     Q_UNUSED(from);
0128     Q_UNUSED(to);
0129     return 0;
0130 }
0131 
0132 KisPropertiesConfigurationSP KisImportExportFilter::lastSavedConfiguration(const QByteArray &from, const QByteArray &to) const
0133 {
0134     KisPropertiesConfigurationSP cfg = defaultConfiguration(from, to);
0135     const QString filterConfig = KisConfig(true).exportConfigurationXML(to);
0136     if (cfg && !filterConfig.isEmpty()) {
0137         cfg->fromXML(filterConfig, false);
0138     }
0139     return cfg;
0140 }
0141 
0142 KisConfigWidget *KisImportExportFilter::createConfigurationWidget(QWidget *, const QByteArray &from, const QByteArray &to) const
0143 {
0144     Q_UNUSED(from);
0145     Q_UNUSED(to);
0146     return 0;
0147 }
0148 
0149 QMap<QString, KisExportCheckBase *> KisImportExportFilter::exportChecks()
0150 {
0151     qDeleteAll(d->capabilities);
0152     initializeCapabilities();
0153     return d->capabilities;
0154 }
0155 
0156 QString KisImportExportFilter::verify(const QString &fileName) const
0157 {
0158     QFileInfo fi(fileName);
0159 
0160     if (!fi.exists()) {
0161         return i18n("%1 does not exist after writing. Try saving again under a different name, in another location.", fileName);
0162     }
0163 
0164     if (!fi.isReadable()) {
0165         return i18n("%1 is not readable", fileName);
0166     }
0167 
0168     if (fi.size() < 10)  {
0169         return i18n("%1 is smaller than 10 bytes, it must be corrupt. Try saving again under a different name, in another location.", fileName);
0170     }
0171 
0172     QFile f(fileName);
0173     f.open(QFile::ReadOnly);
0174     QByteArray ba = f.read(std::min(f.size(), (qint64)1000));
0175     bool found = false;
0176     for(int i = 0; i < ba.size(); ++i) {
0177         if (ba.at(i) > 0) {
0178             found = true;
0179             break;
0180         }
0181     }
0182 
0183     if (!found) {
0184         return i18n("%1 has only zero bytes in the first 1000 bytes, it's probably corrupt. Try saving again under a different name, in another location.", fileName);
0185     }
0186 
0187     return QString();
0188 }
0189 
0190 void KisImportExportFilter::setUpdater(QPointer<KoUpdater> updater)
0191 {
0192     d->updater = updater;
0193 }
0194 
0195 QPointer<KoUpdater> KisImportExportFilter::updater()
0196 {
0197     return d->updater;
0198 }
0199 
0200 void KisImportExportFilter::setProgress(int value)
0201 {
0202     if (d->updater) {
0203         d->updater->setValue(value);
0204     }
0205 }
0206 
0207 void KisImportExportFilter::initializeCapabilities()
0208 {
0209     // XXX: Initialize everything to fully supported?
0210 }
0211 
0212 void KisImportExportFilter::addCapability(KisExportCheckBase *capability)
0213 {
0214     d->capabilities[capability->id()] = capability;
0215 }
0216 
0217 
0218 
0219 void KisImportExportFilter::addSupportedColorModels(QList<QPair<KoID, KoID> > supportedColorModels, const QString &name, KisExportCheckBase::Level level)
0220 {
0221     Q_ASSERT(level != KisExportCheckBase::SUPPORTED);
0222     QString layerMessage;
0223     QString imageMessage;
0224     QList<KoID> allColorModels = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces);
0225     Q_FOREACH(const KoID &colorModelID, allColorModels) {
0226         QList<KoID> allColorDepths = KoColorSpaceRegistry::instance()->colorDepthList(colorModelID.id(), KoColorSpaceRegistry::AllColorSpaces);
0227         Q_FOREACH(const KoID &colorDepthID, allColorDepths) {
0228 
0229             KisExportCheckFactory *colorModelCheckFactory =
0230                     KisExportCheckRegistry::instance()->get("ColorModelCheck/" + colorModelID.id() + "/" + colorDepthID.id());
0231             KisExportCheckFactory *colorModelPerLayerCheckFactory =
0232                     KisExportCheckRegistry::instance()->get("ColorModelPerLayerCheck/" + colorModelID.id() + "/" + colorDepthID.id());
0233 
0234             if(!colorModelCheckFactory || !colorModelPerLayerCheckFactory) {
0235                 qWarning() << "No factory for" << colorModelID << colorDepthID;
0236                 continue;
0237             }
0238 
0239             if (supportedColorModels.contains(QPair<KoID, KoID>(colorModelID, colorDepthID))) {
0240                 addCapability(colorModelCheckFactory->create(KisExportCheckBase::SUPPORTED));
0241                 addCapability(colorModelPerLayerCheckFactory->create(KisExportCheckBase::SUPPORTED));
0242             }
0243             else {
0244 
0245 
0246                 if (level == KisExportCheckBase::PARTIALLY) {
0247                     imageMessage = i18nc("image conversion warning",
0248                                          "%1 cannot save images with color model <b>%2</b> and depth <b>%3</b>. The image will be converted."
0249                                          ,name, colorModelID.name(), colorDepthID.name());
0250 
0251                     layerMessage =
0252                             i18nc("image conversion warning",
0253                                   "%1 cannot save layers with color model <b>%2</b> and depth <b>%3</b>. The layers will be converted or skipped."
0254                                   ,name, colorModelID.name(), colorDepthID.name());
0255                 }
0256                 else {
0257                     imageMessage = i18nc("image conversion warning",
0258                                          "%1 cannot save images with color model <b>%2</b> and depth <b>%3</b>. The image will not be saved."
0259                                          ,name, colorModelID.name(), colorDepthID.name());
0260 
0261                     layerMessage =
0262                             i18nc("image conversion warning",
0263                                   "%1 cannot save layers with color model <b>%2</b> and depth <b>%3</b>. The layers will be skipped."
0264                                   , name, colorModelID.name(), colorDepthID.name());
0265                  }
0266 
0267 
0268 
0269                 addCapability(colorModelCheckFactory->create(level, imageMessage));
0270                 addCapability(colorModelPerLayerCheckFactory->create(level, layerMessage));
0271             }
0272         }
0273     }
0274 }
0275 
0276 QString KisImportExportFilter::verifyZiPBasedFiles(const QString &fileName, const QStringList &filesToCheck) const
0277 {
0278     QScopedPointer<KoStore> store(KoStore::createStore(fileName, KoStore::Read, KIS_MIME_TYPE, KoStore::Zip));
0279 
0280     if (!store || store->bad()) {
0281         return i18n("Could not open the saved file %1. Please try to save again in a different location.", fileName);
0282     }
0283 
0284     Q_FOREACH(const QString &file, filesToCheck) {
0285         if (!store->hasFile(file)) {
0286             return i18n("File %1 is missing in %2 and is broken. Please try to save again in a different location.", file, fileName);
0287         }
0288     }
0289 
0290     return QString();
0291 
0292 }