14#include <QApplication>
16#include <QPluginLoader>
20#include <QTextBrowser>
25#include <QtConcurrentRun>
26#include <QTemporaryFile>
28#include <klocalizedstring.h>
29#include <ksqueezedtextlabel.h>
30#include <kpluginfactory.h>
75 SynchronousUserFeedbackInterface(QWidget *parent,
bool batchMode)
77 , m_batchMode(batchMode)
81 Result askUser(AskCallback callback)
override
83 if (m_batchMode)
return SuppressedByBatchMode;
89 QWidget *m_parent {
nullptr};
90 bool m_batchMode {
false};
149 : m_document(document)
191 QSet<QString> mimeTypes;
198 QJsonObject json = loader.
metaData().value(
"MetaData").toObject();
199 Q_FOREACH(
const QString &mimetype, json.value(
"X-KDE-Import").toString().split(
",", Qt::SkipEmptyParts)) {
202 mimeTypes << mimetype;
213 QJsonObject json = loader.
metaData().value(
"MetaData").toObject();
214 Q_FOREACH(
const QString &mimetype, json.value(
"X-KDE-Export").toString().split(
",", Qt::SkipEmptyParts)) {
217 mimeTypes << mimetype;
234 QJsonObject json = loader.
metaData().value(
"MetaData").toObject();
235 QString directionKey = direction ==
Export ?
"X-KDE-Export" :
"X-KDE-Import";
237 if (json.value(directionKey).toString().split(
",", Qt::SkipEmptyParts).contains(mimetype)) {
239 KPluginFactory *factory = qobject_cast<KPluginFactory *>(loader.
instance());
247 if (!obj || !obj->inherits(
"KisImportExportFilter")) {
260 int w = json.value(
"X-KDE-Weight").toInt();
265 f->setObjectName(loader.
fileName());
291 if (!defaultDir.isEmpty()) {
292 dialog.setDefaultDir(defaultDir);
296 mimeTypes <<
"audio/mpeg";
297 mimeTypes <<
"audio/ogg";
298 mimeTypes <<
"audio/vorbis";
299 mimeTypes <<
"audio/vnd.wave";
300 mimeTypes <<
"audio/flac";
302 dialog.setMimeTypeFilters(mimeTypes);
303 dialog.setCaption(i18nc(
"@title:window",
"Open Audio"));
305 return dialog.filename();
314 dialog.setDirectoryUrl(QUrl(defaultUri));
315 dialog.setMimeTypeFilters(
QStringList(
"application/x-krita"));
317 return dialog.filename();
325 QString typeName = mimeType;
326 if (typeName.isEmpty()) {
337 d->cachedExportFilter &&
338 d->cachedExportFilterMimeType == typeName) {
340 filter =
d->cachedExportFilter;
345 if (direction ==
Export) {
346 d->cachedExportFilter = filter;
347 d->cachedExportFilterMimeType = typeName;
355 filter->setFilename(location);
356 filter->setRealFilename(realLocation);
358 filter->setMimeType(typeName);
360 if (direction ==
Import) {
362 filter->setImportUserFeedBackInterface(
new SynchronousUserFeedbackInterface(kisMain,
batchMode()));
365 if (!
d->updater.isNull()) {
373 filter->setUpdater(
d->updater);
377 if (direction ==
Export) {
379 to = typeName.toLatin1();
382 from = typeName.toLatin1();
391 if (direction ==
Import) {
393 KisUsageLogger::log(QString(
"Importing %1 to %2. Location: %3. Real location: %4. Batchmode: %5")
394 .arg(QString::fromLatin1(from), QString::fromLatin1(to), location,
395 realLocation, QString::number(
batchMode())));
405 result =
doImport(location, filter);
412 qWarning() <<
"Loaded a profile-less file without a fallback. Rejecting image "
417 "model: %6 %5 (%7). Layers: %8")
418 .arg(QString::fromLatin1(from), QString::number(image->
width()),
419 QString::number(image->
height()), QString::number(image->
xRes()),
423 QString::number(image->
nlayers())));
425 qWarning() <<
"The filter returned OK, but there is no image";
430 KisUsageLogger::log(QString(
"Failed to load image from %1").arg(QString::fromLatin1(from)));
435 if (!exportConfiguration) {
436 exportConfiguration = filter->lastSavedConfiguration(from, to);
439 if (exportConfiguration) {
443 bool alsoAsKra =
false;
447 &alsoAsKra, isAdvancedExporting);
456 "Converting from %1 to %2. Location: %3. Real location: %4. Batchmode: %5. Configuration: %6")
457 .arg(QString::fromLatin1(from), QString::fromLatin1(to), location, realLocation,
459 (exportConfiguration ? exportConfiguration->toXML() :
"none")));
464 exportConfiguration, alsoAsKraLocation));
472 exportConfiguration, alsoAsKraLocation));
474 result =
doExport(location, filter, exportConfiguration, alsoAsKraLocation);
477 if (exportConfiguration && !
batchMode()) {
488 const bool isThereAlpha =
496 (cs->
profile()->
name().contains(QLatin1String(
"srgb"), Qt::CaseInsensitive) &&
497 !cs->
profile()->
name().contains(QLatin1String(
"g10")));
510 static_cast<int>(primaries));
511 exportConfiguration->setProperty(
513 static_cast<int>(transferFunction));
526 const QByteArray &from,
527 const QByteArray &to,
528 const bool batchMode,
529 const bool showWarnings,
531 bool isAdvancedExporting)
547 errors = checker.
errors();
552 if (QThread::currentThread() == qApp->thread()) {
553 wdg = filter->createConfigurationWidget(0, from, to);
556 if (wdg && kisMain) {
564 warnings.append(i18nc(
"image conversion warning",
"The image contains <b>assistants</b>. The assistants will not be saved."));
567 warnings.append(i18nc(
"image conversion warning",
"The image contains <b>reference images</b>. The reference images will not be saved."));
570 warnings.append(i18nc(
"image conversion warning",
"The image contains <b>guides</b>. The guides will not be saved."));
573 warnings.append(i18nc(
"image conversion warning",
"The image contains a <b>custom grid configuration</b>. The configuration will not be saved."));
576 bool shouldFlattenTheImageBeforeScaling =
false;
578 if (isAdvancedExporting) {
579 QMap<QString, KisExportCheckBase *> exportChecks = filter->exportChecks();
581 const bool filterSupportsMultilayerExport =
582 exportChecks.contains(
"MultiLayerCheck") &&
586 if (!filterSupportsMultilayerExport) {
587 shouldFlattenTheImageBeforeScaling =
true;
590 shouldFlattenTheImageBeforeScaling =
true;
591 warnings.append(i18nc(
"image conversion warning",
"Trying to perform an Advanced Export with the image containing a <b>filter layer</b>. The image will be <b>flattened</b> before resizing."));
594 shouldFlattenTheImageBeforeScaling =
true;
595 warnings.append(i18nc(
"image conversion warning",
"Trying to perform an Advanced Export with the image containing a <b>filter mask</b>. The image will be <b>flattened</b> before resizing."));
597 bool hasLayerStyles =
600 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
601 return layer && layer->layerStyle();
604 if (hasLayerStyles) {
605 shouldFlattenTheImageBeforeScaling =
true;
606 warnings.append(i18nc(
"image conversion warning",
"Trying to perform an Advanced Export with the image containing a <b>layer style</b>. The image will be <b>flattened</b> before resizing."));
612 QString error =
"<html><body><p><b>"
613 + i18n(
"Error: cannot save this image as a %1.", mimeUserDescription)
614 +
"</b> " + i18n(
"Reasons:") +
"</p>"
616 Q_FOREACH(
const QString &w, errors) {
617 error +=
"\n<li>" + w +
"</li>";
622 QMessageBox::critical(
KisPart::instance()->currentMainwindow(), i18nc(
"@title:window",
"Krita: Export Error"), error);
626 if (!
batchMode && (wdg || !warnings.isEmpty() || isAdvancedExporting)) {
627 QWidget *page =
new QWidget();
628 QVBoxLayout *layout =
new QVBoxLayout(page);
630 if (showWarnings && !warnings.isEmpty()) {
631 QHBoxLayout *hLayout =
new QHBoxLayout();
633 QLabel *labelWarning =
new QLabel();
635 hLayout->addWidget(labelWarning);
639 bn->setText(i18nc(
"Keep the extra space at the end of the sentence, please",
"Warning: saving as a %1 will lose information from your image. ", mimeUserDescription));
640 bn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
642 hLayout->addWidget(bn);
644 layout->addLayout(hLayout);
646 QTextBrowser *browser =
new QTextBrowser();
647 browser->setMinimumWidth(bn->width());
650 QString warning =
"<html><body><p><b>"
651 + i18n(
"You will lose information when saving this image as a %1.", mimeUserDescription);
653 if (warnings.size() == 1) {
654 warning +=
"</b> " + i18n(
"Reason:") +
"</p>";
657 warning +=
"</b> " + i18n(
"Reasons:") +
"</p>";
659 warning +=
"<p/><ul>";
661 Q_FOREACH(
const QString &w, warnings) {
662 warning +=
"\n<li>" + w +
"</li>";
666 browser->setHtml(warning);
669 QTabWidget *box =
new QTabWidget;
672 box->addTab(wdg,i18n(
"Options"));
677 if (isAdvancedExporting) {
680 box->addTab(wdgImageSize,i18n(
"Resize"));
682 layout->addWidget(box);
684 QCheckBox *chkAlsoAsKra = 0;
685 if (showWarnings && !warnings.isEmpty()) {
686 chkAlsoAsKra =
new QCheckBox(i18n(
"Also save your image as a Krita file."));
687 chkAlsoAsKra->setChecked(
KisConfig(
true).readEntry<bool>(
"AlsoSaveAsKra",
false));
688 layout->addWidget(chkAlsoAsKra);
693 page->setParent(&dlg);
695 dlg.setWindowTitle(mimeUserDescription);
697 if (showWarnings || wdg || isAdvancedExporting) {
706 *alsoAsKra = chkAlsoAsKra->isChecked();
711 if (isAdvancedExporting && wdgImageSize) {
712 if (shouldFlattenTheImageBeforeScaling) {
735 QFile file(location);
736 if (!file.exists()) {
740 if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
756 const QString alsoAsKraLocation)
761 if (!alsoAsKraLocation.isNull() && status.
isOk()) {
769 filter->setFilename(alsoAsKraLocation);
772 filter->lastSavedConfiguration(mime, mime);
774 status =
doExportImpl(alsoAsKraLocation, filter, kraExportConfiguration);
792#if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS) || defined(Q_OS_ANDROID))
797QFileDevice::FileError getFileOpenError(
const QFileDevice &file)
799 QFileDevice::FileError error = file.error();
806 if (error == QFileDevice::NoError) {
807 return QFileDevice::OpenError;
817 QSaveFile file(location);
818 file.setDirectWriteFallback(
true);
819 if (filter->supportsIO() && !file.open(QFile::WriteOnly)) {
821 QFileInfo fi(location);
822 QTemporaryFile file(QDir::tempPath() +
"/.XXXXXX.kra");
823 if (filter->supportsIO() && !file.open()) {
827 file.cancelWriting();
834 if (filter->supportsIO()) {
835 if (!status.
isOk()) {
837 file.cancelWriting();
841 if (!file.commit()) {
842 qWarning() <<
"Could not commit QSaveFile";
845#elif defined(Q_OS_ANDROID)
854 }
else if (!file.open()) {
859 qint64 expectedSize = file.size();
860 if (expectedSize < 0 || !file.seek(0)) {
870 if (!
target.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
878 qint64 totalWritten = 0;
880 qint64 read = file.read(buf.data(), BUFSIZ);
884 }
else if (read == 0) {
889 qint64 written =
target.write(buf.constData(), read);
896 totalWritten += written;
911 if (totalWritten != expectedSize) {
922 if (!file.copy(location)) {
923 file.setAutoRemove(
false);
932 QString verificationResult = filter->verify(location);
933 if (!verificationResult.isEmpty()) {
948 return location +
".kra";
float value(const T *src, size_t ch)
KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g)
QList< QString > QStringList
ColorPrimaries
The colorPrimaries enum Enum of colorants, follows ITU H.273 for values 0 to 255, and has extra known...
@ PRIMARIES_ADOBE_RGB_1998
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
void writeEntry(const QString &name, const T &value)
void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
KisSharedPtr< KisReferenceImagesLayer > referenceImagesLayer() const
void setErrorMessage(const QString &errMsg)
static QByteArray nativeFormatMimeType()
QList< KisPaintingAssistantSP > assistants
bool fileBatchMode() const
KisGuidesConfig guidesConfig
const KoColorSpace * colorSpace() const
void flatten(KisNodeSP activeNode)
KisPaintDeviceSP projection() const
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
start asynchronous operation on scaling the image
The base class for import and export filters.
void setMimeType(const QString &mime)
static const QString ColorDepthIDTag
static const QString ColorModelIDTag
static const QString CICPTransferCharacteristicsTag
static const QString CICPPrimariesTag
static const QString ImageContainsTransparencyTag
static const QString sRGBTag
static const QString HDRTag
The class managing all the filters.
KisImportExportErrorCode importDocument(const QString &location, const QString &mimeType)
ConversionResult convert(Direction direction, const QString &location, const QString &realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync, bool isAdvancedExporting=false)
KisImportExportErrorCode doExport(const QString &location, QSharedPointer< KisImportExportFilter > filter, KisPropertiesConfigurationSP exportConfiguration, const QString alsoAsKraLocation)
void setUpdater(KoUpdaterPtr updater)
QString getAlsoAsKraLocation(const QString location) const
QString cachedExportFilterMimeType
bool batchMode(void) const
QFuture< KisImportExportErrorCode > exportDocumentAsync(const QString &location, const QString &realLocation, const QByteArray &mimeType, KisImportExportErrorCode &status, bool showWarnings=true, KisPropertiesConfigurationSP exportConfiguration=0, bool isAdvancedExporting=false)
KisImportExportManager(KisDocument *document)
static QString getUriForAdditionalFile(const QString &defaultUri, QWidget *parent)
static QStringList m_exportMimeTypes
static QString askForAudioFileName(const QString &defaultDir, QWidget *parent)
static KisImportExportFilter * filterForMimeType(const QString &mimetype, Direction direction)
filterForMimeType loads the relevant import/export plugin and returns it. The caller is responsible f...
KisImportExportErrorCode exportDocument(const QString &location, const QString &realLocation, const QByteArray &mimeType, bool showWarnings=true, KisPropertiesConfigurationSP exportConfiguration=0, bool isAdvancedExporting=false)
Exports the given file/document to the specified URL/mimetype.
bool askUserAboutExportConfiguration(QSharedPointer< KisImportExportFilter > filter, KisPropertiesConfigurationSP exportConfiguration, const QByteArray &from, const QByteArray &to, bool batchMode, const bool showWarnings, bool *alsoAsKra, bool isAdvancedExporting=false)
~KisImportExportManager() override
KisImportExportErrorCode doExportImpl(const QString &location, QSharedPointer< KisImportExportFilter > filter, KisPropertiesConfigurationSP exportConfiguration)
static void fillStaticExportConfigurationProperties(KisPropertiesConfigurationSP exportConfiguration, KisImageSP image)
KisImportExportErrorCode doImport(const QString &location, QSharedPointer< KisImportExportFilter > filter)
QSharedPointer< KisImportExportFilter > cachedExportFilter
static QStringList m_importMimeTypes
A static cache for the availability checks of filters.
static QStringList supportedMimeTypes(Direction direction)
KisViewManager * viewManager
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
static QString descriptionForMimeType(const QString &mimeType)
Find the user-readable description for the given mimetype.
const KoColorSpace * colorSpace() const
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
static KisPart * instance()
KisMainWindow * currentMainwindow() const
bool check(KisImageSP image, QMap< QString, KisExportCheckBase * > filterChecks)
check checks the image against the capabilities of the export filter
QStringList errors() const
QStringList warnings() const
static void log(const QString &message)
Logs with date/time.
virtual bool hasHighDynamicRange() const =0
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
virtual const KoColorProfile * profile() const =0
A dialog base class with standard buttons and predefined layouts.
void setMainWidget(QWidget *widget)
void setButtons(ButtonCodes buttonMask)
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
@ Cancel
Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
static KoJsonTrader * instance()
QList< Plugin > query(const QString &servicetype, const QString &mimetype)
KisFilterStrategy * filterType()
double desiredResolution()
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_ASSERT_RECOVER_NOOP(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
QSharedPointer< T > toQShared(T *ptr)
KisPinnedSharedPtr< KisPropertiesConfiguration > KisPropertiesConfigurationSP
KisSharedPtr< KisNode > KisNodeSP
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
QIcon loadIcon(const QString &name)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void forceAllDelayedNodesUpdate(KisNodeSP root)
KisImportExportErrorCode m_status
KisImportExportErrorCode status() const
QFuture< KisImportExportErrorCode > futureStatus() const
QFuture< KisImportExportErrorCode > m_futureStatus
ConversionResult(KisImportExportErrorCode status)
ConversionResult(const QFuture< KisImportExportErrorCode > &futureStatus)
void setStatus(KisImportExportErrorCode value)
virtual ColorPrimaries getColorPrimaries() const
getColorPrimaries
virtual TransferCharacteristics getTransferCharacteristics() const
getTransferCharacteristics This function should be subclassed at some point so we can get the value f...
QObject * instance() const
QJsonObject metaData() const
QString errorString() const