73#define __STDC_FORMAT_MACROS
92#include <glib/gstdio.h>
103#include <exiv2/exiv2.hpp>
105#include <pugixml.hpp>
135#define DT_XMP_EXIF_VERSION 5
137#if EXIV2_TEST_VERSION(0,28,0)
138#define AnyError Error
139#define toLong toInt64
152 const char *
t = (
const char *)tag->data;
167 = {
"Xmp.dc.subject",
"Xmp.lr.hierarchicalSubject",
"Xmp.darktable.colorlabels",
"Xmp.darktable.history",
168 "Xmp.darktable.history_modversion",
"Xmp.darktable.history_enabled",
"Xmp.darktable.history_end",
169 "Xmp.darktable.iop_order_version",
"Xmp.darktable.iop_order_list",
170 "Xmp.darktable.history_operation",
"Xmp.darktable.history_params",
"Xmp.darktable.blendop_params",
171 "Xmp.darktable.blendop_version",
"Xmp.darktable.multi_priority",
"Xmp.darktable.multi_name",
172 "Xmp.darktable.iop_order",
173 "Xmp.darktable.xmp_version",
"Xmp.darktable.raw_params",
"Xmp.darktable.auto_presets_applied",
174 "Xmp.darktable.mask_id",
"Xmp.darktable.mask_type",
"Xmp.darktable.mask_name",
175 "Xmp.darktable.masks_history",
"Xmp.darktable.mask_num",
"Xmp.darktable.mask_points",
176 "Xmp.darktable.mask_version",
"Xmp.darktable.mask",
"Xmp.darktable.mask_nb",
"Xmp.darktable.mask_src",
177 "Xmp.darktable.history_basic_hash",
"Xmp.darktable.history_auto_hash",
"Xmp.darktable.history_current_hash",
178 "Xmp.darktable.import_timestamp",
"Xmp.darktable.change_timestamp",
179 "Xmp.darktable.export_timestamp",
"Xmp.darktable.print_timestamp",
180 "Xmp.acdsee.notes",
"Xmp.darktable.version_name",
181 "Xmp.dc.creator",
"Xmp.dc.publisher",
"Xmp.dc.title",
"Xmp.dc.description",
"Xmp.dc.rights",
182 "Xmp.xmpMM.DerivedFrom" };
207 packet.assign(content, length);
219 Exiv2::XmpData::iterator pos = xmp.findKey(Exiv2::XmpKey(
dt_xmp_keys[
i]));
221 while(pos != xmp.end())
223 std::string
key = pos->key();
224 const char *
ckey =
key.c_str();
225 size_t len =
key.size();
229 pos = xmp.erase(pos);
240 Exiv2::XmpData::iterator pos;
241 while((pos = xmp.findKey(Exiv2::XmpKey(keys[
i]))) != xmp.end())
244 catch(
const std::exception &e)
256 static const char *keys[] =
258 "Exif.GPSInfo.GPSLatitude",
259 "Exif.GPSInfo.GPSLongitude",
260 "Exif.GPSInfo.GPSAltitude",
261 "Exif.GPSInfo.GPSLatitudeRef",
262 "Exif.GPSInfo.GPSLongitudeRef",
263 "Exif.GPSInfo.GPSAltitudeRef",
264 "Exif.GPSInfo.GPSVersionID"
276 std::unique_ptr<Exiv2::Image> image(Exiv2::ImageFactory::open(
WIDEN(path)));
277 if(!image.get())
return 1;
278 image->readMetadata();
279 Exiv2::ExifData &
exifData = image->exifData();
283 Exiv2::ExifData::const_iterator pos;
286 static const char *keys[] = {
287 "Exif.Image.ImageWidth",
288 "Exif.Image.ImageLength",
289 "Exif.Image.BitsPerSample",
290 "Exif.Image.Compression",
291 "Exif.Image.PhotometricInterpretation",
292 "Exif.Image.FillOrder",
293 "Exif.Image.SamplesPerPixel",
294 "Exif.Image.StripOffsets",
295 "Exif.Image.RowsPerStrip",
296 "Exif.Image.StripByteCounts",
297 "Exif.Image.TileWidth",
298 "Exif.Image.TileLength",
299 "Exif.Image.TileOffsets",
300 "Exif.Image.TileByteCounts",
301 "Exif.Image.PlanarConfiguration"
312 static const std::string
needle =
"Exif.SubImage";
320 static const char *keys[] = {
322 "Exif.Canon.ColorSpace",
323 "Exif.Canon.ColorData",
326 "Exif.Nikon3.Preview",
327 "Exif.NikonPreview.JPEGInterchangeFormat",
330 "Exif.Image.CFARepeatPatternDim",
331 "Exif.Image.CFAPattern",
332 "Exif.Image.InterColorProfile",
333 "Exif.Image.SpectralSensitivity",
335 "Exif.Image.SpatialFrequencyResponse",
337 "Exif.Image.SensingMethod",
338 "Exif.Image.TIFFEPStandardID",
339 "Exif.Photo.SpectralSensitivity",
341 "Exif.Photo.SpatialFrequencyResponse",
342 "Exif.Photo.SensingMethod",
343 "Exif.Photo.CFAPattern",
346 "Exif.Image.DNGVersion",
347 "Exif.Image.DNGBackwardVersion",
348 "Exif.Image.DNGPrivateData",
349 "Exif.Image.DefaultBlackRender",
350 "Exif.Image.DefaultCropOrigin",
351 "Exif.Image.DefaultCropSize",
352 "Exif.Image.RawDataUniqueID",
353 "Exif.Image.OriginalRawFileName",
354 "Exif.Image.OriginalRawFileData",
355 "Exif.Image.ActiveArea",
356 "Exif.Image.MaskedAreas",
357 "Exif.Image.AsShotICCProfile",
358 "Exif.Image.OpcodeList1",
359 "Exif.Image.OpcodeList2",
360 "Exif.Image.OpcodeList3",
361 "Exif.Image.AsShotNeutral",
362 "Exif.Image.AsShotWhiteXY",
363 "Exif.Image.BaselineExposure",
364 "Exif.Image.BaselineNoise",
365 "Exif.Image.BaselineSharpness",
366 "Exif.Image.LinearResponseLimit",
367 "Exif.Image.ShadowScale",
368 "Exif.Image.PreviewApplicationName",
369 "Exif.Image.PreviewApplicationVersion",
370 "Exif.Image.PreviewSettingsDigest",
371 "Exif.Image.PreviewColorSpace",
372 "Exif.Image.PreviewDateTime",
373 "Exif.Image.NoiseProfile",
374 "Exif.Image.NewRawImageDigest",
375 "Exif.Image.RawImageDigest",
377 "Exif.Photo.MakerNote",
380 "Exif.Pentax.PreviewResolution",
381 "Exif.Pentax.PreviewLength",
382 "Exif.Pentax.PreviewOffset",
383 "Exif.PentaxDng.PreviewResolution",
384 "Exif.PentaxDng.PreviewLength",
385 "Exif.PentaxDng.PreviewOffset",
387 "Exif.PentaxDng.ColorInfo",
390 "Exif.Minolta.Thumbnail",
391 "Exif.Minolta.ThumbnailOffset",
392 "Exif.Minolta.ThumbnailLength",
395 "Exif.SonyMinolta.ThumbnailOffset",
396 "Exif.SonyMinolta.ThumbnailLength",
399 "Exif.Olympus.Thumbnail",
400 "Exif.Olympus.ThumbnailOffset",
401 "Exif.Olympus.ThumbnailLength",
403 "Exif.Image.BaselineExposureOffset",
407 "Exif.Samsung2.SensorAreas",
408 "Exif.Samsung2.ColorSpace",
409 "Exif.Samsung2.EncryptionKey",
410 "Exif.Samsung2.WB_RGGBLevelsUncorrected",
411 "Exif.Samsung2.WB_RGGBLevelsAuto",
412 "Exif.Samsung2.WB_RGGBLevelsIlluminator1",
413 "Exif.Samsung2.WB_RGGBLevelsIlluminator2",
414 "Exif.Samsung2.WB_RGGBLevelsBlack",
415 "Exif.Samsung2.ColorMatrix",
416 "Exif.Samsung2.ColorMatrixSRGB",
417 "Exif.Samsung2.ColorMatrixAdobeRGB",
418 "Exif.Samsung2.ToneCurve1",
419 "Exif.Samsung2.ToneCurve2",
420 "Exif.Samsung2.ToneCurve3",
421 "Exif.Samsung2.ToneCurve4"
427 static const char *
dngkeys[] = {
429 "Exif.Image.ColorMatrix1",
430 "Exif.Image.ColorMatrix2",
431 "Exif.Image.CameraCalibration1",
432 "Exif.Image.CameraCalibration2",
433 "Exif.Image.ReductionMatrix1",
434 "Exif.Image.ReductionMatrix2",
435 "Exif.Image.AnalogBalance",
436 "Exif.Image.CalibrationIlluminant1",
437 "Exif.Image.CalibrationIlluminant2",
438 "Exif.Image.CameraCalibrationSignature",
439 "Exif.Image.ProfileCalibrationSignature",
440 "Exif.Image.ExtraCameraProfiles",
441 "Exif.Image.AsShotProfileName",
442 "Exif.Image.ProfileName",
443 "Exif.Image.ProfileHueSatMapDims",
444 "Exif.Image.ProfileHueSatMapData1",
445 "Exif.Image.ProfileHueSatMapData2",
446 "Exif.Image.ProfileToneCurve",
447 "Exif.Image.ProfileEmbedPolicy",
448 "Exif.Image.ProfileCopyright",
449 "Exif.Image.ForwardMatrix1",
450 "Exif.Image.ForwardMatrix2",
451 "Exif.Image.ProfileLookTableDims",
452 "Exif.Image.ProfileLookTableData",
453 "Exif.Image.ProfileLookTableEncoding",
454 "Exif.Image.ProfileHueSatMapEncoding"
461 exifData[
"Exif.Photo.ColorSpace"] = uint16_t(1);
463 exifData[
"Exif.Photo.ColorSpace"] = uint16_t(0xFFFF);
475 exifData[
"Exif.Image.XResolution"] = Exiv2::Rational(resolution, 1);
476 exifData[
"Exif.Image.YResolution"] = Exiv2::Rational(resolution, 1);
477 exifData[
"Exif.Image.ResolutionUnit"] = uint16_t(2);
487 static const char * keys[] = {
489 "Exif.Image.ImageDescription",
490 "Exif.Photo.UserComment",
491 "Exif.Image.Copyright",
493 "Exif.Image.RatingPercent",
494 "Exif.Photo.SubSecTimeOriginal",
495 "Exif.GPSInfo.GPSVersionID",
496 "Exif.GPSInfo.GPSLongitudeRef",
497 "Exif.GPSInfo.GPSLatitudeRef",
498 "Exif.GPSInfo.GPSLongitude",
499 "Exif.GPSInfo.GPSLatitude",
500 "Exif.GPSInfo.GPSAltitudeRef",
501 "Exif.GPSInfo.GPSAltitude"
517 char *desc = (
char *)
res->data;
519 exifData[
"Exif.Image.ImageDescription"] = desc;
521 exifData[
"Exif.Photo.UserComment"] = desc;
525#if EXIV2_TEST_VERSION(0,27,4)
529 exifData[
"Exif.Image.ImageDescription"] =
"";
535 exifData[
"Exif.Image.Copyright"] = (
char *)
res->data;
539#if EXIV2_TEST_VERSION(0,27,4)
543 exifData[
"Exif.Image.Copyright"] =
"";
550 exifData[
"Exif.Image.Rating"] = rating;
560 exifData[
"Exif.GPSInfo.GPSVersionID"] =
"02 02 00 00";
561 exifData[
"Exif.GPSInfo.GPSLongitudeRef"] = (
cimg->geoloc.longitude < 0) ?
"W" :
"E";
562 exifData[
"Exif.GPSInfo.GPSLatitudeRef"] = (
cimg->geoloc.latitude < 0) ?
"S" :
"N";
577 exifData[
"Exif.GPSInfo.GPSVersionID"] =
"02 02 00 00";
578 exifData[
"Exif.GPSInfo.GPSAltitudeRef"] = (
cimg->geoloc.elevation < 0) ?
"1" :
"0";
596 exifData[
"Exif.Image.DateTimeOriginal"] = datetime;
597 exifData[
"Exif.Photo.DateTimeOriginal"] = datetime;
605 Exiv2::ExifParser::encode(blob, Exiv2::bigEndian,
exifData);
606 const size_t length = blob.size();
607 *buf = (uint8_t *)
malloc(length);
612 memcpy(*buf, &(blob[0]), length);
615 catch(
const std::exception &e)
618 std::string s(e.what());
619 std::cerr <<
"[exiv2 dt_exif_read_blob] " << path <<
": " << s << std::endl;
667 std::cout <<
"malformed entry" << std::endl;
671 std::cout << entry->
operation << std::endl;
672 std::cout <<
" modversion :" << entry->
modversion << std::endl;
673 std::cout <<
" enabled :" << entry->
enabled << std::endl;
674 std::cout <<
" params :" << (entry->
params ?
"<found>" :
"<missing>") << std::endl;
675 std::cout <<
" multi_name :" << (entry->
multi_name ? entry->
multi_name :
"<missing>") << std::endl;
676 std::cout <<
" multi_priority :" << entry->
multi_priority << std::endl;
677 std::cout <<
" iop_order :" << entry->
iop_order << std::endl;
678 std::cout <<
" blendop_version :" << entry->
blendop_version << std::endl;
679 std::cout <<
" blendop_params :" << (entry->
blendop_params ?
"<found>" :
"<missing>") << std::endl;
680 std::cout << std::endl;
700 pugi::xml_document
doc;
701#if defined(PUGIXML_VERSION) && PUGIXML_VERSION >= 150
702 pugi::xml_parse_result result =
doc.load_string(
xmpPacket.c_str());
704 pugi::xml_parse_result result =
doc.load(
xmpPacket.c_str());
709 std::cerr <<
"XML '" << filename <<
"' parsed with errors" << std::endl;
710 std::cerr <<
"Error description: " << result.description() << std::endl;
711 std::cerr <<
"Error offset: " << result.offset << std::endl;
717#if defined(PUGIXML_VERSION) && PUGIXML_VERSION >= 150
718 pugi::xpath_node modversion =
superold ?
719 doc.select_node(
"//darktable:history_modversion/rdf:Bag"):
720 doc.select_node(
"//darktable:history_modversion/rdf:Seq");
722 doc.select_node(
"//darktable:history_enabled/rdf:Bag"):
723 doc.select_node(
"//darktable:history_enabled/rdf:Seq");
724 pugi::xpath_node operation =
superold ?
725 doc.select_node(
"//darktable:history_operation/rdf:Bag"):
726 doc.select_node(
"//darktable:history_operation/rdf:Seq");
728 doc.select_node(
"//darktable:history_params/rdf:Bag"):
729 doc.select_node(
"//darktable:history_params/rdf:Seq");
730 pugi::xpath_node blendop_params =
superold ?
731 doc.select_node(
"//darktable:blendop_params/rdf:Bag"):
732 doc.select_node(
"//darktable:blendop_params/rdf:Seq");
733 pugi::xpath_node blendop_version =
superold ?
734 doc.select_node(
"//darktable:blendop_version/rdf:Bag"):
735 doc.select_node(
"//darktable:blendop_version/rdf:Seq");
736 pugi::xpath_node multi_priority =
superold ?
737 doc.select_node(
"//darktable:multi_priority/rdf:Bag"):
738 doc.select_node(
"//darktable:multi_priority/rdf:Seq");
739 pugi::xpath_node multi_name =
superold ?
740 doc.select_node(
"//darktable:multi_name/rdf:Bag"):
741 doc.select_node(
"//darktable:multi_name/rdf:Seq");
743 pugi::xpath_node modversion =
superold ?
744 doc.select_single_node(
"//darktable:history_modversion/rdf:Bag"):
745 doc.select_single_node(
"//darktable:history_modversion/rdf:Seq");
747 doc.select_single_node(
"//darktable:history_enabled/rdf:Bag"):
748 doc.select_single_node(
"//darktable:history_enabled/rdf:Seq");
749 pugi::xpath_node operation =
superold ?
750 doc.select_single_node(
"//darktable:history_operation/rdf:Bag"):
751 doc.select_single_node(
"//darktable:history_operation/rdf:Seq");
753 doc.select_single_node(
"//darktable:history_params/rdf:Bag"):
754 doc.select_single_node(
"//darktable:history_params/rdf:Seq");
755 pugi::xpath_node blendop_params =
superold ?
756 doc.select_single_node(
"//darktable:blendop_params/rdf:Bag"):
757 doc.select_single_node(
"//darktable:blendop_params/rdf:Seq");
758 pugi::xpath_node blendop_version =
superold ?
759 doc.select_single_node(
"//darktable:blendop_version/rdf:Bag"):
760 doc.select_single_node(
"//darktable:blendop_version/rdf:Seq");
761 pugi::xpath_node multi_priority =
superold ?
762 doc.select_single_node(
"//darktable:multi_priority/rdf:Bag"):
763 doc.select_single_node(
"//darktable:multi_priority/rdf:Seq");
764 pugi::xpath_node multi_name =
superold ?
765 doc.select_single_node(
"//darktable:multi_name/rdf:Bag"):
766 doc.select_single_node(
"//darktable:multi_name/rdf:Seq");
795 if(multi_name &&
multi_name_iter != multi_name.node().children().end())
836 for(
auto history = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history")); history != xmpData.end(); history++)
842 std::string
key_item = history->key();
852 std::cerr <<
"error reading history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
862 std::cerr <<
"error reading history from '"
863 <<
key <<
"' (" << filename <<
")" << std::endl;
937 static const std::locale&
c_locale = std::locale(
"C");
965 std::cerr <<
"[exif] error: reading history from '" << filename <<
"' failed due to missing tags" << std::endl;
989 Exiv2::XmpData::iterator mask;
991 Exiv2::XmpData::iterator mask_type;
992 Exiv2::XmpData::iterator mask_version;
993 Exiv2::XmpData::iterator
mask_id;
994 Exiv2::XmpData::iterator mask_nb;
995 Exiv2::XmpData::iterator mask_src;
996 if((mask = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask"))) != xmpData.end()
997 && (mask_src = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_src"))) != xmpData.end()
998 && (
mask_name = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_name"))) != xmpData.end()
999 && (mask_type = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_type"))) != xmpData.end()
1000 && (mask_version = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_version"))) != xmpData.end()
1001 && (
mask_id = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_id"))) != xmpData.end()
1002 && (mask_nb = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.mask_nb"))) != xmpData.end())
1005 const size_t cnt = (size_t)mask->count();
1011 const size_t mask_nb_cnt = (size_t)mask_nb->count();
1015 for(
size_t i = 0;
i <
cnt;
i++)
1030 std::string
mask_str = mask->toString(
i);
1035 entry->
mask_nb = mask_nb->toLong(
i);
1055 for(
auto history = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.masks_history")); history != xmpData.end(); history++)
1061 std::string
key_item = history->key();
1071 std::cerr <<
"error reading masks history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
1081 std::cerr <<
"error reading masks history from '" <<
key <<
"' (" << filename <<
")" << std::endl;
1121 std::string
value_item = history->toString();
1130 std::string
value_item = history->toString();
1141 std::string
value_item = history->toString();
1161 const int mask_num = 0;
1186 int32_t imgid = *(
int *)user_data;
1206 fprintf(
stderr,
"[masks] error loading masks from xmp file, bad binary blob size.\n");
1251 std::unique_ptr<Exiv2::Image> image(Exiv2::ImageFactory::open(
WIDEN(filename)));
1252 if(!image.get())
return 1;
1253 image->readMetadata();
1254 Exiv2::XmpData &xmpData = image->xmpData();
1256 Exiv2::XmpData::iterator pos;
1263 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.xmp_version"))) != xmpData.end())
1269 const size_t ns_pos = image->xmpPacket().find(
"xmlns:darktable=\"http://darktable.sf.net/\"");
1276 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.raw_params"))) != xmpData.end())
1290 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.auto_presets_applied"))) != xmpData.end())
1297 else if(xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.xmp_version")) == xmpData.end())
1313 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_version"))) != xmpData.end())
1318 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_list"))) != xmpData.end())
1332 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.iop_order_version"))) != xmpData.end())
1388 std::string &
xmpPacket = image->xmpPacket();
1397 std::cerr <<
"error: Xmp schema version " <<
xmp_version <<
" in " << filename <<
" not supported" << std::endl;
1406 fprintf(
stderr,
"[exif] error deleting history for image %d\n", img->
id);
1434 fprintf(
stderr,
"[exif] error adding history entry for image %d\n", img->
id);
1470 "[exif] cannot get iop-order for module '%s', XMP may be corrupted\n",
1473 iop_order_list =
NULL;
1513 fprintf(
stderr,
"[exif] error shifting history nums for image %d\n", img->
id);
1517 if(!
dt_history_repository_write_item(img->
id, 0,
"mask_manager",
NULL, 0, 1, 0,
NULL, 0, 0, 0,
""))
1519 fprintf(
stderr,
"[exif] error adding mask history entry for image %d\n", img->
id);
1530 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history_end"))) != xmpData.end() && num > 0)
1532 int history_end =
MIN(pos->toLong(), num);
1537 fprintf(
stderr,
"[exif] error writing history_end for image %d\n", img->
id);
1549 fprintf(
stderr,
"[exif] error writing history_end for image %d\n", img->
id);
1557 fprintf(
stderr,
"[exif] error writing iop_list for image %d\n", img->
id);
1580 fprintf(
stderr,
"[exif] dt_exif_xmp_read for %s, id %i found auto_presets_applied but there was no history\n",filename,img->
id);
1585 iop_order_list =
NULL;
1597 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.history_current_hash"))) != xmpData.end())
1603 const std::string
hash_str = pos->toString();
1616 std::cerr <<
"[exif] error reading history from '" << filename <<
"'" << std::endl;
1622 catch(
const std::exception &e)
1640 const int mask_type,
const char *
mask_name,
const int mask_version,
1641 const void *points,
const int points_len,
const int mask_nb,
1647 const int num =
ctx->num;
1652 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_num", num);
1653 xmpData[
key] = mask_num;
1654 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_id", num);
1656 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_type", num);
1657 xmpData[
key] = mask_type;
1658 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_name", num);
1660 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_version", num);
1661 xmpData[
key] = mask_version;
1662 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_points", num);
1664 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_nb", num);
1665 xmpData[
key] = mask_nb;
1666 snprintf(
key,
sizeof(
key),
"Xmp.darktable.masks_history[%d]/darktable:mask_src", num);
1667 xmpData[
key] = mask_src;
1682 const int modversion,
const char *operation,
1683 const void *op_params,
const int params_len,
1685 const int blendop_params_len,
const int blendop_version,
1686 const int multi_priority,
const char *multi_name,
1687 const char *preset_name)
1692 const int num =
ctx->num;
1698 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:num", num);
1700 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:operation", num);
1701 xmpData[
key] = operation;
1702 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:enabled", num);
1704 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:modversion", num);
1705 xmpData[
key] = modversion;
1706 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:params", num);
1708 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:multi_name", num);
1709 xmpData[
key] = multi_name ? multi_name :
"";
1710 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:multi_priority", num);
1711 xmpData[
key] = multi_priority;
1718 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:blendop_version", num);
1719 xmpData[
key] = blendop_version;
1720 snprintf(
key,
sizeof(
key),
"Xmp.darktable.history[%d]/darktable:blendop_params", num);
1721 xmpData[
key] = blendop_params;
1736 Exiv2::XmpTextValue
tvm(
"");
1737 tvm.setXmpArrayType(Exiv2::XmpValue::xaSeq);
1738 xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.masks_history"), &
tvm);
1747 Exiv2::XmpTextValue
tv(
"");
1748 tv.setXmpArrayType(Exiv2::XmpValue::xaSeq);
1749 xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.history"), &
tv);
1753 if(history_end == -1) history_end = num - 1;
1754 else history_end =
MIN(history_end, num - 1);
1755 xmpData[
"Xmp.darktable.history_end"] = history_end;
1761 static const char *keys[] =
1763 "Xmp.darktable.import_timestamp",
1764 "Xmp.darktable.change_timestamp",
1765 "Xmp.darktable.export_timestamp",
1766 "Xmp.darktable.print_timestamp"
1796 Exiv2::XmpData::iterator pos;
1799 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.change_timestamp"))) != xmpData.end())
1803 else if(pos->toLong() >= 1)
1806 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.export_timestamp"))) != xmpData.end())
1810 else if(pos->toLong() >= 1)
1813 if((pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.darktable.print_timestamp"))) != xmpData.end())
1817 else if(pos->toLong() >= 1)
1824 static const char *keys[] =
1826 "Xmp.exif.GPSVersionID",
1827 "Xmp.exif.GPSLongitude",
1828 "Xmp.exif.GPSLatitude",
1829 "Xmp.exif.GPSAltitudeRef",
1830 "Xmp.exif.GPSAltitude"
1843 if(latitude < 0)
lat_dir =
'S';
1845 longitude =
fabs(longitude);
1846 latitude =
fabs(latitude);
1860 xmpData[
"Xmp.exif.GPSVersionID"] =
"2.2.0.0";
1861 xmpData[
"Xmp.exif.GPSLongitude"] =
long_str;
1862 xmpData[
"Xmp.exif.GPSLatitude"] =
lat_str;
1867 if(!
isnan(altitude))
1869 xmpData[
"Xmp.exif.GPSAltitudeRef"] = (altitude < 0) ?
"1" :
"0";
1873 xmpData[
"Xmp.exif.GPSAltitude"] =
ele_str;
1904 Exiv2::Value *
v = (Exiv2::Value *)user_data;
1917 std::unique_ptr<Exiv2::Value>
v(Exiv2::Value::create(Exiv2::xmpSeq));
1921 if(
v->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.darktable.colorlabels"),
v.get());
1940 xmpData[
"Xmp.darktable.history_current_hash"] =
value;
1952 int stars = 1,
raw_params = 0, history_end = -1;
1953 double longitude =
NAN, latitude =
NAN, altitude =
NAN;
1954 gchar *filename =
NULL;
1955 gchar *iop_order_list =
NULL;
1965 if(
row.has_longitude) longitude =
row.longitude;
1966 if(
row.has_latitude) latitude =
row.latitude;
1967 if(
row.has_altitude) altitude =
row.altitude;
1968 history_end =
row.history_end;
1969 gts =
row.datetime_taken;
1995 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.xmp.Rating"));
1996 if(pos != xmpData.end()) xmpData.erase(pos);
2000 if(filename) xmpData[
"Xmp.xmpMM.DerivedFrom"] = filename;
2012 std::unique_ptr<Exiv2::Value>
v1(Exiv2::Value::create(Exiv2::xmpBag));
2014 std::unique_ptr<Exiv2::Value>
v2(Exiv2::Value::create(Exiv2::xmpBag));
2021 v1->read((
char *)tag->data);
2029 if(
v1->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.dc.subject"),
v1.get());
2038 v2->read((
char *)
hier->data);
2046 if(
v2->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.lr.hierarchicalSubject"),
v2.get());
2051 xmpData[
"Xmp.darktable.xmp_version"] =
xmp_version;
2052 xmpData[
"Xmp.darktable.raw_params"] =
raw_params;
2054 xmpData[
"Xmp.darktable.auto_presets_applied"] = 1;
2056 xmpData[
"Xmp.darktable.auto_presets_applied"] = 0;
2061 xmpData[
"Xmp.darktable.iop_order_version"] = iop_order_version;
2062 if(iop_order_list) xmpData[
"Xmp.darktable.iop_order_list"] = iop_order_list;
2075 int stars = 1,
raw_params = 0, history_end = -1;
2076 double longitude =
NAN, latitude =
NAN, altitude =
NAN;
2077 gchar *filename =
NULL;
2079 gchar *iop_order_list =
NULL;
2088 if(
row.has_longitude) longitude =
row.longitude;
2089 if(
row.has_latitude) latitude =
row.latitude;
2090 if(
row.has_altitude) altitude =
row.altitude;
2091 history_end =
row.history_end;
2092 gts =
row.datetime_taken;
2122 Exiv2::XmpData::iterator pos = xmpData.findKey(Exiv2::XmpKey(
"Xmp.xmp.Rating"));
2123 if(pos != xmpData.end()) xmpData.erase(pos);
2127 if(filename) xmpData[
"Xmp.xmpMM.DerivedFrom"] = filename;
2145 std::unique_ptr<Exiv2::Value>
v1(Exiv2::Value::create(Exiv2::xmpBag));
2151 v1->read((
char *)tag->data);
2159 if(
v1->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.dc.subject"),
v1.get());
2166 std::unique_ptr<Exiv2::Value>
v2(Exiv2::Value::create(Exiv2::xmpBag));
2172 v2->read((
char *)
hier->data);
2180 if(
v2->count() > 0) xmpData.add(Exiv2::XmpKey(
"Xmp.lr.hierarchicalSubject"),
v2.get());
2187 xmpData[
"Xmp.darktable.xmp_version"] =
xmp_version;
2188 xmpData[
"Xmp.darktable.raw_params"] =
raw_params;
2190 xmpData[
"Xmp.darktable.auto_presets_applied"] = 1;
2192 xmpData[
"Xmp.darktable.auto_presets_applied"] = 0;
2197 xmpData[
"Xmp.darktable.iop_order_version"] = iop_order_version;
2198 if(iop_order_list) xmpData[
"Xmp.darktable.iop_order_list"] = iop_order_list;
2207#if EXIV2_TEST_VERSION(0,27,0)
2208#define ERROR_CODE(a) (static_cast<Exiv2::ErrorCode>((a)))
2210#define ERROR_CODE(a) (a)
2222 Exiv2::XmpData xmpData;
2229 Exiv2::XmpParser::decode(xmpData,
xmpPacket);
2261 if(Exiv2::XmpParser::encode(
xmpPacket, xmpData,
2262 Exiv2::XmpParser::useCompactFormat | Exiv2::XmpParser::omitPacketWrapper) != 0)
2264 throw Exiv2::Error(
ERROR_CODE(1),
"[xmp_write] failed to serialize xmp data");
2268 catch(
const std::exception &e)
2270 std::cerr <<
"[xmp_read_blob] caught exiv2 exception '" << e.what() <<
"'\n";
2279 Exiv2::XmpData::iterator pos = xmp.findKey(Exiv2::XmpKey(
key));
2280 if (pos != xmp.end())
2283 catch(
const std::exception &e)
2293 for(Exiv2::XmpData::iterator
i = xmpData.begin();
i != xmpData.end();)
2296 i = xmpData.erase(
i);
2301 catch(
const std::exception &e)
2310 Exiv2::ExifData::iterator pos = exif.findKey(Exiv2::ExifKey(
key));
2311 if (pos != exif.end())
2314 catch(
const std::exception &e)
2323 Exiv2::IptcData::iterator pos;
2324 while((pos =
iptc.findKey(Exiv2::IptcKey(
key))) !=
iptc.end())
2327 catch(
const std::exception &e)
2341 std::unique_ptr<Exiv2::Image> img(Exiv2::ImageFactory::open(
WIDEN(filename)));
2345 img->readMetadata();
2358 catch(
const std::exception &e)
2360 std::cerr <<
"[xmp_attach] " <<
input_filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
2363 Exiv2::XmpData &xmpData = img->xmpData();
2387 static const char *keys[] = {
2388 "Xmp.tiff.Orientation"
2400 Exiv2::ExifData &
exifData = img->exifData();
2407 img->clearExifData();
2412 Exiv2::IptcData &
iptcData = img->iptcData();
2420 params->jobcode =
"infos";
2427 gchar *tagname = (gchar *)
tags->data;
2436 Exiv2::ExifData::const_iterator pos;
2445 if(result && result[0])
2459 xmpData[tagname] =
tuple;
2463 xmpData[tagname] = result;
2474 Exiv2::IptcKey
key(tagname);
2475 Exiv2::Iptcdatum id(
key);
2476 gchar **values =
g_strsplit(result,
", ", 0);
2479 gchar **entry = values;
2537 img->writeMetadata();
2539 catch(Exiv2::AnyError &e)
2541#if EXIV2_TEST_VERSION(0,27,0)
2542 if(e.code() == Exiv2::ErrorCode::kerTooLargeJpegSegment)
2553 img->writeMetadata();
2555 catch(
const std::exception &
e2)
2557 std::cerr <<
"[dt_exif_xmp_attach_export] without history " << filename <<
": caught exiv2 exception '" <<
e2.what() <<
"'\n";
2564 catch(
const std::exception &e)
2566 std::cerr <<
"[dt_exif_xmp_attach_export] " << filename <<
": caught exception '" << e.what() <<
"'\n";
2571 catch(
const std::exception &e)
2573 std::cerr <<
"[dt_exif_xmp_attach_export] " << filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
2586 const int32_t imgid = image->
id;
2590 Exiv2::XmpData xmpData;
2600 unsigned char *content = (
unsigned char*)
dt_read_file(filename, &end);
2606 _(
"The XMP file \n'%s'\n weighs %.2f MB. Writing it will take some time."),
2607 filename, (
float)end / 1000000);
2608 fprintf(
stdout,
"The XMP file '%s' weighs %.2f MB. Writing it will take some time.\n", filename, (
float)end / 1000000);
2615 xmpPacket.assign((
const char *)content, end);
2618 Exiv2::XmpParser::decode(xmpData,
xmpPacket);
2635 if(Exiv2::XmpParser::encode(
xmpPacket, xmpData,
2636 Exiv2::XmpParser::useCompactFormat | Exiv2::XmpParser::omitPacketWrapper) != 0)
2638 throw Exiv2::Error(
ERROR_CODE(1),
"[xmp_write] failed to serialize xmp data");
2642 const char *
xml_header =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2680 catch(
const std::exception &e)
2682 std::cerr <<
"[dt_exif_xmp_write] " << filename <<
": caught exiv2 exception '" << e.what() <<
"'\n";
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)
Integer for name, clamped to the bounds declared in the XML.
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...
gboolean enabled
–doc was passed on the command line
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.
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.
static const dt_iop_drawlayer_params_t * params
_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)
g_free() one pointer, with the signature GDestroyNotify wants.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
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::@24 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)
static bool _read_xmp_packet(const char *filename, std::string &packet)
Read an XMP packet off disk into packet, FALSE if the file cannot be read.
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,...