File indexing completed on 2024-05-26 04:33:41
0001 /* 0002 * SPDX-FileCopyrightText: 2005 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "kis_tiff_export.h" 0009 0010 #include <QBuffer> 0011 #include <QFileInfo> 0012 0013 #include <memory> 0014 0015 #include <exiv2/exiv2.hpp> 0016 #include <kpluginfactory.h> 0017 #ifdef Q_OS_WIN 0018 #include <io.h> 0019 #endif 0020 #include <tiffio.h> 0021 0022 #include <KisDocument.h> 0023 #include <KisExportCheckRegistry.h> 0024 #include <KoColorModelStandardIds.h> 0025 #include <KoDocumentInfo.h> 0026 #include <KoUnit.h> 0027 #include <kis_assert.h> 0028 #include <kis_group_layer.h> 0029 #include <kis_layer_utils.h> 0030 #include <kis_meta_data_backend_registry.h> 0031 #include <kis_paint_layer.h> 0032 #include <kis_tiff_writer_visitor.h> 0033 #include <KisExiv2IODevice.h> 0034 0035 #include <config-tiff.h> 0036 #ifdef TIFF_CAN_WRITE_PSD_TAGS 0037 #include "kis_tiff_psd_writer_visitor.h" 0038 #endif 0039 0040 #include "kis_dlg_options_tiff.h" 0041 #include "kis_tiff_converter.h" 0042 #include "kis_tiff_logger.h" 0043 0044 K_PLUGIN_FACTORY_WITH_JSON(KisTIFFExportFactory, "krita_tiff_export.json", registerPlugin<KisTIFFExport>();) 0045 0046 KisTIFFExport::KisTIFFExport(QObject *parent, const QVariantList &) 0047 : KisImportExportFilter(parent) 0048 , oldErrHandler(TIFFSetErrorHandler(&KisTiffErrorHandler)) 0049 , oldWarnHandler(TIFFSetWarningHandler(&KisTiffWarningHandler)) 0050 { 0051 } 0052 0053 KisTIFFExport::~KisTIFFExport() 0054 { 0055 TIFFSetErrorHandler(oldErrHandler); 0056 TIFFSetWarningHandler(oldWarnHandler); 0057 } 0058 0059 KisImportExportErrorCode KisTIFFExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration) 0060 { 0061 // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings 0062 KisPropertiesConfigurationSP cfg(new KisPropertiesConfiguration()); 0063 if (configuration) { 0064 cfg->fromXML(configuration->toXML()); 0065 } 0066 else { 0067 cfg = lastSavedConfiguration(KisDocument::nativeFormatMimeType(), "image/tiff"); 0068 } 0069 0070 KisTIFFOptions options; 0071 options.fromProperties(configuration); 0072 0073 if (!options.flatten && !options.saveAsPhotoshop) { 0074 const bool hasGroupLayers = 0075 KisLayerUtils::recursiveFindNode(document->savingImage()->root(), 0076 [] (KisNodeSP node) { 0077 return node->parent() && node->inherits("KisGroupLayer"); 0078 }); 0079 options.flatten = hasGroupLayers; 0080 } 0081 0082 KisImageSP kisimage = [&]() { 0083 if (options.flatten) { 0084 KisImageSP image = 0085 new KisImage(nullptr, 0086 document->savingImage()->width(), 0087 document->savingImage()->height(), 0088 document->savingImage()->colorSpace(), 0089 ""); 0090 image->setResolution(document->savingImage()->xRes(), 0091 document->savingImage()->yRes()); 0092 KisPaintDeviceSP pd = KisPaintDeviceSP( 0093 new KisPaintDevice(*document->savingImage()->projection())); 0094 KisPaintLayerSP l = 0095 KisPaintLayerSP(new KisPaintLayer(image.data(), 0096 "projection", 0097 OPACITY_OPAQUE_U8, 0098 pd)); 0099 image->addNode(KisNodeSP(l.data()), image->rootLayer().data()); 0100 return image; 0101 } else { 0102 return document->savingImage(); 0103 } 0104 }(); 0105 0106 dbgFile << "Start writing TIFF File"; 0107 KIS_ASSERT_RECOVER_RETURN_VALUE(kisimage, ImportExportCodes::InternalError); 0108 0109 QFile file(filename()); 0110 if (!file.open(QFile::ReadWrite)) { 0111 return {KisImportExportErrorCannotRead(file.error())}; 0112 } 0113 0114 // Open file for writing 0115 const QByteArray encodedFilename = QFile::encodeName(filename()); 0116 0117 // https://gitlab.com/libtiff/libtiff/-/issues/173 0118 #ifdef Q_OS_WIN 0119 const int handle = (int)(_get_osfhandle(file.handle())); 0120 #else 0121 const int handle = file.handle(); 0122 #endif 0123 0124 // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) 0125 std::unique_ptr<TIFF, decltype(&TIFFCleanup)> image(TIFFFdOpen(handle, encodedFilename.data(), "w"), &TIFFCleanup); 0126 0127 if (!image) { 0128 dbgFile << "Could not open the file for writing" << filename(); 0129 return ImportExportCodes::NoAccessToWrite; 0130 } 0131 0132 // Set the document information 0133 KoDocumentInfo *info = document->documentInfo(); 0134 QString title = info->aboutInfo("title"); 0135 if (!title.isEmpty()) { 0136 if (!TIFFSetField(image.get(), 0137 TIFFTAG_DOCUMENTNAME, 0138 title.toLatin1().constData())) { 0139 return ImportExportCodes::ErrorWhileWriting; 0140 } 0141 } 0142 QString abstract = info->aboutInfo("description"); 0143 if (!abstract.isEmpty()) { 0144 if (!TIFFSetField(image.get(), 0145 TIFFTAG_IMAGEDESCRIPTION, 0146 abstract.toLatin1().constData())) { 0147 return ImportExportCodes::ErrorWhileWriting; 0148 } 0149 } 0150 QString author = info->authorInfo("creator"); 0151 if (!author.isEmpty()) { 0152 if (!TIFFSetField(image.get(), 0153 TIFFTAG_ARTIST, 0154 author.toLatin1().constData())) { 0155 return ImportExportCodes::ErrorWhileWriting; 0156 } 0157 } 0158 0159 dbgFile << "xres: " << INCH_TO_POINT(kisimage->xRes()) 0160 << " yres: " << INCH_TO_POINT(kisimage->yRes()); 0161 if (!TIFFSetField( 0162 image.get(), 0163 TIFFTAG_XRESOLUTION, 0164 INCH_TO_POINT(kisimage->xRes()))) { // It is the "invert" macro 0165 // because we convert from 0166 // pointer-per-inch to points 0167 return ImportExportCodes::ErrorWhileWriting; 0168 } 0169 if (!TIFFSetField(image.get(), 0170 TIFFTAG_YRESOLUTION, 0171 INCH_TO_POINT(kisimage->yRes()))) { 0172 return ImportExportCodes::ErrorWhileWriting; 0173 } 0174 0175 if (!TIFFSetField(image.get(), TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) { 0176 return ImportExportCodes::ErrorWhileWriting; 0177 } 0178 0179 KisGroupLayer *root = 0180 dynamic_cast<KisGroupLayer *>(kisimage->rootLayer().data()); 0181 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(root, 0182 ImportExportCodes::InternalError); 0183 0184 #ifdef TIFF_CAN_WRITE_PSD_TAGS 0185 if (options.saveAsPhotoshop) { 0186 KisTiffPsdWriter writer(image.get(), &options); 0187 KisImportExportErrorCode result = writer.writeImage(root); 0188 if (!result.isOk()) { 0189 return result; 0190 } 0191 } else 0192 #endif // TIFF_CAN_WRITE_PSD_TAGS 0193 { 0194 KisTIFFWriterVisitor visitor(image.get(), &options); 0195 if (!(visitor.visit(root))) { 0196 return ImportExportCodes::Failure; 0197 } 0198 } 0199 0200 image.reset(); 0201 file.close(); 0202 0203 if (!options.flatten && !options.saveAsPhotoshop) { 0204 // HACK!! Externally inject the Exif metadata 0205 // libtiff has no way to access the fields wholesale 0206 try { 0207 KisExiv2IODevice::ptr_type basicIoDevice(new KisExiv2IODevice(filename())); 0208 0209 #if EXIV2_TEST_VERSION(0,28,0) 0210 const std::unique_ptr<Exiv2::Image> img = Exiv2::ImageFactory::open(std::move(basicIoDevice)); 0211 #else 0212 const std::unique_ptr<Exiv2::Image> img(Exiv2::ImageFactory::open(basicIoDevice).release()); 0213 #endif 0214 0215 img->readMetadata(); 0216 0217 Exiv2::ExifData &data = img->exifData(); 0218 0219 const KisMetaData::IOBackend *io = 0220 KisMetadataBackendRegistry::instance()->value("exif"); 0221 0222 // All IFDs are paint layer children of root 0223 KisNodeSP node = root->firstChild(); 0224 0225 QBuffer ioDevice; 0226 0227 // Get layer 0228 KisLayer *layer = qobject_cast<KisLayer *>(node.data()); 0229 Q_ASSERT(layer); 0230 0231 // Inject the data as any other IOBackend 0232 io->saveTo(layer->metaData(), &ioDevice); 0233 0234 Exiv2::ExifData dataToInject; 0235 0236 // Reinterpret the blob we just got and inject its contents into 0237 // tempData 0238 Exiv2::ExifParser::decode( 0239 dataToInject, 0240 reinterpret_cast<const Exiv2::byte *>(ioDevice.data().data()), 0241 static_cast<uint32_t>(ioDevice.size())); 0242 0243 for (const auto &v : dataToInject) { 0244 data[v.key()] = v.value(); 0245 } 0246 // Write metadata 0247 img->writeMetadata(); 0248 #if EXIV2_TEST_VERSION(0,28,0) 0249 } catch (Exiv2::Error &e) { 0250 errFile << "Failed injecting TIFF metadata:" << Exiv2::Error(e.code()).what(); 0251 #else 0252 } catch (Exiv2::AnyError &e) { 0253 errFile << "Failed injecting TIFF metadata:" << e.code() 0254 << e.what(); 0255 #endif 0256 } 0257 } 0258 return ImportExportCodes::OK; 0259 } 0260 0261 KisPropertiesConfigurationSP KisTIFFExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const 0262 { 0263 KisTIFFOptions options; 0264 return options.toProperties(); 0265 } 0266 0267 KisConfigWidget *KisTIFFExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const 0268 { 0269 return new KisTIFFOptionsWidget(parent); 0270 } 0271 0272 void KisTIFFExport::initializeCapabilities() 0273 { 0274 addCapability(KisExportCheckRegistry::instance()->get("MultiLayerCheck")->create(KisExportCheckBase::SUPPORTED)); 0275 addCapability(KisExportCheckRegistry::instance()->get("LayerOpacityCheck")->create(KisExportCheckBase::PARTIALLY)); 0276 addCapability(KisExportCheckRegistry::instance()->get("sRGBProfileCheck")->create(KisExportCheckBase::SUPPORTED)); 0277 addCapability(KisExportCheckRegistry::instance()->get("ExifCheck")->create(KisExportCheckBase::SUPPORTED)); 0278 addCapability(KisExportCheckRegistry::instance() 0279 ->get("TiffExifCheck") 0280 ->create(KisExportCheckBase::PARTIALLY)); 0281 addCapability( 0282 KisExportCheckRegistry::instance()->get("ColorModelHomogenousCheck")->create(KisExportCheckBase::SUPPORTED)); 0283 0284 QList<QPair<KoID, KoID>> supportedColorModels = { 0285 {}, 0286 {RGBAColorModelID, Integer8BitsColorDepthID}, 0287 {RGBAColorModelID, Integer16BitsColorDepthID}, 0288 {RGBAColorModelID, Float16BitsColorDepthID}, 0289 {RGBAColorModelID, Float32BitsColorDepthID}, 0290 {GrayAColorModelID, Integer8BitsColorDepthID}, 0291 {GrayAColorModelID, Integer16BitsColorDepthID}, 0292 {CMYKAColorModelID, Integer8BitsColorDepthID}, 0293 {CMYKAColorModelID, Integer16BitsColorDepthID}, 0294 {YCbCrAColorModelID, Integer8BitsColorDepthID}, 0295 {YCbCrAColorModelID, Integer16BitsColorDepthID}, 0296 {YCbCrAColorModelID, Float16BitsColorDepthID}, 0297 {YCbCrAColorModelID, Float32BitsColorDepthID}, 0298 {LABAColorModelID, Integer8BitsColorDepthID}, 0299 {LABAColorModelID, Integer16BitsColorDepthID}, 0300 {LABAColorModelID, Float16BitsColorDepthID}, 0301 {LABAColorModelID, Float32BitsColorDepthID}}; 0302 addSupportedColorModels(supportedColorModels, "TIFF"); 0303 0304 } 0305 0306 #include <kis_tiff_export.moc> 0307