Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tiff_export.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "kis_tiff_export.h"
9
10#include <QBuffer>
11
12#include <memory>
13
14#include <exiv2/exiv2.hpp>
15#include <kpluginfactory.h>
16#ifdef Q_OS_WIN
17#include <io.h>
18#endif
19#include <tiffio.h>
20
21#include <KisDocument.h>
24#include <KoDocumentInfo.h>
25#include <KoUnit.h>
26#include <kis_assert.h>
27#include <kis_group_layer.h>
28#include <kis_layer_utils.h>
30#include <kis_paint_layer.h>
32#include <KisExiv2IODevice.h>
33
34#include <config-tiff.h>
35#ifdef TIFF_CAN_WRITE_PSD_TAGS
37#endif
38
40#include "kis_tiff_converter.h"
41#include "kis_tiff_logger.h"
42
43K_PLUGIN_FACTORY_WITH_JSON(KisTIFFExportFactory, "krita_tiff_export.json", registerPlugin<KisTIFFExport>();)
44
45KisTIFFExport::KisTIFFExport(QObject *parent, const QVariantList &)
46 : KisImportExportFilter(parent)
47 , oldErrHandler(TIFFSetErrorHandler(&KisTiffErrorHandler))
48 , oldWarnHandler(TIFFSetWarningHandler(&KisTiffWarningHandler))
49{
50}
51
53{
54 TIFFSetErrorHandler(oldErrHandler);
55 TIFFSetWarningHandler(oldWarnHandler);
56}
57
59{
60 // If a configuration object was passed to the convert method, we use that, otherwise we load from the settings
62 if (configuration) {
63 cfg->fromXML(configuration->toXML());
64 }
65 else {
67 }
68
69 KisTIFFOptions options;
70 options.fromProperties(configuration);
71
72 if (!options.flatten && !options.saveAsPhotoshop) {
73 const bool hasGroupLayers =
74 KisLayerUtils::recursiveFindNode(document->savingImage()->root(),
75 [] (KisNodeSP node) {
76 return node->parent() && node->inherits("KisGroupLayer");
77 });
78 options.flatten = hasGroupLayers;
79 }
80
81 KisImageSP kisimage = [&]() {
82 if (options.flatten) {
83 KisImageSP image =
84 new KisImage(nullptr,
85 document->savingImage()->width(),
86 document->savingImage()->height(),
87 document->savingImage()->colorSpace(),
88 "");
89 image->setResolution(document->savingImage()->xRes(),
90 document->savingImage()->yRes());
92 new KisPaintDevice(*document->savingImage()->projection()));
95 "projection",
97 pd));
98 image->addNode(KisNodeSP(l.data()), image->rootLayer().data());
99 return image;
100 } else {
101 return document->savingImage();
102 }
103 }();
104
105 dbgFile << "Start writing TIFF File";
107
108 QFile file(filename());
109 if (!file.open(QFile::ReadWrite | QFile::Truncate)) {
110 return {KisImportExportErrorCannotRead(file.error())};
111 }
112
113 // Open file for writing
114 const QByteArray encodedFilename = QFile::encodeName(filename());
115
116 // https://gitlab.com/libtiff/libtiff/-/issues/173
117#ifdef Q_OS_WIN
118 const int handle = (int)(_get_osfhandle(file.handle()));
119#else
120 const int handle = file.handle();
121#endif
122
123 // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
124 std::unique_ptr<TIFF, decltype(&TIFFCleanup)> image(TIFFFdOpen(handle, encodedFilename.data(), "w"), &TIFFCleanup);
125
126 if (!image) {
127 dbgFile << "Could not open the file for writing" << filename();
129 }
130
131 // Set the document information
132 KoDocumentInfo *info = document->documentInfo();
133 QString title = info->aboutInfo("title");
134 if (!title.isEmpty()) {
135 if (!TIFFSetField(image.get(),
136 TIFFTAG_DOCUMENTNAME,
137 title.toLatin1().constData())) {
139 }
140 }
141 QString abstract = info->aboutInfo("description");
142 if (!abstract.isEmpty()) {
143 if (!TIFFSetField(image.get(),
144 TIFFTAG_IMAGEDESCRIPTION,
145 abstract.toLatin1().constData())) {
147 }
148 }
149 QString author = info->authorInfo("creator");
150 if (!author.isEmpty()) {
151 if (!TIFFSetField(image.get(),
152 TIFFTAG_ARTIST,
153 author.toLatin1().constData())) {
155 }
156 }
157
158 dbgFile << "xres: " << INCH_TO_POINT(kisimage->xRes())
159 << " yres: " << INCH_TO_POINT(kisimage->yRes());
160 if (!TIFFSetField(
161 image.get(),
162 TIFFTAG_XRESOLUTION,
163 INCH_TO_POINT(kisimage->xRes()))) { // It is the "invert" macro
164 // because we convert from
165 // pointer-per-inch to points
167 }
168 if (!TIFFSetField(image.get(),
169 TIFFTAG_YRESOLUTION,
170 INCH_TO_POINT(kisimage->yRes()))) {
172 }
173
174 if (!TIFFSetField(image.get(), TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
176 }
177
178 KisGroupLayer *root =
179 dynamic_cast<KisGroupLayer *>(kisimage->rootLayer().data());
182
183#ifdef TIFF_CAN_WRITE_PSD_TAGS
184 if (options.saveAsPhotoshop) {
185 KisTiffPsdWriter writer(image.get(), &options);
186 KisImportExportErrorCode result = writer.writeImage(root);
187 if (!result.isOk()) {
188 return result;
189 }
190 } else
191#endif // TIFF_CAN_WRITE_PSD_TAGS
192 {
193 KisTIFFWriterVisitor visitor(image.get(), &options);
194 if (!(visitor.visit(root))) {
196 }
197 }
198
199 image.reset();
200 file.close();
201
202 if (!options.flatten && !options.saveAsPhotoshop) {
203 // HACK!! Externally inject the Exif metadata
204 // libtiff has no way to access the fields wholesale
205 try {
207
208#if EXIV2_TEST_VERSION(0,28,0)
209 const std::unique_ptr<Exiv2::Image> img = Exiv2::ImageFactory::open(std::move(basicIoDevice));
210#else
211 const std::unique_ptr<Exiv2::Image> img(Exiv2::ImageFactory::open(basicIoDevice).release());
212#endif
213
214 img->readMetadata();
215
216 Exiv2::ExifData &data = img->exifData();
217
218 const KisMetaData::IOBackend *io =
220
221 // All IFDs are paint layer children of root
222 KisNodeSP node = root->firstChild();
223
224 QBuffer ioDevice;
225
226 // Get layer
227 KisLayer *layer = qobject_cast<KisLayer *>(node.data());
228 Q_ASSERT(layer);
229
230 // Inject the data as any other IOBackend
231 io->saveTo(layer->metaData(), &ioDevice);
232
233 Exiv2::ExifData dataToInject;
234
235 // Reinterpret the blob we just got and inject its contents into
236 // tempData
237 Exiv2::ExifParser::decode(
238 dataToInject,
239 reinterpret_cast<const Exiv2::byte *>(ioDevice.data().data()),
240 static_cast<uint32_t>(ioDevice.size()));
241
242 for (const auto &v : dataToInject) {
243 data[v.key()] = v.value();
244 }
245 // Write metadata
246 img->writeMetadata();
247#if EXIV2_TEST_VERSION(0,28,0)
248 } catch (Exiv2::Error &e) {
249 errFile << "Failed injecting TIFF metadata:" << Exiv2::Error(e.code()).what();
250#else
251 } catch (Exiv2::AnyError &e) {
252 errFile << "Failed injecting TIFF metadata:" << e.code()
253 << e.what();
254#endif
255 }
256 }
258}
259
260KisPropertiesConfigurationSP KisTIFFExport::defaultConfiguration(const QByteArray &/*from*/, const QByteArray &/*to*/) const
261{
262 KisTIFFOptions options;
263 return options.toProperties();
264}
265
266KisConfigWidget *KisTIFFExport::createConfigurationWidget(QWidget *parent, const QByteArray &/*from*/, const QByteArray &/*to*/) const
267{
268 return new KisTIFFOptionsWidget(parent);
269}
270
272{
278 ->get("TiffExifCheck")
281 KisExportCheckRegistry::instance()->get("ColorModelHomogenousCheck")->create(KisExportCheckBase::SUPPORTED));
282
283 QList<QPair<KoID, KoID>> supportedColorModels = {
284 {},
301 addSupportedColorModels(supportedColorModels, "TIFF");
302
303}
304
305#include <kis_tiff_export.moc>
306
qreal v
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID YCbCrAColorModelID("YCbCrA", ki18n("YCbCr/Alpha"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const quint8 OPACITY_OPAQUE_U8
constexpr qreal INCH_TO_POINT(qreal inch)
Definition KoUnit.h:38
static QByteArray nativeFormatMimeType()
Exiv2::BasicIo::AutoPtr ptr_type
static KisExportCheckRegistry * instance()
KisGroupLayerSP rootLayer() const
double xRes() const
double yRes() const
void setResolution(double xres, double yres)
The base class for import and export filters.
KisPropertiesConfigurationSP lastSavedConfiguration(const QByteArray &from="", const QByteArray &to="") const
lastSavedConfiguration return the last saved configuration for this filter
void addSupportedColorModels(QList< QPair< KoID, KoID > > supportedColorModels, const QString &name, KisExportCheckBase::Level level=KisExportCheckBase::PARTIALLY)
void addCapability(KisExportCheckBase *capability)
virtual bool saveTo(const Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const =0
static KisMetadataBackendRegistry * instance()
KisConfigWidget * createConfigurationWidget(QWidget *parent, const QByteArray &from="", const QByteArray &to="") const override
createConfigurationWidget creates a widget that can be used to define the settings for a given import...
TIFFErrorHandler oldErrHandler
KisTIFFExport(QObject *parent, const QVariantList &)
KisImportExportErrorCode convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration=0) override
~KisTIFFExport() override
TIFFErrorHandler oldWarnHandler
void initializeCapabilities() override
KisPropertiesConfigurationSP defaultConfiguration(const QByteArray &from="", const QByteArray &to="") const override
defaultConfiguration defines the default settings for the given import export filter
bool visit(KisNode *) override
KisImportExportErrorCode writeImage(KisGroupLayerSP rootLayer)
The class containing all meta information about a document.
QString authorInfo(const QString &info) const
QString aboutInfo(const QString &info) const
const T value(const QString &id) const
K_PLUGIN_FACTORY_WITH_JSON(KritaASCCDLFactory, "kritaasccdl.json", registerPlugin< KritaASCCDL >();) KritaASCCDL
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define errFile
Definition kis_debug.h:115
#define dbgFile
Definition kis_debug.h:53
KisSharedPtr< KisPaintLayer > KisPaintLayerSP
void KisTiffWarningHandler(const char *module, const char *fmt, va_list args)
void KisTiffErrorHandler(const char *module, const char *fmt, va_list args)
KisSharedPtr< KisPaintDevice > KisPaintDeviceSP
Definition kis_types.h:73
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
KisNodeSP recursiveFindNode(KisNodeSP node, std::function< bool(KisNodeSP)> func)
KisMetaData::Store * metaData()
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
KisNodeSP firstChild() const
Definition kis_node.cpp:361
void fromProperties(KisPropertiesConfigurationSP cfg)
KisPropertiesConfigurationSP toProperties() const