73#define __STDC_FORMAT_MACROS
91#include <glib/gstdio.h>
102#include <exiv2/exiv2.hpp>
104#if defined(_WIN32) && defined(EXV_UNICODE_PATH)
105 #define WIDEN(s) pugi::as_wide(s)
110#include <pugixml.hpp>
140#define DT_XMP_EXIF_VERSION 5
142#if EXIV2_TEST_VERSION(0,28,0)
143#define AnyError Error
144#define toLong toInt64
157 const char *
t = (
const char *)tag->data;
172 = {
"Xmp.dc.subject",
"Xmp.lr.hierarchicalSubject",
"Xmp.darktable.colorlabels",
"Xmp.darktable.history",
173 "Xmp.darktable.history_modversion",
"Xmp.darktable.history_enabled",
"Xmp.darktable.history_end",
174 "Xmp.darktable.iop_order_version",
"Xmp.darktable.iop_order_list",
175 "Xmp.darktable.history_operation",
"Xmp.darktable.history_params",
"Xmp.darktable.blendop_params",
176 "Xmp.darktable.blendop_version",
"Xmp.darktable.multi_priority",
"Xmp.darktable.multi_name",
177 "Xmp.darktable.iop_order",
178 "Xmp.darktable.xmp_version",
"Xmp.darktable.raw_params",
"Xmp.darktable.auto_presets_applied",
179 "Xmp.darktable.mask_id",
"Xmp.darktable.mask_type",
"Xmp.darktable.mask_name",
180 "Xmp.darktable.masks_history",
"Xmp.darktable.mask_num",
"Xmp.darktable.mask_points",
181 "Xmp.darktable.mask_version",
"Xmp.darktable.mask",
"Xmp.darktable.mask_nb",
"Xmp.darktable.mask_src",
182 "Xmp.darktable.history_basic_hash",
"Xmp.darktable.history_auto_hash",
"Xmp.darktable.history_current_hash",
183 "Xmp.darktable.import_timestamp",
"Xmp.darktable.change_timestamp",
184 "Xmp.darktable.export_timestamp",
"Xmp.darktable.print_timestamp",
185 "Xmp.acdsee.notes",
"Xmp.darktable.version_name",
186 "Xmp.dc.creator",
"Xmp.dc.publisher",
"Xmp.dc.title",
"Xmp.dc.description",
"Xmp.dc.rights",
187 "Xmp.xmpMM.DerivedFrom" };
198 Exiv2::XmpData::iterator pos =
xmp.findKey(Exiv2::XmpKey(
dt_xmp_keys[
i]));
200 while(pos !=
xmp.end())
202 std::string
key = pos->key();
203 const char *
ckey =
key.c_str();
204 size_t len =
key.size();
208 pos =
xmp.erase(pos);
219 Exiv2::XmpData::iterator pos;
220 while((pos =
xmp.findKey(Exiv2::XmpKey(keys[
i]))) !=
xmp.end())
223 catch(
const std::exception &e)
235 static const char *keys[] =
237 "Exif.GPSInfo.GPSLatitude",
238 "Exif.GPSInfo.GPSLongitude",
239 "Exif.GPSInfo.GPSAltitude",
240 "Exif.GPSInfo.GPSLatitudeRef",
241 "Exif.GPSInfo.GPSLongitudeRef",
242 "Exif.GPSInfo.GPSAltitudeRef",
243 "Exif.GPSInfo.GPSVersionID"
255 std::unique_ptr<Exiv2::Image> image(Exiv2::ImageFactory::open(
WIDEN(path)));
256 if(!image.get())
return 1;
258 Exiv2::ExifData &
exifData = image->exifData();
262 Exiv2::ExifData::const_iterator pos;
265 static const char *keys[] = {
266 "Exif.Image.ImageWidth",
267 "Exif.Image.ImageLength",
268 "Exif.Image.BitsPerSample",
269 "Exif.Image.Compression",
270 "Exif.Image.PhotometricInterpretation",
271 "Exif.Image.FillOrder",
272 "Exif.Image.SamplesPerPixel",
273 "Exif.Image.StripOffsets",
274 "Exif.Image.RowsPerStrip",
275 "Exif.Image.StripByteCounts",
276 "Exif.Image.TileWidth",
277 "Exif.Image.TileLength",
278 "Exif.Image.TileOffsets",
279 "Exif.Image.TileByteCounts",
280 "Exif.Image.PlanarConfiguration"
291 static const std::string
needle =
"Exif.SubImage";
299 static const char *keys[] = {
301 "Exif.Canon.ColorSpace",
302 "Exif.Canon.ColorData",
305 "Exif.Nikon3.Preview",
306 "Exif.NikonPreview.JPEGInterchangeFormat",
309 "Exif.Image.CFARepeatPatternDim",
310 "Exif.Image.CFAPattern",
311 "Exif.Image.InterColorProfile",
312 "Exif.Image.SpectralSensitivity",
314 "Exif.Image.SpatialFrequencyResponse",
316 "Exif.Image.SensingMethod",
317 "Exif.Image.TIFFEPStandardID",
318 "Exif.Photo.SpectralSensitivity",
320 "Exif.Photo.SpatialFrequencyResponse",
321 "Exif.Photo.SensingMethod",
322 "Exif.Photo.CFAPattern",
325 "Exif.Image.DNGVersion",
326 "Exif.Image.DNGBackwardVersion",
327 "Exif.Image.DNGPrivateData",
328 "Exif.Image.DefaultBlackRender",
329 "Exif.Image.DefaultCropOrigin",
330 "Exif.Image.DefaultCropSize",
331 "Exif.Image.RawDataUniqueID",
332 "Exif.Image.OriginalRawFileName",
333 "Exif.Image.OriginalRawFileData",
334 "Exif.Image.ActiveArea",
335 "Exif.Image.MaskedAreas",
336 "Exif.Image.AsShotICCProfile",
337 "Exif.Image.OpcodeList1",
338 "Exif.Image.OpcodeList2",
339 "Exif.Image.OpcodeList3",
340 "Exif.Image.AsShotNeutral",
341 "Exif.Image.AsShotWhiteXY",
342 "Exif.Image.BaselineExposure",
343 "Exif.Image.BaselineNoise",
344 "Exif.Image.BaselineSharpness",
345 "Exif.Image.LinearResponseLimit",
346 "Exif.Image.ShadowScale",
347 "Exif.Image.PreviewApplicationName",
348 "Exif.Image.PreviewApplicationVersion",
349 "Exif.Image.PreviewSettingsDigest",
350 "Exif.Image.PreviewColorSpace",
351 "Exif.Image.PreviewDateTime",
352 "Exif.Image.NoiseProfile",
353 "Exif.Image.NewRawImageDigest",
354 "Exif.Image.RawImageDigest",
356 "Exif.Photo.MakerNote",
359 "Exif.Pentax.PreviewResolution",
360 "Exif.Pentax.PreviewLength",
361 "Exif.Pentax.PreviewOffset",
362 "Exif.PentaxDng.PreviewResolution",
363 "Exif.PentaxDng.PreviewLength",
364 "Exif.PentaxDng.PreviewOffset",
366 "Exif.PentaxDng.ColorInfo",
369 "Exif.Minolta.Thumbnail",
370 "Exif.Minolta.ThumbnailOffset",
371 "Exif.Minolta.ThumbnailLength",
374 "Exif.SonyMinolta.ThumbnailOffset",
375 "Exif.SonyMinolta.ThumbnailLength",
378 "Exif.Olympus.Thumbnail",
379 "Exif.Olympus.ThumbnailOffset",
380 "Exif.Olympus.ThumbnailLength",
382 "Exif.Image.BaselineExposureOffset",
386 "Exif.Samsung2.SensorAreas",
387 "Exif.Samsung2.ColorSpace",
388 "Exif.Samsung2.EncryptionKey",
389 "Exif.Samsung2.WB_RGGBLevelsUncorrected",
390 "Exif.Samsung2.WB_RGGBLevelsAuto",
391 "Exif.Samsung2.WB_RGGBLevelsIlluminator1",
392 "Exif.Samsung2.WB_RGGBLevelsIlluminator2",
393 "Exif.Samsung2.WB_RGGBLevelsBlack",
394 "Exif.Samsung2.ColorMatrix",
395 "Exif.Samsung2.ColorMatrixSRGB",
396 "Exif.Samsung2.ColorMatrixAdobeRGB",
397 "Exif.Samsung2.ToneCurve1",
398 "Exif.Samsung2.ToneCurve2",
399 "Exif.Samsung2.ToneCurve3",
400 "Exif.Samsung2.ToneCurve4"
406 static const char *
dngkeys[] = {
408 "Exif.Image.ColorMatrix1",
409 "Exif.Image.ColorMatrix2",
410 "Exif.Image.CameraCalibration1",
411 "Exif.Image.CameraCalibration2",
412 "Exif.Image.ReductionMatrix1",
413 "Exif.Image.ReductionMatrix2",
414 "Exif.Image.AnalogBalance",
415 "Exif.Image.CalibrationIlluminant1",
416 "Exif.Image.CalibrationIlluminant2",
417 "Exif.Image.CameraCalibrationSignature",
418 "Exif.Image.ProfileCalibrationSignature",
419 "Exif.Image.ExtraCameraProfiles",
420 "Exif.Image.AsShotProfileName",
421 "Exif.Image.ProfileName",
422 "Exif.Image.ProfileHueSatMapDims",
423 "Exif.Image.ProfileHueSatMapData1",
424 "Exif.Image.ProfileHueSatMapData2",
425 "Exif.Image.ProfileToneCurve",
426 "Exif.Image.ProfileEmbedPolicy",
427 "Exif.Image.ProfileCopyright",
428 "Exif.Image.ForwardMatrix1",
429 "Exif.Image.ForwardMatrix2",
430 "Exif.Image.ProfileLookTableDims",
431 "Exif.Image.ProfileLookTableData",
432 "Exif.Image.ProfileLookTableEncoding",
433 "Exif.Image.ProfileHueSatMapEncoding"
440 exifData[
"Exif.Photo.ColorSpace"] = uint16_t(1);
442 exifData[
"Exif.Photo.ColorSpace"] = uint16_t(0xFFFF);
454 exifData[
"Exif.Image.XResolution"] = Exiv2::Rational(resolution, 1);
455 exifData[
"Exif.Image.YResolution"] = Exiv2::Rational(resolution, 1);
456 exifData[
"Exif.Image.ResolutionUnit"] = uint16_t(2);
466 static const char * keys[] = {
468 "Exif.Image.ImageDescription",
469 "Exif.Photo.UserComment",
470 "Exif.Image.Copyright",
472 "Exif.Image.RatingPercent",
473 "Exif.Photo.SubSecTimeOriginal",
474 "Exif.GPSInfo.GPSVersionID",
475 "Exif.GPSInfo.GPSLongitudeRef",
476 "Exif.GPSInfo.GPSLatitudeRef",
477 "Exif.GPSInfo.GPSLongitude",
478 "Exif.GPSInfo.GPSLatitude",
479 "Exif.GPSInfo.GPSAltitudeRef",
480 "Exif.GPSInfo.GPSAltitude"
496 char *desc = (
char *)
res->data;
498 exifData[
"Exif.Image.ImageDescription"] = desc;
500 exifData[
"Exif.Photo.UserComment"] = desc;
504#if EXIV2_TEST_VERSION(0,27,4)
508 exifData[
"Exif.Image.ImageDescription"] =
"";
514 exifData[
"Exif.Image.Copyright"] = (
char *)
res->data;
518#if EXIV2_TEST_VERSION(0,27,4)
522 exifData[
"Exif.Image.Copyright"] =
"";
529 exifData[
"Exif.Image.Rating"] = rating;
539 exifData[
"Exif.GPSInfo.GPSVersionID"] =
"02 02 00 00";
540 exifData[
"Exif.GPSInfo.GPSLongitudeRef"] = (
cimg->geoloc.longitude < 0) ?
"W" :
"E";
541 exifData[
"Exif.GPSInfo.GPSLatitudeRef"] = (
cimg->geoloc.latitude < 0) ?
"S" :
"N";
556 exifData[
"Exif.GPSInfo.GPSVersionID"] =
"02 02 00 00";
557 exifData[
"Exif.GPSInfo.GPSAltitudeRef"] = (
cimg->geoloc.elevation < 0) ?
"1" :
"0";
575 exifData[
"Exif.Image.DateTimeOriginal"] = datetime;
576 exifData[
"Exif.Photo.DateTimeOriginal"] = datetime;
584 Exiv2::ExifParser::encode(blob, Exiv2::bigEndian,
exifData);
585 const size_t length = blob.size();
586 *buf = (uint8_t *)
malloc(length);
591 memcpy(*buf, &(blob[0]), length);
594 catch(
const std::exception &e)
597 std::string s(e.what());
598 std::cerr <<
"[exiv2 dt_exif_read_blob] " << path <<
": " << s << std::endl;
646 std::cout <<
"malformed entry" << std::endl;
650 std::cout << entry->
operation << std::endl;
651 std::cout <<
" modversion :" << entry->
modversion << std::endl;
652 std::cout <<
" enabled :" << entry->
enabled << std::endl;
653 std::cout <<
" params :" << (entry->
params ?
"<found>" :
"<missing>") << std::endl;
654 std::cout <<
" multi_name :" << (entry->
multi_name ? entry->
multi_name :
"<missing>") << std::endl;
655 std::cout <<
" multi_priority :" << entry->
multi_priority << std::endl;
656 std::cout <<
" iop_order :" << entry->
iop_order << std::endl;
657 std::cout <<
" blendop_version :" << entry->
blendop_version << std::endl;
658 std::cout <<
" blendop_params :" << (entry->
blendop_params ?
"<found>" :
"<missing>") << std::endl;
659 std::cout << std::endl;
679 pugi::xml_document
doc;
680#if defined(PUGIXML_VERSION) && PUGIXML_VERSION >= 150
681 pugi::xml_parse_result result =
doc.load_string(
xmpPacket.c_str());
683 pugi::xml_parse_result result =
doc.load(
xmpPacket.c_str());
688 std::cerr <<
"XML '" << filename <<
"' parsed with errors" << std::endl;
689 std::cerr <<
"Error description: " << result.description() << std::endl;
690 std::cerr <<
"Error offset: " << result.offset << std::endl;
696#if defined(PUGIXML_VERSION) && PUGIXML_VERSION >= 150
697 pugi::xpath_node modversion =
superold ?
698 doc.select_node(
"//darktable:history_modversion/rdf:Bag"):
699 doc.select_node(
"//darktable:history_modversion/rdf:Seq");
700 pugi::xpath_node enabled =
superold ?
701 doc.select_node(
"//darktable:history_enabled/rdf:Bag"):
702 doc.select_node(
"//darktable:history_enabled/rdf:Seq");
703 pugi::xpath_node operation =
superold ?
704 doc.select_node(
"//darktable:history_operation/rdf:Bag"):
705 doc.select_node(
"//darktable:history_operation/rdf:Seq");
706 pugi::xpath_node params =
superold ?
707 doc.select_node(
"//darktable:history_params/rdf:Bag"):
708 doc.select_node(
"//darktable:history_params/rdf:Seq");
709 pugi::xpath_node blendop_params =
superold ?
710 doc.select_node(
"//darktable:blendop_params/rdf:Bag"):
711 doc.select_node(
"//darktable:blendop_params/rdf:Seq");
712 pugi::xpath_node blendop_version =
superold ?
713 doc.select_node(
"//darktable:blendop_version/rdf:Bag"):
714 doc.select_node(
"//darktable:blendop_version/rdf:Seq");
715 pugi::xpath_node multi_priority =
superold ?
716 doc.select_node(
"//darktable:multi_priority/rdf:Bag"):
717 doc.select_node(
"//darktable:multi_priority/rdf:Seq");
718 pugi::xpath_node multi_name =
superold ?
719 doc.select_node(
"//darktable:multi_name/rdf:Bag"):
720 doc.select_node(
"//darktable:multi_name/rdf:Seq");
722 pugi::xpath_node modversion =
superold ?
723 doc.select_single_node(
"//darktable:history_modversion/rdf:Bag"):
724 doc.select_single_node(
"//darktable:history_modversion/rdf:Seq");
725 pugi::xpath_node enabled =
superold ?
726 doc.select_single_node(
"//darktable:history_enabled/rdf:Bag"):
727 doc.select_single_node(
"//darktable:history_enabled/rdf:Seq");
728 pugi::xpath_node operation =
superold ?
729 doc.select_single_node(
"//darktable:history_operation/rdf:Bag"):
730 doc.select_single_node(
"//darktable:history_operation/rdf:Seq");
731 pugi::xpath_node params =
superold ?
732 doc.select_single_node(
"//darktable:history_params/rdf:Bag"):
733 doc.select_single_node(
"//darktable:history_params/rdf:Seq");
734 pugi::xpath_node blendop_params =
superold ?
735 doc.select_single_node(
"//darktable:blendop_params/rdf:Bag"):
736 doc.select_single_node(
"//darktable:blendop_params/rdf:Seq");
737 pugi::xpath_node blendop_version =
superold ?
738 doc.select_single_node(
"//darktable:blendop_version/rdf:Bag"):
739 doc.select_single_node(
"//darktable:blendop_version/rdf:Seq");
740 pugi::xpath_node multi_priority =
superold ?
741 doc.select_single_node(
"//darktable:multi_priority/rdf:Bag"):
742 doc.select_single_node(
"//darktable:multi_priority/rdf:Seq");
743 pugi::xpath_node multi_name =
superold ?
744 doc.select_single_node(
"//darktable:multi_name/rdf:Bag"):
745 doc.select_single_node(
"//darktable:multi_name/rdf:Seq");
752 auto params_iter = params.node().children().begin();
774 if(multi_name &&
multi_name_iter != multi_name.node().children().end())
815 for(
auto history = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history")); history != xmpData.end(); history++)
821 std::string
key_item = history->key();
831 std::cerr <<
"error reading history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
841 std::cerr <<
"error reading history from '"
842 <<
key <<
"' (" << filename <<
")" << std::endl;
916 static const std::locale&
c_locale = std::locale(
"C");
944 std::cerr <<
"[exif] error: reading history from '" << filename <<
"' failed due to missing tags" << std::endl;
968 Exiv2::XmpData::iterator mask;
970 Exiv2::XmpData::iterator mask_type;
971 Exiv2::XmpData::iterator mask_version;
972 Exiv2::XmpData::iterator
mask_id;
973 Exiv2::XmpData::iterator mask_nb;
974 Exiv2::XmpData::iterator mask_src;
975 if((mask = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask"))) != xmpData.end()
976 && (mask_src = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_src"))) != xmpData.end()
977 && (
mask_name = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_name"))) != xmpData.end()
978 && (mask_type = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_type"))) != xmpData.end()
979 && (mask_version = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_version"))) != xmpData.end()
980 && (
mask_id = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_id"))) != xmpData.end()
981 && (mask_nb = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_nb"))) != xmpData.end())
984 const size_t cnt = (size_t)mask->count();
990 const size_t mask_nb_cnt = (size_t)mask_nb->count();
994 for(
size_t i = 0;
i <
cnt;
i++)
1009 std::string
mask_str = mask->toString(
i);
1014 entry->
mask_nb = mask_nb->toLong(
i);
1034 for(
auto history = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.masks_history")); history != xmpData.end(); history++)
1040 std::string
key_item = history->key();
1050 std::cerr <<
"error reading masks history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
1060 std::cerr <<
"error reading masks history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
1100 std::string
value_item = history->toString();
1109 std::string
value_item = history->toString();
1120 std::string
value_item = history->toString();
1140 const int mask_num = 0;
1165 int32_t imgid = *(
int *)user_data;
1185 fprintf(
stderr,
"[masks] error loading masks from xmp file, bad binary blob size.\n");
1230 std::unique_ptr<Exiv2::Image> image(Exiv2::ImageFactory::open(
WIDEN(filename)));
1231 if(!image.get())
return 1;
1233 Exiv2::XmpData &xmpData = image->xmpData();
1235 Exiv2::XmpData::iterator pos;
1242 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.xmp_version"))) != xmpData.end())
1248 const size_t ns_pos = image->xmpPacket().find(
"xmlns:darktable=\"http://darktable.sf.net/\"");
1255 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.raw_params"))) != xmpData.end())
1269 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.auto_presets_applied"))) != xmpData.end())
1276 else if(xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.xmp_version")) == xmpData.end())
1292 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_version"))) != xmpData.end())
1297 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_list"))) != xmpData.end())
1311 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_version"))) != xmpData.end())
1367 std::string &
xmpPacket = image->xmpPacket();
1376 std::cerr <<
"error: Xmp schema version " <<
xmp_version <<
" in " << filename <<
" not supported" << std::endl;
1385 fprintf(
stderr,
"[exif] error deleting history for image %d\n", img->
id);
1413 fprintf(
stderr,
"[exif] error adding history entry for image %d\n", img->
id);
1449 "[exif] cannot get iop-order for module '%s', XMP may be corrupted\n",
1452 iop_order_list =
NULL;
1492 fprintf(
stderr,
"[exif] error shifting history nums for image %d\n", img->
id);
1496 if(!
dt_history_repository_write_item(img->
id, 0,
"mask_manager",
NULL, 0, 1, 0,
NULL, 0, 0, 0,
""))
1498 fprintf(
stderr,
"[exif] error adding mask history entry for image %d\n", img->
id);
1509 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history_end"))) != xmpData.end() && num > 0)
1511 int history_end =
MIN(pos->toLong(), num);
1516 fprintf(
stderr,
"[exif] error writing history_end for image %d\n", img->
id);
1528 fprintf(
stderr,
"[exif] error writing history_end for image %d\n", img->
id);
1536 fprintf(
stderr,
"[exif] error writing iop_list for image %d\n", img->
id);
1559 fprintf(
stderr,
"[exif] dt_exif_xmp_read for %s, id %i found auto_presets_applied but there was no history\n",filename,img->
id);
1564 iop_order_list =
NULL;
1576 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history_current_hash"))) != xmpData.end())
1582 const std::string
hash_str = pos->toString();
1595 std::cerr <<
"[exif] error reading history from '" << filename <<
"'" << std::endl;
1601 catch(
const std::exception &e)
1619 const int mask_type,
const char *
mask_name,
const int mask_version,
1620 const void *points,
const int points_len,
const int mask_nb,
1626 const int num =
ctx->num;
1631 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_num", num);
1632 xmpData[
key] = mask_num;
1633 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_id", num);
1635 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_type", num);
1636 xmpData[
key] = mask_type;
1637 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_name", num);
1639 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_version", num);
1640 xmpData[
key] = mask_version;
1641 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_points", num);
1643 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_nb", num);
1644 xmpData[
key] = mask_nb;
1645 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_src", num);
1646 xmpData[
key] = mask_src;
1661 const int modversion,
const char *operation,
1662 const void *op_params,
const int params_len,
1664 const int blendop_params_len,
const int blendop_version,
1665 const int multi_priority,
const char *multi_name,
1666 const char *preset_name)
1671 const int num =
ctx->num;
1677 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:num", num);
1679 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:operation", num);
1680 xmpData[
key] = operation;
1681 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:enabled", num);
1682 xmpData[
key] = enabled;
1683 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:modversion", num);
1684 xmpData[
key] = modversion;
1685 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:params", num);
1686 xmpData[
key] = params;
1687 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:multi_name", num);
1688 xmpData[
key] = multi_name ? multi_name :
"";
1689 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:multi_priority", num);
1690 xmpData[
key] = multi_priority;
1697 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:blendop_version", num);
1698 xmpData[
key] = blendop_version;
1699 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:blendop_params", num);
1700 xmpData[
key] = blendop_params;
1715 Exiv2::XmpTextValue
tvm(
"");
1716 tvm.setXmpArrayType(Exiv2::XmpValue::xaSeq);
1717 xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.masks_history"), &
tvm);
1726 Exiv2::XmpTextValue
tv(
"");
1727 tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
1728 xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.history"), &
tv);
1732 if(history_end == -1) history_end = num - 1;
1733 else history_end =
MIN(history_end, num - 1);
1734 xmpData[
"Xmp.darktable.history_end"] = history_end;
1740 static const char *keys[] =
1742 "Xmp.darktable.import_timestamp",
1743 "Xmp.darktable.change_timestamp",
1744 "Xmp.darktable.export_timestamp",
1745 "Xmp.darktable.print_timestamp"
1775 Exiv2::XmpData::iterator pos;
1778 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.change_timestamp"))) != xmpData.end())
1782 else if(pos->toLong() >= 1)
1785 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.export_timestamp"))) != xmpData.end())
1789 else if(pos->toLong() >= 1)
1792 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.print_timestamp"))) != xmpData.end())
1796 else if(pos->toLong() >= 1)
1803 static const char *keys[] =
1805 "Xmp.exif.GPSVersionID",
1806 "Xmp.exif.GPSLongitude",
1807 "Xmp.exif.GPSLatitude",
1808 "Xmp.exif.GPSAltitudeRef",
1809 "Xmp.exif.GPSAltitude"
1822 if(latitude < 0)
lat_dir =
'S';
1824 longitude =
fabs(longitude);
1825 latitude =
fabs(latitude);
1839 xmpData[
"Xmp.exif.GPSVersionID"] =
"2.2.0.0";
1840 xmpData[
"Xmp.exif.GPSLongitude"] =
long_str;
1841 xmpData[
"Xmp.exif.GPSLatitude"] =
lat_str;
1846 if(!
isnan(altitude))
1848 xmpData[
"Xmp.exif.GPSAltitudeRef"] = (altitude < 0) ?
"1" :
"0";
1852 xmpData[
"Xmp.exif.GPSAltitude"] =
ele_str;
1883 Exiv2::Value *
v = (Exiv2::Value *)user_data;
1896 std::unique_ptr<Exiv2::Value>
v(Exiv2::Value::create(Exiv2::xmpSeq));
1900 if(
v->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.colorlabels"),
v.get());
1919 xmpData[
"Xmp.darktable.history_current_hash"] =
value;
1931 int stars = 1,
raw_params = 0, history_end = -1;
1932 double longitude =
NAN, latitude =
NAN, altitude =
NAN;
1933 gchar *filename =
NULL;
1934 gchar *iop_order_list =
NULL;
1944 if(
row.has_longitude) longitude =
row.longitude;
1945 if(
row.has_latitude) latitude =
row.latitude;
1946 if(
row.has_altitude) altitude =
row.altitude;
1947 history_end =
row.history_end;
1948 gts =
row.datetime_taken;
1974 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.xmp.Rating"));
1975 if(pos != xmpData.end()) xmpData.erase(pos);
1979 if(filename) xmpData[
"Xmp.xmpMM.DerivedFrom"] = filename;
1991 std::unique_ptr<Exiv2::Value>
v1(Exiv2::Value::create(Exiv2::xmpBag));
1993 std::unique_ptr<Exiv2::Value>
v2(Exiv2::Value::create(Exiv2::xmpBag));
2000 v1->read((
char *)tag->data);
2008 if(
v1->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.dc.subject"),
v1.get());
2017 v2->read((
char *)
hier->data);
2025 if(
v2->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.lr.hierarchicalSubject"),
v2.get());
2030 xmpData[
"Xmp.darktable.xmp_version"] =
xmp_version;
2031 xmpData[
"Xmp.darktable.raw_params"] =
raw_params;
2033 xmpData[
"Xmp.darktable.auto_presets_applied"] = 1;
2035 xmpData[
"Xmp.darktable.auto_presets_applied"] = 0;
2040 xmpData[
"Xmp.darktable.iop_order_version"] = iop_order_version;
2041 if(iop_order_list) xmpData[
"Xmp.darktable.iop_order_list"] = iop_order_list;
2054 int stars = 1,
raw_params = 0, history_end = -1;
2055 double longitude =
NAN, latitude =
NAN, altitude =
NAN;
2056 gchar *filename =
NULL;
2058 gchar *iop_order_list =
NULL;
2067 if(
row.has_longitude) longitude =
row.longitude;
2068 if(
row.has_latitude) latitude =
row.latitude;
2069 if(
row.has_altitude) altitude =
row.altitude;
2070 history_end =
row.history_end;
2071 gts =
row.datetime_taken;
2101 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.xmp.Rating"));
2102 if(pos != xmpData.end()) xmpData.erase(pos);
2106 if(filename) xmpData[
"Xmp.xmpMM.DerivedFrom"] = filename;
2124 std::unique_ptr<Exiv2::Value>
v1(Exiv2::Value::create(Exiv2::xmpBag));
2130 v1->read((
char *)tag->data);
2138 if(
v1->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.dc.subject"),
v1.get());
2145 std::unique_ptr<Exiv2::Value>
v2(Exiv2::Value::create(Exiv2::xmpBag));
2151 v2->read((
char *)
hier->data);
2159 if(
v2->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.lr.hierarchicalSubject"),
v2.get());
2166 xmpData[
"Xmp.darktable.xmp_version"] =
xmp_version;
2167 xmpData[
"Xmp.darktable.raw_params"] =
raw_params;
2169 xmpData[
"Xmp.darktable.auto_presets_applied"] = 1;
2171 xmpData[
"Xmp.darktable.auto_presets_applied"] = 0;
2176 xmpData[
"Xmp.darktable.iop_order_version"] = iop_order_version;
2177 if(iop_order_list) xmpData[
"Xmp.darktable.iop_order_list"] = iop_order_list;
2186#if EXIV2_TEST_VERSION(0,27,0)
2187#define ERROR_CODE(a) (static_cast<Exiv2::ErrorCode>((a)))
2189#define ERROR_CODE(a) (a)
2205 Exiv2::XmpData xmpData;
2211#if EXIV2_TEST_VERSION(0,28,0)
2212 xmpPacket.assign(buf.c_str(), buf.size());
2214 xmpPacket.assign(
reinterpret_cast<char *
>(buf.pData_), buf.size_);
2216 Exiv2::XmpParser::decode(xmpData,
xmpPacket);
2231#if EXIV2_TEST_VERSION(0,28,0)
2232 xmpPacket.assign(buf.c_str(), buf.size());
2234 xmpPacket.assign(
reinterpret_cast<char *
>(buf.pData_), buf.size_);
2250 if(Exiv2::XmpParser::encode(
xmpPacket, xmpData,
2251 Exiv2::XmpParser::useCompactFormat | Exiv2::XmpParser::omitPacketWrapper) != 0)
2253 throw Exiv2::Error(
ERROR_CODE(1),
"[xmp_write] failed to serialize xmp data");
2257 catch(
const std::exception &e)
2259 std::cerr <<
"[xmp_read_blob] caught exiv2 exception '" << e.what() <<
"'\n";
2268 Exiv2::XmpData::iterator pos =
xmp.findKey(Exiv2::XmpKey(
key));
2269 if (pos !=
xmp.end())
2272 catch(
const std::exception &e)
2282 for(Exiv2::XmpData::iterator
i = xmpData.begin();
i != xmpData.end();)
2285 i = xmpData.erase(
i);
2290 catch(
const std::exception &e)
2299 Exiv2::ExifData::iterator pos = exif.findKey(Exiv2::ExifKey(
key));
2300 if (pos != exif.end())
2303 catch(
const std::exception &e)
2312 Exiv2::IptcData::iterator pos;
2313 while((pos =
iptc.findKey(Exiv2::IptcKey(
key))) !=
iptc.end())
2316 catch(
const std::exception &e)
2337 std::unique_ptr<Exiv2::Image> img(Exiv2::ImageFactory::open(
WIDEN(filename)));
2354 catch(
const std::exception &e)
2356 std::cerr <<
"[xmp_attach] " <<
input_filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
2359 Exiv2::XmpData &xmpData = img->xmpData();
2370#if EXIV2_TEST_VERSION(0,28,0)
2371 xmpPacket.assign(buf.c_str(), buf.size());
2373 xmpPacket.assign(
reinterpret_cast<char *
>(buf.pData_), buf.size_);
2386 static const char *keys[] = {
2387 "Xmp.tiff.Orientation"
2399 Exiv2::ExifData &
exifData = img->exifData();
2406 img->clearExifData();
2411 Exiv2::IptcData &
iptcData = img->iptcData();
2419 params->jobcode =
"infos";
2420 params->sequence = 0;
2421 params->imgid = imgid;
2426 gchar *tagname = (gchar *)tags->data;
2429 gchar *
formula = (gchar *)tags->data;
2435 Exiv2::ExifData::const_iterator pos;
2444 if(result && result[0])
2458 xmpData[tagname] =
tuple;
2462 xmpData[tagname] = result;
2473 Exiv2::IptcKey
key(tagname);
2474 Exiv2::Iptcdatum id(
key);
2475 gchar **values =
g_strsplit(result,
", ", 0);
2478 gchar **entry = values;
2536 img->writeMetadata();
2538 catch(Exiv2::AnyError &e)
2540#if EXIV2_TEST_VERSION(0,27,0)
2541 if(e.code() == Exiv2::ErrorCode::kerTooLargeJpegSegment)
2552 img->writeMetadata();
2554 catch(
const std::exception &
e2)
2556 std::cerr <<
"[dt_exif_xmp_attach_export] without history " << filename <<
": caught exiv2 exception '" <<
e2.what() <<
"'\n";
2563 catch(
const std::exception &e)
2565 std::cerr <<
"[dt_exif_xmp_attach_export] " << filename <<
": caught exception '" << e.what() <<
"'\n";
2570 catch(
const std::exception &e)
2572 std::cerr <<
"[dt_exif_xmp_attach_export] " << filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
2585 const int32_t imgid = image->
id;
2596 Exiv2::XmpData xmpData;
2606 unsigned char *content = (
unsigned char*)
dt_read_file(filename, &end);
2612 _(
"The XMP file \n'%s'\n weighs %.2f MB. Writing it will take some time."),
2613 filename, (
float)end / 1000000);
2614 fprintf(
stdout,
"The XMP file '%s' weighs %.2f MB. Writing it will take some time.\n", filename, (
float)end / 1000000);
2627 Exiv2::DataBuf buf = Exiv2::readFile(
WIDEN(filename));
2628#if EXIV2_TEST_VERSION(0,28,0)
2629 xmpPacket.assign(buf.c_str(), buf.size());
2631 xmpPacket.assign(
reinterpret_cast<char *
>(buf.pData_), buf.size_);
2633 Exiv2::XmpParser::decode(xmpData,
xmpPacket);
2643 if(Exiv2::XmpParser::encode(
xmpPacket, xmpData,
2644 Exiv2::XmpParser::useCompactFormat | Exiv2::XmpParser::omitPacketWrapper) != 0)
2646 throw Exiv2::Error(
ERROR_CODE(1),
"[xmp_write] failed to serialize xmp data");
2650 const char *
xml_header =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2688 catch(
const std::exception &e)
2690 std::cerr <<
"[dt_exif_xmp_write] " << filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
RAII guard over the process-wide exiv2 lock.
void dt_colorlabel_repository_foreach(const int32_t imgid, void(*cb)(void *, const int), void *user_data)
Every colour label of imgid, one call per row, in the order the rows come back.
main.color_labels: which of the five colour labels are set on an image.
static dt_aligned_pixel_t sRGB
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_conf_get_int(const char *name)
void dt_image_path_append_version(const int32_t imgid, char *pathname, size_t pathname_len)
int dt_image_get_xmp_rating_from_flags(const int flags)
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
const char darktable_package_string[]
const char * dt_database_get_last_error(void)
void dt_database_rollback_transaction(void)
#define dt_database_start_transaction()
#define dt_database_release_transaction()
gboolean dt_datetime_img_to_exif(char *exif, const size_t exif_size, const dt_image_t *img)
void dt_datetime_now_to_exif(char *exif)
gboolean dt_datetime_gtimespan_to_exif(char *sdt, const size_t sdt_size, const GTimeSpan gts)
GTimeSpan dt_datetime_gdatetime_to_gtimespan(GDateTime *gdt)
#define DT_DATETIME_LENGTH
#define DT_DATETIME_EXIF_LENGTH
int dt_deprecated(const char *op)
Modules without a proper IOP order should throw errors and log, except if they are deprecated definit...
bool dt_exif_read_exif_tag(Exiv2::ExifData &exifData, Exiv2::ExifData::const_iterator *pos, string key)
Read one EXIF tag into pos, FALSE if the image does not carry it.
unsigned char * dt_exif_xmp_decode(const char *input, const int len, int *output_len)
bool dt_exif_decode_xmp_data(dt_image_t *img, Exiv2::XmpData &xmpData, int version, bool exif_read)
Apply one XMP packet's metadata, tags and colour labels to img.
char * dt_exif_xmp_encode(const unsigned char *input, const int len, int *output_len)
void dt_remove_exif_keys(Exiv2::ExifData &exif, const char *keys[], unsigned int n_keys)
Strip the given EXIF keys from exif, ignoring any that are absent.
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
The seam between metadata/exif.cc and common/xmp_sidecar.cc.
#define read_metadata_threadsafe(image)
void dt_history_repository_foreach_mask_item(const int32_t imgid, dt_history_repository_mask_cb cb, void *user_data)
gboolean dt_history_repository_shift_nums(const int32_t imgid, const int delta)
gboolean dt_history_repository_write_item(const int32_t imgid, const int num, const char *operation, const void *op_params, const int op_params_size, const int module_version, const gboolean enabled, const void *blendop_params, const int blendop_params_size, const int blendop_version, const int multi_priority, const char *multi_name)
gboolean dt_history_repository_write_mask_item(const int32_t imgid, const int num, const int formid, const int form, const char *name, const int version, const void *points, const int points_len, const int points_count, const void *source, const int source_len)
int dt_history_repository_count_mask_items(const int32_t imgid)
gboolean dt_history_repository_delete_masks_history(const int32_t imgid)
gboolean dt_history_repository_delete_history(const int32_t imgid)
gboolean dt_history_repository_set_end(const int32_t imgid, const int32_t history_end)
int32_t dt_history_repository_get_next_num(const int32_t imgid)
void dt_history_repository_foreach_row(const int32_t imgid, dt_history_repository_row_cb cb, void *user_data)
@ DT_IMAGE_NO_LEGACY_PRESETS
@ DT_IMAGE_AUTO_PRESETS_APPLIED
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
void dt_image_repository_xmp_row_cleanup(dt_image_xmp_row_t *row)
Release what dt_image_repository_get_xmp_row() allocated.
gboolean dt_image_repository_get_timestamps(const int32_t imgid, dt_image_timestamps_t *ts)
Read imgid's four timestamps. FALSE when there is no such image.
gboolean dt_image_repository_get_xmp_row(const int32_t imgid, dt_image_xmp_row_t *row)
Read imgid's sidecar fields. FALSE when there is no such image.
Reading and writing one dt_image_t to and from the library database. The SQL half of what used to be ...
GList * dt_ioppr_get_iop_order_list(int32_t imgid, gboolean sorted)
Load the order list for an image from the DB.
gboolean dt_ioppr_has_multiple_instances(GList *iop_order_list)
Detect whether multiple instances are grouped for a non-custom order.
GList * dt_ioppr_get_iop_order_list_version(dt_iop_order_t version)
Return the built-in order list for a given version.
gint dt_sort_iop_list_by_order_f(gconstpointer a, gconstpointer b)
Compare two list nodes holding modules by iop_order.
gboolean dt_ioppr_write_iop_order_list(GList *iop_order_list, const int32_t imgid)
Persist an order list to the DB for a given image.
char * dt_ioppr_serialize_text_iop_order_list(GList *iop_order_list)
Serialize an order list to a text representation.
GList * dt_ioppr_deserialize_text_iop_order_list(const char *buf)
Deserialize an order list from a text representation.
GList * dt_ioppr_insert_missing_modules(GList *iop_order_list)
Insert modules that postdate a deserialized/custom order list.
dt_iop_order_t dt_ioppr_get_iop_order_version(const int32_t imgid)
Fetch the IOP order version stored for an image.
GList * dt_ioppr_get_iop_order_link(GList *iop_order_list, const char *op_name, const int multi_priority)
Find a list link matching an operation and instance.
_lib_location_type_t type
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void dt_free_gpointer(gpointer ptr)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
static const dt_aligned_pixel_simd_t value
char * dt_variables_expand(dt_variables_params_t *params, gchar *source, gboolean iterate)
void dt_variables_params_destroy(dt_variables_params_t *params)
void dt_variables_params_init(dt_variables_params_t **params)
void dt_variables_set_tags_flags(dt_variables_params_t *params, uint32_t flags)
unsigned __int64 uint64_t
GTimeSpan export_timestamp
GTimeSpan change_timestamp
GTimeSpan print_timestamp
dt_image_raw_parameters_t legacy_flip
The four timestamps, each with whether it is set at all.
The main.images fields an XMP sidecar carries.
union dt_iop_order_entry_t::@26 o
unsigned char * blendop_params
unsigned char * mask_points
typedef double((*spd)(unsigned long int wavelength, double TempK))
static const char * mask_name
char * dt_read_file(const char *const filename, size_t *filesize)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
static const char * _exif_get_exiv2_tag_type(const char *tagname)
int dt_exif_xmp_attach_export(const int32_t imgid, const char *filename, void *metadata)
static void dt_remove_xmp_key(Exiv2::XmpData &xmp, const char *key)
static void _xmp_append_metadata(void *user_data, const int keyid, const char *value)
static void dt_remove_known_keys(Exiv2::XmpData &xmp)
static void free_history_entry(gpointer data)
static void read_xmp_timestamps(Exiv2::XmpData &xmpData, dt_image_t *img, const int xmp_version)
static void dt_set_xmp_exif_geotag(Exiv2::XmpData &xmpData, double longitude, double latitude, double altitude)
static void dt_set_xmp_dt_history(Exiv2::XmpData &xmpData, const int32_t imgid, int history_end)
static void dt_remove_xmp_keys(Exiv2::XmpData &xmp, const char *keys[], unsigned int n_keys)
static void _xmp_append_colorlabel(void *user_data, const int color)
static GList * read_masks_v3(Exiv2::XmpData &xmpData, const char *filename, const int version)
int dt_exif_xmp_write_with_imgpath(const dt_image_t *image, const char *filename, const char *imgpath)
GTimeSpan _convert_unix_to_gtimespan(const time_t unix)
static GList * read_history_v1(const std::string &xmpPacket, const char *filename, const int superold)
static void print_history_entry(history_entry_t *entry) __attribute__((unused))
int dt_exif_xmp_read(dt_image_t *img, const char *filename, const int history_only)
static void _xmp_append_history(void *user_data, const int32_t row_imgid, const int hist_num, const int modversion, const char *operation, const void *op_params, const int params_len, const gboolean enabled, const void *blendop_params_blob, const int blendop_params_len, const int blendop_version, const int multi_priority, const char *multi_name, const char *preset_name)
static void dt_remove_xmp_exif_geotag(Exiv2::XmpData &xmpData)
static void add_mask_entry_to_db(int32_t imgid, mask_entry_t *entry)
#define DT_XMP_EXIF_VERSION
static void _remove_xmp_keys(Exiv2::XmpData &xmpData, const char *key)
const char * dt_xmp_keys[]
int dt_exif_read_blob(uint8_t **buf, const char *path, const int32_t imgid, const int sRGB, const int out_width, const int out_height, const int dng_mode)
static void dt_remove_exif_key(Exiv2::ExifData &exif, const char *key)
char * dt_exif_xmp_read_string(const int32_t imgid)
static void _exif_xmp_read_data_export(Exiv2::XmpData &xmpData, const int32_t imgid, dt_export_metadata_t *metadata)
int _get_max_multi_priority(GList *history, const char *operation)
static void _exif_xmp_append_history_hash(Exiv2::XmpData &xmpData, const int32_t imgid, const dt_image_t *image)
static void dt_remove_iptc_key(Exiv2::IptcData &iptc, const char *key)
static GList * read_history_v2(Exiv2::XmpData &xmpData, const char *filename)
static GHashTable * read_masks(Exiv2::XmpData &xmpData, const char *filename, const int version)
static void add_non_clone_mask_entries_to_db(gpointer key, gpointer value, gpointer user_data)
static void _exif_xmp_read_data(Exiv2::XmpData &xmpData, const int32_t imgid, const dt_image_t *image)
void free_mask_entry(gpointer data)
static const guint dt_xmp_keys_n
static void _xmp_append_mask(void *user_data, const int mask_num, const int mask_id, const int mask_type, const char *mask_name, const int mask_version, const void *points, const int points_len, const int mask_nb, const void *source, const int source_len)
static void set_xmp_timestamps(Exiv2::XmpData &xmpData, const int32_t imgid)
static void dt_set_xmp_dt_metadata(Exiv2::XmpData &xmpData, const int32_t imgid, const gboolean export_flag)
static void add_mask_entries_to_db(int32_t imgid, GHashTable *mask_entries, int mask_id)
static void dt_remove_exif_geotag(Exiv2::ExifData &exifData)
The XMP document that carries an image's development: history stack, mask shapes, module order,...