Krita Source Code Documentation
Loading...
Searching...
No Matches
KisImportExportManager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2016 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8
9#include <QDir>
10#include <QFile>
11#include <QLabel>
12#include <QVBoxLayout>
13#include <QList>
14#include <QApplication>
15#include <QByteArray>
16#include <QPluginLoader>
17#include <QFileInfo>
18#include <QMessageBox>
19#include <QJsonObject>
20#include <QTextBrowser>
21#include <QCheckBox>
22#include <QSaveFile>
23#include <QGroupBox>
24#include <QFuture>
25#include <QtConcurrent>
26
27#include <klocalizedstring.h>
28#include <ksqueezedtextlabel.h>
29#include <kpluginfactory.h>
30
31#include <KisMimeDatabase.h>
32#include <KisPart.h>
33#include <KisPopupButton.h>
34#include <KisPreExportChecker.h>
35#include <KisUsageLogger.h>
36#include <KoColorProfile.h>
38#include <KoDialog.h>
39#include <KoFileDialog.h>
40#include <KoJsonTrader.h>
41#include <KoProgressUpdater.h>
42#include <kis_assert.h>
43#include <kis_config_widget.h>
44#include <kis_debug.h>
45#include <kis_icon_utils.h>
46#include <kis_image.h>
47#include <kis_iterator_ng.h>
48#include <kis_layer_utils.h>
49#include <kis_paint_layer.h>
50#include <kis_painter.h>
51
52#include "KisDocument.h"
55#include "KisMainWindow.h"
59#include "kis_config.h"
60#include "kis_grid_config.h"
61#include "kis_guides_config.h"
63#include <kis_filter_mask.h>
64
66
67// static cache for import and export mimetypes
70
71namespace {
72struct SynchronousUserFeedbackInterface : KisImportUserFeedbackInterface
73{
74 SynchronousUserFeedbackInterface(QWidget *parent, bool batchMode)
75 : m_parent(parent)
76 , m_batchMode(batchMode)
77 {
78 }
79
80 Result askUser(AskCallback callback) override
81 {
82 if (m_batchMode) return SuppressedByBatchMode;
83 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_parent, SuppressedByBatchMode);
84
85 return callback(m_parent) ? Success : UserCancelled;
86 }
87
88 QWidget *m_parent {nullptr};
89 bool m_batchMode {false};
90};
91
92}
93
94
103
106 {
107 }
108
114
120
121 bool isAsync() const {
122 return m_isAsync;
123 }
124
126 // if the result is not async, then it means some failure happened,
127 // just return a cancelled future
129
130 return m_futureStatus;
131 }
132
134 return m_status;
135 }
136
140private:
141 bool m_isAsync = false;
144};
145
146
148 : m_document(document)
149 , d(new Private)
150{
151}
152
157
158KisImportExportErrorCode KisImportExportManager::importDocument(const QString& location, const QString& mimeType)
159{
160 ConversionResult result = convert(Import, location, location, mimeType, false, 0, false);
162
163 return result.status();
164}
165
166KisImportExportErrorCode KisImportExportManager::exportDocument(const QString& location, const QString& realLocation, const QByteArray& mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAdvancedExporting)
167{
168 ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, false, isAdvancedExporting);
170
171 return result.status();
172}
173
174QFuture<KisImportExportErrorCode> KisImportExportManager::exportDocumentAsync(const QString &location, const QString &realLocation, const QByteArray &mimeType,
175 KisImportExportErrorCode &status, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAdvancedExporting)
176{
177 ConversionResult result = convert(Export, location, realLocation, mimeType, showWarnings, exportConfiguration, true, isAdvancedExporting);
180
181 status = result.status();
182 return result.futureStatus();
183}
184
185// The static method to figure out to which parts of the
186// graph this mimetype has a connection to.
188{
189 // Find the right mimetype by the extension
190 QSet<QString> mimeTypes;
191 // mimeTypes << KisDocument::nativeFormatMimeType() << "application/x-krita-paintoppreset" << "image/openraster";
192
193 if (direction == KisImportExportManager::Import) {
194 if (m_importMimeTypes.isEmpty()) {
195 QList<KoJsonTrader::Plugin> list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
196 Q_FOREACH(const KoJsonTrader::Plugin &loader, list) {
197 QJsonObject json = loader.metaData().value("MetaData").toObject();
198 Q_FOREACH(const QString &mimetype, json.value("X-KDE-Import").toString().split(",", Qt::SkipEmptyParts)) {
199
200 //qDebug() << "Adding import mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
201 mimeTypes << mimetype;
202 }
203 }
204 m_importMimeTypes = QList<QString>(mimeTypes.begin(), mimeTypes.end());
205 }
206 return m_importMimeTypes;
207 }
208 else if (direction == KisImportExportManager::Export) {
209 if (m_exportMimeTypes.isEmpty()) {
210 QList<KoJsonTrader::Plugin> list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
211 Q_FOREACH(const KoJsonTrader::Plugin &loader, list) {
212 QJsonObject json = loader.metaData().value("MetaData").toObject();
213 Q_FOREACH(const QString &mimetype, json.value("X-KDE-Export").toString().split(",", Qt::SkipEmptyParts)) {
214
215 //qDebug() << "Adding export mimetype" << mimetype << KisMimeDatabase::descriptionForMimeType(mimetype) << "from plugin" << loader;
216 mimeTypes << mimetype;
217 }
218 }
219 m_exportMimeTypes = QList<QString>(mimeTypes.begin(), mimeTypes.end());
220 }
221 return m_exportMimeTypes;
222 }
223 return QStringList();
224}
225
227{
228 int weight = -1;
229 KisImportExportFilter *filter = 0;
230 QList<KoJsonTrader::Plugin>list = KoJsonTrader::instance()->query("Krita/FileFilter", "");
231
232 Q_FOREACH(const KoJsonTrader::Plugin &loader, list) {
233 QJsonObject json = loader.metaData().value("MetaData").toObject();
234 QString directionKey = direction == Export ? "X-KDE-Export" : "X-KDE-Import";
235
236 if (json.value(directionKey).toString().split(",", Qt::SkipEmptyParts).contains(mimetype)) {
237
238 KPluginFactory *factory = qobject_cast<KPluginFactory *>(loader.instance());
239
240 if (!factory) {
241 warnUI << loader.errorString();
242 continue;
243 }
244
245 QObject* obj = factory->create<KisImportExportFilter>(0);
246 if (!obj || !obj->inherits("KisImportExportFilter")) {
247 delete obj;
248 continue;
249 }
250
251 KisImportExportFilter *f = qobject_cast<KisImportExportFilter*>(obj);
252 if (!f) {
253 delete obj;
254 continue;
255 }
256
257 KIS_ASSERT_RECOVER_NOOP(json.value("X-KDE-Weight").isDouble());
258
259 int w = json.value("X-KDE-Weight").toInt();
260
261 if (w > weight) {
262 delete filter;
263 filter = f;
264 f->setObjectName(loader.fileName());
265 weight = w;
266 }
267 }
268 }
269
270 if (filter) {
271 filter->setMimeType(mimetype);
272 }
273 return filter;
274}
275
277{
278 return m_document->fileBatchMode();
279}
280
282{
283 d->updater = updater;
284}
285
286QString KisImportExportManager::askForAudioFileName(const QString &defaultDir, QWidget *parent)
287{
288 KoFileDialog dialog(parent, KoFileDialog::ImportFiles, "ImportAudio");
289
290 if (!defaultDir.isEmpty()) {
291 dialog.setDefaultDir(defaultDir);
292 }
293
294 QStringList mimeTypes;
295 mimeTypes << "audio/mpeg";
296 mimeTypes << "audio/ogg";
297 mimeTypes << "audio/vorbis";
298 mimeTypes << "audio/vnd.wave";
299 mimeTypes << "audio/flac";
300
301 dialog.setMimeTypeFilters(mimeTypes);
302 dialog.setCaption(i18nc("@title:window", "Open Audio"));
303
304 return dialog.filename();
305}
306
307QString KisImportExportManager::getUriForAdditionalFile(const QString &defaultUri, QWidget *parent)
308{
309 KoFileDialog dialog(parent, KoFileDialog::SaveFile, "Save Kra");
310
311 KIS_SAFE_ASSERT_RECOVER_NOOP(!defaultUri.isEmpty());
312
313 dialog.setDirectoryUrl(QUrl(defaultUri));
314 dialog.setMimeTypeFilters(QStringList("application/x-krita"));
315
316 return dialog.filename();
317}
318
319KisImportExportManager::ConversionResult KisImportExportManager::convert(KisImportExportManager::Direction direction, const QString &location, const QString& realLocation, const QString &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration, bool isAsync, bool isAdvancedExporting)
320{
321 // export configuration is supported for export only
322 KIS_SAFE_ASSERT_RECOVER_NOOP(direction == Export || !bool(exportConfiguration));
323
324 QString typeName = mimeType;
325 if (typeName.isEmpty()) {
326 typeName = KisMimeDatabase::mimeTypeForFile(location, direction == KisImportExportManager::Export ? false : true);
327 }
328
330
335 if (direction == KisImportExportManager::Export &&
336 d->cachedExportFilter &&
337 d->cachedExportFilterMimeType == typeName) {
338
339 filter = d->cachedExportFilter;
340 } else {
341
342 filter = toQShared(filterForMimeType(typeName, direction));
343
344 if (direction == Export) {
345 d->cachedExportFilter = filter;
346 d->cachedExportFilterMimeType = typeName;
347 }
348 }
349
350 if (!filter) {
352 }
353
354 filter->setFilename(location);
355 filter->setRealFilename(realLocation);
356 filter->setBatchMode(batchMode());
357 filter->setMimeType(typeName);
358
359 if (direction == Import) {
361 filter->setImportUserFeedBackInterface(new SynchronousUserFeedbackInterface(kisMain, batchMode()));
362 }
363
364 if (!d->updater.isNull()) {
365 // WARNING: The updater is not guaranteed to be persistent! If you ever want
366 // to add progress reporting to "Save also as .kra", make sure you create
367 // a separate KoProgressUpdater for that!
368
369 // WARNING2: the failsafe completion of the updater happens in the destructor
370 // the filter.
371
372 filter->setUpdater(d->updater);
373 }
374
375 QByteArray from, to;
376 if (direction == Export) {
378 to = typeName.toLatin1();
379 }
380 else {
381 from = typeName.toLatin1();
383 }
384
386 direction == Import || direction == Export,
388
390 if (direction == Import) {
391
392 KisUsageLogger::log(QString("Importing %1 to %2. Location: %3. Real location: %4. Batchmode: %5")
393 .arg(QString::fromLatin1(from), QString::fromLatin1(to), location,
394 realLocation, QString::number(batchMode())));
395
396 // async importing is not yet supported!
398
399 // FIXME: Dmitry says "this progress reporting code never worked. Initial idea was to implement it his way, but I stopped and didn't finish it"
400 if (0 && !batchMode()) {
401 KisAsyncActionFeedback f(i18n("Opening document..."), 0);
402 result = f.runAction(std::bind(&KisImportExportManager::doImport, this, location, filter));
403 } else {
404 result = doImport(location, filter);
405 }
406 if (result.status().isOk()) {
407 KisImageSP image = m_document->image().toStrongRef();
408 if (image) {
409 KIS_SAFE_ASSERT_RECOVER(image->colorSpace() != nullptr && image->colorSpace()->profile() != nullptr)
410 {
411 qWarning() << "Loaded a profile-less file without a fallback. Rejecting image "
412 "opening";
414 }
415 KisUsageLogger::log(QString("Loaded image from %1. Size: %2 * %3 pixels, %4 dpi. Color "
416 "model: %6 %5 (%7). Layers: %8")
417 .arg(QString::fromLatin1(from), QString::number(image->width()),
418 QString::number(image->height()), QString::number(image->xRes()),
419 image->colorSpace()->colorModelId().name(),
420 image->colorSpace()->colorDepthId().name(),
421 image->colorSpace()->profile()->name(),
422 QString::number(image->nlayers())));
423 } else {
424 qWarning() << "The filter returned OK, but there is no image";
425 }
426
427 }
428 else {
429 KisUsageLogger::log(QString("Failed to load image from %1").arg(QString::fromLatin1(from)));
430 }
431
432 }
433 else /* if (direction == Export) */ {
434 if (!exportConfiguration) {
435 exportConfiguration = filter->lastSavedConfiguration(from, to);
436 }
437
438 if (exportConfiguration) {
439 fillStaticExportConfigurationProperties(exportConfiguration);
440 }
441
442 bool alsoAsKra = false;
443 bool askUser = askUserAboutExportConfiguration(filter, exportConfiguration,
444 from, to,
445 batchMode(), showWarnings,
446 &alsoAsKra, isAdvancedExporting);
447
448
449 if (!batchMode() && !askUser) {
451 }
452
454 QString(
455 "Converting from %1 to %2. Location: %3. Real location: %4. Batchmode: %5. Configuration: %6")
456 .arg(QString::fromLatin1(from), QString::fromLatin1(to), location, realLocation,
457 QString::number(batchMode()),
458 (exportConfiguration ? exportConfiguration->toXML() : "none")));
459
460 const QString alsoAsKraLocation = alsoAsKra ? getAlsoAsKraLocation(location) : QString();
461 if (isAsync) {
462 result = QtConcurrent::run(std::bind(&KisImportExportManager::doExport, this, location, filter,
463 exportConfiguration, alsoAsKraLocation));
464
465 // we should explicitly report that the exporting has been initiated
467
468 } else if (!batchMode()) {
469 KisAsyncActionFeedback f(i18n("Saving document..."), 0);
470 result = f.runAction(std::bind(&KisImportExportManager::doExport, this, location, filter,
471 exportConfiguration, alsoAsKraLocation));
472 } else {
473 result = doExport(location, filter, exportConfiguration, alsoAsKraLocation);
474 }
475
476 if (exportConfiguration && !batchMode()) {
477 KisConfig(false).setExportConfiguration(typeName, exportConfiguration);
478 }
479 }
480 return result;
481}
482
484{
485 KisPaintDeviceSP dev = image->projection();
486 const KoColorSpace* cs = dev->colorSpace();
487 const bool isThereAlpha =
489
490 exportConfiguration->setProperty(KisImportExportFilter::ImageContainsTransparencyTag, isThereAlpha);
491 exportConfiguration->setProperty(KisImportExportFilter::ColorModelIDTag, cs->colorModelId().id());
492 exportConfiguration->setProperty(KisImportExportFilter::ColorDepthIDTag, cs->colorDepthId().id());
493
494 const bool sRGB =
495 (cs->profile()->name().contains(QLatin1String("srgb"), Qt::CaseInsensitive) &&
496 !cs->profile()->name().contains(QLatin1String("g10")));
497 exportConfiguration->setProperty(KisImportExportFilter::sRGBTag, sRGB);
498
499 ColorPrimaries primaries = cs->profile()->getColorPrimaries();
500 if (primaries >= PRIMARIES_ADOBE_RGB_1998) {
501 primaries = PRIMARIES_UNSPECIFIED;
502 }
503 TransferCharacteristics transferFunction =
505 if (transferFunction >= TRC_GAMMA_1_8) {
506 transferFunction = TRC_UNSPECIFIED;
507 }
508 exportConfiguration->setProperty(KisImportExportFilter::CICPPrimariesTag,
509 static_cast<int>(primaries));
510 exportConfiguration->setProperty(
512 static_cast<int>(transferFunction));
513 exportConfiguration->setProperty(KisImportExportFilter::HDRTag, cs->hasHighDynamicRange());
514}
515
516
521
524 KisPropertiesConfigurationSP exportConfiguration,
525 const QByteArray &from,
526 const QByteArray &to,
527 const bool batchMode,
528 const bool showWarnings,
529 bool *alsoAsKra,
530 bool isAdvancedExporting)
531{
532
533 // prevents the animation renderer from running this code
534
535
536 const QString mimeUserDescription = KisMimeDatabase::descriptionForMimeType(to);
537
538 QStringList warnings;
539 QStringList errors;
540
541 {
542 KisPreExportChecker checker;
543 checker.check(m_document->image(), filter->exportChecks());
544
545 warnings = checker.warnings();
546 errors = checker.errors();
547 }
548
549 KisConfigWidget *wdg = 0;
550
551 if (QThread::currentThread() == qApp->thread()) {
552 wdg = filter->createConfigurationWidget(0, from, to);
553
555 if (wdg && kisMain) {
556 KisViewManager *manager = kisMain->viewManager();
557 wdg->setView(manager);
558 }
559 }
560
561 // Extra checks that cannot be done by the checker, because the checker only has access to the image.
562 if (!m_document->assistants().isEmpty() && to != m_document->nativeFormatMimeType()) {
563 warnings.append(i18nc("image conversion warning", "The image contains <b>assistants</b>. The assistants will not be saved."));
564 }
566 warnings.append(i18nc("image conversion warning", "The image contains <b>reference images</b>. The reference images will not be saved."));
567 }
568 if (m_document->guidesConfig().hasGuides() && !filter->exportSupportsGuides()) {
569 warnings.append(i18nc("image conversion warning", "The image contains <b>guides</b>. The guides will not be saved."));
570 }
572 warnings.append(i18nc("image conversion warning", "The image contains a <b>custom grid configuration</b>. The configuration will not be saved."));
573 }
574
575 bool shouldFlattenTheImageBeforeScaling = false;
576
577 if (isAdvancedExporting) {
578 QMap<QString, KisExportCheckBase *> exportChecks = filter->exportChecks();
579
580 const bool filterSupportsMultilayerExport =
581 exportChecks.contains("MultiLayerCheck") &&
582 exportChecks["MultiLayerCheck"]->checkNeeded(m_document->image()) &&
583 exportChecks["MultiLayerCheck"]->check(m_document->image()) == KisExportCheckBase::SUPPORTED;
584
585 if (!filterSupportsMultilayerExport) {
586 shouldFlattenTheImageBeforeScaling = true;
587 } else {
588 if (KisLayerUtils::findNodeByType<KisAdjustmentLayer>(m_document->image()->root())) {
589 shouldFlattenTheImageBeforeScaling = true;
590 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."));
591 }
592 if (KisLayerUtils::findNodeByType<KisFilterMask>(m_document->image()->root())) {
593 shouldFlattenTheImageBeforeScaling = true;
594 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."));
595 }
596 bool hasLayerStyles =
598 [] (KisNodeSP node) {
599 KisLayer *layer = dynamic_cast<KisLayer*>(node.data());
600 return layer && layer->layerStyle();
601 });
602
603 if (hasLayerStyles) {
604 shouldFlattenTheImageBeforeScaling = true;
605 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."));
606 }
607 }
608 }
609
610 if (!batchMode && !errors.isEmpty()) {
611 QString error = "<html><body><p><b>"
612 + i18n("Error: cannot save this image as a %1.", mimeUserDescription)
613 + "</b> " + i18n("Reasons:") + "</p>"
614 + "<p/><ul>";
615 Q_FOREACH(const QString &w, errors) {
616 error += "\n<li>" + w + "</li>";
617 }
618
619 error += "</ul>";
620
621 QMessageBox::critical(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita: Export Error"), error);
622 return false;
623 }
624
625 if (!batchMode && (wdg || !warnings.isEmpty() || isAdvancedExporting)) {
626 QWidget *page = new QWidget();
627 QVBoxLayout *layout = new QVBoxLayout(page);
628
629 if (showWarnings && !warnings.isEmpty()) {
630 QHBoxLayout *hLayout = new QHBoxLayout();
631
632 QLabel *labelWarning = new QLabel();
633 labelWarning->setPixmap(KisIconUtils::loadIcon("dialog-warning").pixmap(48, 48));
634 hLayout->addWidget(labelWarning);
635
636 KisPopupButton *bn = new KisPopupButton(0);
637
638 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));
639 bn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
640
641 hLayout->addWidget(bn);
642
643 layout->addLayout(hLayout);
644
645 QTextBrowser *browser = new QTextBrowser();
646 browser->setMinimumWidth(bn->width());
647 bn->setPopupWidget(browser);
648
649 QString warning = "<html><body><p><b>"
650 + i18n("You will lose information when saving this image as a %1.", mimeUserDescription);
651
652 if (warnings.size() == 1) {
653 warning += "</b> " + i18n("Reason:") + "</p>";
654 }
655 else {
656 warning += "</b> " + i18n("Reasons:") + "</p>";
657 }
658 warning += "<p/><ul>";
659
660 Q_FOREACH(const QString &w, warnings) {
661 warning += "\n<li>" + w + "</li>";
662 }
663
664 warning += "</ul>";
665 browser->setHtml(warning);
666 }
667
668 QTabWidget *box = new QTabWidget;
669 if (wdg) {
670 wdg->setConfiguration(exportConfiguration);
671 box->addTab(wdg,i18n("Options"));
672 }
673
674 WdgImageSize *wdgImageSize = nullptr;
675
676 if (isAdvancedExporting) {
677 wdgImageSize = new WdgImageSize(box, m_document->image()->width(), m_document->image()->height(), m_document->image()->yRes());
678
679 box->addTab(wdgImageSize,i18n("Resize"));
680 }
681 layout->addWidget(box);
682
683 QCheckBox *chkAlsoAsKra = 0;
684 if (showWarnings && !warnings.isEmpty()) {
685 chkAlsoAsKra = new QCheckBox(i18n("Also save your image as a Krita file."));
686 chkAlsoAsKra->setChecked(KisConfig(true).readEntry<bool>("AlsoSaveAsKra", false));
687 layout->addWidget(chkAlsoAsKra);
688 }
689
690 KoDialog dlg(qApp->activeWindow());
691 dlg.setMainWidget(page);
692 page->setParent(&dlg);
694 dlg.setWindowTitle(mimeUserDescription);
695
696 if (showWarnings || wdg || isAdvancedExporting) {
697 if (!dlg.exec()) {
698 return false;
699 }
700 }
701
702 *alsoAsKra = false;
703 if (chkAlsoAsKra) {
704 KisConfig(false).writeEntry<bool>("AlsoSaveAsKra", chkAlsoAsKra->isChecked());
705 *alsoAsKra = chkAlsoAsKra->isChecked();
706 }
707
708 KIS_SAFE_ASSERT_RECOVER_NOOP(bool(isAdvancedExporting) == bool(wdgImageSize));
709
710 if (isAdvancedExporting && wdgImageSize) {
711 if (shouldFlattenTheImageBeforeScaling) {
714 }
715
716 const QSize desiredSize(wdgImageSize->desiredWidth(), wdgImageSize->desiredHeight());
717 double res = wdgImageSize->desiredResolution();
718 m_document->savingImage()->scaleImage(desiredSize,res,res,wdgImageSize->filterType());
722 }
723
724 if (wdg) {
725 *exportConfiguration = *wdg->configuration();
726 }
727 }
728
729 return true;
730}
731
733{
734 QFile file(location);
735 if (!file.exists()) {
737 }
738
739 if (filter->supportsIO() && !file.open(QFile::ReadOnly)) {
741 }
742
743 KisImportExportErrorCode status = filter->convert(m_document, &file, KisPropertiesConfigurationSP());
744
745 if (file.isOpen()) {
746 file.close();
747 }
748
749 return status;
750}
751
754 KisPropertiesConfigurationSP exportConfiguration,
755 const QString alsoAsKraLocation)
756{
758 doExportImpl(location, filter, exportConfiguration);
759
760 if (!alsoAsKraLocation.isNull() && status.isOk()) {
761 QByteArray mime = m_document->nativeFormatMimeType();
763 filterForMimeType(QString::fromLatin1(mime), Export));
764
766
767 if (filter) {
768 filter->setFilename(alsoAsKraLocation);
769
770 KisPropertiesConfigurationSP kraExportConfiguration =
771 filter->lastSavedConfiguration(mime, mime);
772
773 status = doExportImpl(alsoAsKraLocation, filter, kraExportConfiguration);
774 } else {
776 }
777 }
778
779 return status;
780}
781
782// Temporary workaround until QTBUG-57299 is fixed.
783// 02-10-2019 update: the bug is closed, but we've still seen this issue.
784// and without using QSaveFile the issue can still occur
785// when QFile::copy fails because Dropbox/Google/OneDrive
786// locks the target file.
787// 02-24-2022 update: Added macOS since QSaveFile does not work on sandboxed krita
788// It can work if user gives access to the container dir, but
789// we cannot guarantee the user gave us permission.
790#if !(defined(Q_OS_WIN) || defined(Q_OS_MACOS))
791#define USE_QSAVEFILE
792#endif
793
795{
796#ifdef USE_QSAVEFILE
797 QSaveFile file(location);
798 file.setDirectWriteFallback(true);
799 if (filter->supportsIO() && !file.open(QFile::WriteOnly)) {
800#else
801 QFileInfo fi(location);
802 QTemporaryFile file(QDir::tempPath() + "/.XXXXXX.kra");
803 if (filter->supportsIO() && !file.open()) {
804#endif
805 QFileDevice::FileError error = file.error();
806 if (file.error() == QFileDevice::NoError) {
813 error = QFileDevice::OpenError;
814 }
816#ifdef USE_QSAVEFILE
817 file.cancelWriting();
818#endif
819 return result;
820 }
821
822 KisImportExportErrorCode status = filter->convert(m_document, &file, exportConfiguration);
823
824 if (filter->supportsIO()) {
825 if (!status.isOk()) {
826#ifdef USE_QSAVEFILE
827 file.cancelWriting();
828#endif
829 } else {
830#ifdef USE_QSAVEFILE
831 if (!file.commit()) {
832 qWarning() << "Could not commit QSaveFile";
833 status = KisImportExportErrorCannotWrite(file.error());
834 }
835#else
836 file.flush();
837 file.close();
838 QFile target(location);
839 if (target.exists()) {
840 // There should already be a .kra~ backup
841 target.remove();
842 }
843 if (!file.copy(location)) {
844 file.setAutoRemove(false);
845 return KisImportExportErrorCannotWrite(file.error());
846 }
847#endif
848 }
849 }
850
851 if (status.isOk()) {
852 // Do some minimal verification
853 QString verificationResult = filter->verify(location);
854 if (!verificationResult.isEmpty()) {
856 m_document->setErrorMessage(verificationResult);
857 }
858 }
859
860 return status;
861
862}
863
864QString KisImportExportManager::getAlsoAsKraLocation(const QString location) const
865{
866#ifdef Q_OS_ANDROID
867 return getUriForAdditionalFile(location, nullptr);
868#else
869 return location + ".kra";
870#endif
871}
872
873#include <KisMimeDatabase.h>
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_UNSPECIFIED
@ PRIMARIES_ADOBE_RGB_1998
TransferCharacteristics
The transferCharacteristics enum Enum of transfer characteristics, follows ITU H.273 for values 0 to ...
virtual void setView(KisViewManager *view)
virtual KisPropertiesConfigurationSP configuration() const =0
virtual void setConfiguration(const KisPropertiesConfigurationSP config)=0
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
void setExportConfiguration(const QString &filterId, KisPropertiesConfigurationSP properties) const
KisSharedPtr< KisReferenceImagesLayer > referenceImagesLayer() const
KisImageSP image
void setErrorMessage(const QString &errMsg)
KisGridConfig gridConfig
static QByteArray nativeFormatMimeType()
KisImageSP savingImage
QList< KisPaintingAssistantSP > assistants
bool fileBatchMode() const
KisGuidesConfig guidesConfig
bool isDefault() const
void waitForDone()
const KoColorSpace * colorSpace() const
void flatten(KisNodeSP activeNode)
KisPaintDeviceSP projection() const
qint32 width() const
void scaleImage(const QSize &size, qreal xres, qreal yres, KisFilterStrategy *filterStrategy)
start asynchronous operation on scaling the image
Definition kis_image.cc:961
double xRes() const
double yRes() const
qint32 height() const
qint32 nlayers() const
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
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)
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)
Main window for Krita.
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()
Definition KisPart.cpp:131
KisMainWindow * currentMainwindow() const
Definition KisPart.cpp:483
void setPopupWidget(QWidget *widget)
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.
Definition KoDialog.h:116
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:354
void setButtons(ButtonCodes buttonMask)
Definition KoDialog.cpp:195
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Definition KoDialog.h:127
@ Cancel
Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Definition KoDialog.h:130
QString name() const
Definition KoID.cpp:68
QString id() const
Definition KoID.cpp:63
static KoJsonTrader * instance()
QList< Plugin > query(const QString &servicetype, const QString &mimetype)
KisFilterStrategy * filterType()
qint32 desiredWidth()
double desiredResolution()
qint32 desiredHeight()
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define warnUI
Definition kis_debug.h:94
QSharedPointer< T > toQShared(T *ptr)
KisPinnedSharedPtr< KisPropertiesConfiguration > KisPropertiesConfigurationSP
Definition kis_types.h:278
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:327
QIcon loadIcon(const QString &name)
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
void forceAllDelayedNodesUpdate(KisNodeSP root)
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
QString fileName() const
QJsonObject metaData() const
QString errorString() const