improved support for metadata editing

(cherry picked from commit 85da0b51ecf7ece768c0267aead71dd94404d4dc)
This commit is contained in:
Alberto Griggio 2020-04-10 05:54:40 -07:00 committed by Lawrence Lee
parent 8a5aa6c119
commit b92e77fb96
No known key found for this signature in database
GPG Key ID: 048FF2B76A63895F
15 changed files with 615 additions and 183 deletions

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="SVGRoot"
inkscape:export-filename="/tmp/template.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="0.91 r13725"
sodipodi:docname="edit-small.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
bordercolor="#666768"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="1.04"
inkscape:cx="8"
inkscape:cy="7.5803253"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:grid-bbox="true"
inkscape:pagecheckerboard="false"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="true"
inkscape:snap-others="false"
inkscape:object-nodes="true"
inkscape:snap-grids="true"
inkscape:snap-bbox-midpoints="false">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="7"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Maciej Dworak</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1"
transform="translate(0,-8)">
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
d="M 55,16 H 65 M 60,11 V 21"
style="opacity:0.7;fill:none;fill-rule:evenodd;stroke:#2a7fff;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4977" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
d="M 24,16 H 34 M 29,11 V 21"
style="opacity:0.7;fill:none;fill-rule:evenodd;stroke:#2a7fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4981" />
<path
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0"
d="M 39,16 H 49 M 44,11 V 21"
style="opacity:0.7;fill:none;fill-rule:evenodd;stroke:#2a7fff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4983" />
<path
style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00157475;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 11.413246,9.78023 c 0.784726,0 2.23775,1.576711 2.23775,2.428236 0,0.851523 -1.091559,1.184475 -1.091559,1.184475 l -2.237749,-2.428235 c 0,0 0.306833,-1.184476 1.091558,-1.184476 z M 9.762249,11.571765 12,14 6.405626,20.070587 4.1678762,17.642352 Z m -6.1538101,6.677647 2.237749,2.428235 -0.027314,0.02964 -3.9160622,1.821176 1.6783123,-4.249412 z"
id="path1658"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zzcczccccccccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

136
rtdata/images/svg/edit.svg Normal file
View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24px"
height="24px"
viewBox="0 0 24 24"
version="1.1"
id="SVGRoot"
inkscape:export-filename="/tmp/template.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
inkscape:version="0.91 r13725"
sodipodi:docname="edit.svg">
<sodipodi:namedview
id="base"
pagecolor="#E0E1E2"
bordercolor="#666768"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="34.25"
inkscape:cx="12"
inkscape:cy="12"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="0"
inkscape:window-y="30"
inkscape:window-maximized="1"
inkscape:grid-bbox="true"
inkscape:pagecheckerboard="false"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="true"
inkscape:snap-others="false"
inkscape:object-nodes="false"
inkscape:snap-grids="true"
inkscape:snap-bbox-midpoints="false">
<inkscape:grid
type="xygrid"
id="grid1374"
originx="1"
originy="1"
empspacing="11"
dotted="false" />
</sodipodi:namedview>
<defs
id="defs815" />
<metadata
id="metadata818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Maciej Dworak</dc:title>
</cc:Agent>
</dc:creator>
<dc:rights>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:rights>
<dc:description>RawTherapee icon.</dc:description>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00157475;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 17.761905,2 C 18.897428,2 21,4.1025723 21,5.238095 c 0,1.1355228 -1.57952,1.57952 -1.57952,1.57952 L 16.182385,3.57952 c 0,0 0.443997,-1.57952 1.57952,-1.57952 z M 15.372861,4.3890438 18.610956,7.6271387 10.515719,15.722376 7.277624,12.484281 Z M 6.4681002,13.293805 9.7061953,16.5319 9.6666663,16.571429 4,19 6.4285712,13.333334 Z"
id="path1658"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zzcczccccccccccc" />
<path
style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.00157475;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m -34.951172,1.048828 c -1.402705,0 -4,2.5972953 -4,4 0,1.4027047 1.951172,1.9511719 1.951172,1.9511719 l 4,-4 c 0,0 -0.548467,-1.9511719 -1.951172,-1.9511719 z M -32,3.9999999 l -4,4 L -26,18 -22,14 Z M -21,15 l -4,4 0.04883,0.04883 7,3 -3,-7 z"
id="path1658-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zzcczccccccccccc" />
<path
style="fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M -34,6.9999999 -25,16 l -1,1 -9,-9.0000001 z"
id="path1682"
inkscape:connector-curvature="0" />
<path
style="fill:#2a7fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -24,19 5,2 -4,-3 z"
id="path1686"
inkscape:connector-curvature="0" />
<circle
style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157475;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path1690"
cx="-35"
cy="19"
r="3" />
<path
inkscape:connector-curvature="0"
style="opacity:0.7;fill:#2a7fff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157475;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
d="M -36.898437,16.679688 A 3,3 0 0 0 -38,19 a 3,3 0 0 0 3,3 3,3 0 0 0 2.322266,-1.101562 A 3.5,3.5 0 0 1 -33.5,21 3.5,3.5 0 0 1 -37,17.5 a 3.5,3.5 0 0 1 0.101563,-0.820312 z"
id="circle1692" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -77,8 +77,7 @@ EXIFPANEL_RESET;Reset
EXIFPANEL_RESETALL;Reset All
EXIFPANEL_RESETALLHINT;Reset all tags to their original values.
EXIFPANEL_RESETHINT;Reset the selected tags to their original values.
EXIFPANEL_SHOWALL;Show all
EXIFPANEL_SUBDIRECTORY;Subdirectory
EXIFPANEL_BASIC_GROUP;Basic
EXPORT_BYPASS;Processing steps to bypass
EXPORT_BYPASS_ALL;Select / Unselect All
EXPORT_BYPASS_DEFRINGE;Bypass Defringe
@ -1548,7 +1547,7 @@ MAIN_TAB_COLOR;Color
MAIN_TAB_COLOR_TOOLTIP;Shortcut: <b>Alt-c</b>
MAIN_TAB_DETAIL;Detail
MAIN_TAB_DETAIL_TOOLTIP;Shortcut: <b>Alt-d</b>
MAIN_TAB_DEVELOP; Batch Edit
MAIN_TAB_DEVELOP; Batch Edit
MAIN_TAB_EXIF;Exif
MAIN_TAB_EXPORT; Fast Export
MAIN_TAB_EXPOSURE;Exposure

View File

@ -699,3 +699,36 @@ double FramesMetaData::apertureFromString(std::string s)
return std::atof(s.c_str());
}
namespace {
template<class T>
void set_exif(Exiv2::ExifData &exif, const std::string &key, T val)
{
try {
exif[key] = val;
} catch (std::exception &exc) {}
}
} // namespace
void FramesData::fillBasicTags(Exiv2::ExifData &exif) const
{
if (!hasExif()) {
return;
}
set_exif(exif, "Exif.Photo.ISOSpeedRatings", getISOSpeed());
set_exif(exif, "Exif.Photo.FNumber", Exiv2::DoubleValue(getFNumber()));
//set_exif(exif, "Exif.Photo.ExposureTime", Exiv2::DoubleValue(getShutterSpeed()));
set_exif(exif, "Exif.Photo.ExposureTime", shutterToString(getShutterSpeed()));
set_exif(exif, "Exif.Photo.FocalLength", Exiv2::DoubleValue(getFocalLen()));
set_exif(exif, "Exif.Photo.ExposureBiasValue", Exiv2::DoubleValue(getExpComp()));
set_exif(exif, "Exif.Image.Make", getMake());
set_exif(exif, "Exif.Image.Model", getModel());
set_exif(exif, "Exif.Photo.LensModel", getLens());
char buf[256];
auto t = getDateTime();
strftime(buf, 256, "%Y:%m:%d %H:%M:%S", &t);
set_exif(exif, "Exif.Photo.DateTimeOriginal", buf);
}

View File

@ -23,6 +23,7 @@
#include <string>
#include "imageio.h"
#include "metadata.h"
namespace Glib
{
@ -83,6 +84,8 @@ public:
std::string getOrientation() const override;
Glib::ustring getFileName() const override;
int getRating() const override;
void fillBasicTags(Exiv2::ExifData &exif) const;
};
}

View File

@ -26,6 +26,7 @@
#include "metadata.h"
#include "settings.h"
#include "imagedata.h"
#include "../rtgui/version.h"
#include "../rtgui/pathutils.h"
@ -309,6 +310,16 @@ void Exiv2Metadata::remove_unwanted(Exiv2::ExifData &dst) const
static const std::vector<std::string> badpatterns = {
"Exif.SubImage"
};
if (exif_keys_ && !src_.empty()) {
try {
FramesData fd(src_);
fd.fillBasicTags(dst);
} catch (std::exception &exc) {
std::cout << "Error reading metadata from " << src_
<< std::endl;
}
}
for (auto it = dst.begin(); it != dst.end(); ) {
if (badtags.find(it->key()) != badtags.end()) {

View File

@ -379,33 +379,6 @@ bool saveToKeyfile(
return false;
}
const std::map<std::string, std::string> exif_keys = {
{"Copyright", "Exif.Image.Copyright"},
{"Artist", "Exif.Image.Artist"},
{"ImageDescription", "Exif.Image.ImageDescription"},
{"Exif.UserComment", "Exif.Photo.UserComment"}
};
const std::map<std::string, std::string> iptc_keys = {
{"Title", "Iptc.Application2.ObjectName"},
{"Category", "Iptc.Application2.Category"},
{"SupplementalCategories", "Iptc.Application2.SuppCategory"},
{"Keywords", "Iptc.Application2.Keywords"},
{"Instructions", "Iptc.Application2.SpecialInstructions"},
{"DateCreated", "Iptc.Application2.DateCreated"},
{"Creator", "Iptc.Application2.Byline"},
{"CreatorJobTitle", "Iptc.Application2.BylineTitle"},
{"City", "Iptc.Application2.City"},
{"Province", "Iptc.Application2.ProvinceState"},
{"Country", "Iptc.Application2.CountryName"},
{"TransReference", "Iptc.Application2.TransmissionReference"},
{"Headline", "Iptc.Application2.Headline"},
{"Credit", "Iptc.Application2.Credit"},
{"Source", "Iptc.Application2.Source"},
{"Copyright", "Iptc.Application2.Copyright"},
{"Caption", "Iptc.Application2.Caption"},
{"CaptionWriter", "Iptc.Application2.Writer"}
};
} // namespace
@ -5247,23 +5220,6 @@ Glib::ustring RAWParams::getFlatFieldBlurTypeString(FlatFieldBlurType type)
}
MetaDataParams::MetaDataParams():
mode(MetaDataParams::TUNNEL),
exifKeys{"ALL"}
{
}
bool MetaDataParams::operator==(const MetaDataParams &other) const
{
return mode == other.mode
&& exifKeys == other.exifKeys;
}
bool MetaDataParams::operator!=(const MetaDataParams &other) const
{
return !(*this == other);
}
FilmNegativeParams::FilmNegativeParams() :
enabled(false),
redRatio(1.36),
@ -5316,6 +5272,90 @@ bool FilmNegativeParams::operator !=(const FilmNegativeParams& other) const
return !(*this == other);
}
namespace {
const std::map<std::string, std::string> exif_keys = {
{"Copyright", "Exif.Image.Copyright"},
{"Artist", "Exif.Image.Artist"},
{"ImageDescription", "Exif.Image.ImageDescription"},
{"Exif.UserComment", "Exif.Photo.UserComment"},
{"ISOSpeed", "Exif.Photo.ISOSpeedRatings"},
{"FNumber", "Exif.Photo.FNumber"},
{"ShutterSpeed", "Exif.Photo.ExposureTime"},
{"FocalLength", "Exif.Photo.FocalLength"},
{"ExpComp", "Exif.Photo.ExposureBiasValue"},
{"Flash", "Exif.Photo.Flash"},
{"Make", "Exif.Image.Make"},
{"Model", "Exif.Image.Model"},
{"Lens", "Exif.Photo.LensModel"},
{"DateTime", "Exif.Photo.DateTimeOriginal"}
};
const std::map<std::string, std::string> iptc_keys = {
{"Title", "Iptc.Application2.ObjectName"},
{"Category", "Iptc.Application2.Category"},
{"SupplementalCategories", "Iptc.Application2.SuppCategory"},
{"Keywords", "Iptc.Application2.Keywords"},
{"Instructions", "Iptc.Application2.SpecialInstructions"},
{"DateCreated", "Iptc.Application2.DateCreated"},
{"Creator", "Iptc.Application2.Byline"},
{"CreatorJobTitle", "Iptc.Application2.BylineTitle"},
{"City", "Iptc.Application2.City"},
{"Province", "Iptc.Application2.ProvinceState"},
{"Country", "Iptc.Application2.CountryName"},
{"TransReference", "Iptc.Application2.TransmissionReference"},
{"Headline", "Iptc.Application2.Headline"},
{"Credit", "Iptc.Application2.Credit"},
{"Source", "Iptc.Application2.Source"},
{"Copyright", "Iptc.Application2.Copyright"},
{"Caption", "Iptc.Application2.Caption"},
{"CaptionWriter", "Iptc.Application2.Writer"}
};
} // namespace
std::vector<std::string> MetaDataParams::basicExifKeys = {
"Exif.Image.Copyright",
"Exif.Image.Artist",
"Exif.Image.ImageDescription",
"Exif.Photo.UserComment",
"Exif.Image.Make",
"Exif.Image.Model",
"Exif.Photo.LensModel",
"Exif.Photo.FNumber",
"Exif.Photo.ExposureTime",
"Exif.Photo.FocalLength",
"Exif.Photo.ISOSpeedRatings",
"Exif.Photo.ExposureBiasValue",
"Exif.Photo.Flash",
"Exif.Photo.DateTimeOriginal"
};
MetaDataParams::MetaDataParams():
mode(MetaDataParams::TUNNEL),
exifKeys{"*"},
exif{},
iptc{}
{
}
bool MetaDataParams::operator==(const MetaDataParams &other) const
{
return mode == other.mode
&& exifKeys == other.exifKeys
&& exif == other.exif
&& iptc == other.iptc;
}
bool MetaDataParams::operator!=(const MetaDataParams &other) const
{
return !(*this == other);
}
ProcParams::ProcParams()
{
setDefaults();
@ -5414,8 +5454,8 @@ void ProcParams::setDefaults()
raw = {};
metadata = {};
exif.clear();
iptc.clear();
//exif.clear();
//iptc.clear();
// -1 means that there's no pp3 data with rank yet. In this case, the
// embedded Rating metadata should take precedence. -1 should never be
@ -6876,10 +6916,10 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
for (auto &p : exif_keys) {
m[p.second] = p.first;
}
for (ExifPairs::const_iterator i = exif.begin(); i != exif.end(); ++i) {
auto it = m.find(i->first);
for (auto &p : metadata.exif) {
auto it = m.find(p.first);
if (it != m.end()) {
keyFile.set_string("Exif", it->second, i->second);
keyFile.set_string("Exif", it->second, p.second);
}
}
}
@ -6890,10 +6930,10 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo
for (auto &p : iptc_keys) {
m[p.second] = p.first;
}
for (IPTCPairs::const_iterator i = iptc.begin(); i != iptc.end(); ++i) {
auto it = m.find(i->first);
for (auto &p : metadata.iptc) {
auto it = m.find(p.first);
if (it != m.end()) {
Glib::ArrayHandle<Glib::ustring> values = i->second;
Glib::ArrayHandle<Glib::ustring> values = p.second;
keyFile.set_string_list("IPTC", it->second, values);
}
}
@ -9383,7 +9423,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
for (const auto& key : keyFile.get_keys("Exif")) {
auto it = exif_keys.find(key);
if (it != exif_keys.end()) {
exif[it->second] = keyFile.get_string("Exif", key);
metadata.exif[it->second] = keyFile.get_string("Exif", key);
if (pedited) {
pedited->exif = true;
@ -9413,16 +9453,16 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited)
}
auto kk = it->second;
const IPTCPairs::iterator element = iptc.find(kk);
const IPTCPairs::iterator element = metadata.iptc.find(kk);
if (element != iptc.end()) {
if (element != metadata.iptc.end()) {
// it already exist so we cleanup the values
element->second.clear();
}
// TODO: look out if merging Keywords and SupplementalCategories from the procparams chain would be interesting
for (const auto& currLoadedTagValue : keyFile.get_string_list("IPTC", key)) {
iptc[kk].push_back(currLoadedTagValue);
metadata.iptc[kk].push_back(currLoadedTagValue);
}
if (pedited) {
@ -9500,8 +9540,6 @@ bool ProcParams::operator ==(const ProcParams& other) const
&& rgbCurves == other.rgbCurves
&& colorToning == other.colorToning
&& metadata == other.metadata
&& exif == other.exif
&& iptc == other.iptc
&& dehaze == other.dehaze
&& filmNegative == other.filmNegative;
}

View File

@ -1718,25 +1718,6 @@ struct ColorManagementParams {
bool operator !=(const ColorManagementParams& other) const;
};
/**
* Parameters for metadata handling
*/
struct MetaDataParams {
enum Mode {
TUNNEL,
EDIT,
STRIP
};
Mode mode;
std::vector<std::string> exifKeys;
MetaDataParams();
bool operator ==(const MetaDataParams &other) const;
bool operator !=(const MetaDataParams &other) const;
};
/**
* Minimal wrapper allowing forward declaration for representing a key/value for the exif metadata information
*/
@ -1841,6 +1822,29 @@ private:
std::map<Glib::ustring, std::vector<Glib::ustring>> pairs;
};
/**
* Parameters for metadata handling
*/
struct MetaDataParams {
enum Mode {
TUNNEL,
EDIT,
STRIP
};
Mode mode;
std::vector<std::string> exifKeys;
ExifPairs exif;
IPTCPairs iptc;
MetaDataParams();
bool operator ==(const MetaDataParams &other) const;
bool operator !=(const MetaDataParams &other) const;
static std::vector<std::string> basicExifKeys;
};
struct WaveletParams {
std::vector<double> ccwcurve;
std::vector<double> wavdenoise;
@ -2355,8 +2359,8 @@ public:
int ppVersion; ///< Version of the PP file from which the parameters have been read
MetaDataParams metadata; ///< Metadata parameters
ExifPairs exif; ///< List of modifications appplied on the exif tags of the input image
IPTCPairs iptc; ///< The IPTC tags and values to be saved to the output image
// ExifPairs exif; ///< List of modifications appplied on the exif tags of the input image
// IPTCPairs iptc; ///< The IPTC tags and values to be saved to the output image
/**
* The constructor only sets the hand-wired defaults.

View File

@ -1737,9 +1737,9 @@ private:
readyImg->setMetadata(std::move(info));
break;
case MetaDataParams::EDIT:
info.setExif(params.exif);
info.setIptc(params.iptc);
if (!(params.metadata.exifKeys.size() == 1 && params.metadata.exifKeys[0] == "ALL")) {
info.setExif(params.metadata.exif);
info.setIptc(params.metadata.iptc);
if (!(params.metadata.exifKeys.size() == 1 && params.metadata.exifKeys[0] == "*")) {
info.setExifKeys(&(params.metadata.exifKeys));
}
readyImg->setMetadata(std::move(info));

View File

@ -34,15 +34,12 @@ using namespace rtengine::procparams;
ExifPanel::ExifPanel() :
idata(nullptr),
changeList(new rtengine::procparams::ExifPairs),
defChangeList(new rtengine::procparams::ExifPairs),
editableTags{
{"Exif.Photo.UserComment", "User Comment"},
{"Exif.Image.Artist", "Artist"},
{"Exif.Image.Copyright", "Copyright"},
{"Exif.Image.ImageDescription", "Image Description"},
{ "Exif.Photo.LensModel", "Lens Model" }
}
defChangeList(new rtengine::procparams::ExifPairs)
{
for (auto &k : MetaDataParams::basicExifKeys) {
editableTags.push_back(std::make_pair(k, ""));
}
set_orientation(Gtk::ORIENTATION_VERTICAL);
exifTree = Gtk::manage (new Gtk::TreeView());
scrolledWindow = Gtk::manage (new Gtk::ScrolledWindow());
@ -60,11 +57,29 @@ ExifPanel::ExifPanel() :
exifTreeModel = Gtk::TreeStore::create(exifColumns);
exifTree->set_model(exifTreeModel);
exifTree->set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_NONE);
//exifTree->set_show_expanders(false);
exifTree->set_show_expanders(false);
exifTree->set_tooltip_column(0);
exifTree->set_enable_search(false);
keepicon = RTImage::createPixbufFromFile ("tick-small.png");
editicon = RTImage::createPixbufFromFile("add-small.png");
//keepicon = RTImage::createPixbufFromFile("tick-small.png");
editicon = RTImage::createPixbufFromFile("edit-small.png");
open_icon_ = RTImage::createPixbufFromFile("expander-open-small.png");
closed_icon_ = RTImage::createPixbufFromFile("expander-closed-small.png");
exif_active_renderer_.property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
exif_active_renderer_.signal_toggled().connect(sigc::mem_fun(this, &ExifPanel::onKeyActiveToggled));
exif_active_column_.pack_start(exif_active_renderer_);
exif_active_column_.set_cell_data_func(exif_active_renderer_, sigc::mem_fun(this, &ExifPanel::setKeyActive));
exifTree->append_column(exif_active_column_);
// {
// Gtk::TreeView::Column *c = Gtk::manage(new Gtk::TreeView::Column(""));
// Gtk::CellRendererPixbuf *pb = Gtk::manage(new Gtk::CellRendererPixbuf());
// c->pack_start(*pb, false);
// c->add_attribute(*pb, "pixbuf", exifColumns.expander_icon);
// exifTree->append_column(*c);
// }
Gtk::TreeView::Column *viewcol = Gtk::manage (new Gtk::TreeView::Column ("Field Name"));
Gtk::CellRendererPixbuf* render_pb = Gtk::manage (new Gtk::CellRendererPixbuf ());
@ -85,15 +100,8 @@ ExifPanel::ExifPanel() :
render_pb->property_yalign() = 0;
render_txt->property_yalign() = 0;
exif_active_renderer_.property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
exif_active_renderer_.signal_toggled().connect(sigc::mem_fun(this, &ExifPanel::onKeyActiveToggled));
exif_active_column_.pack_start(exif_active_renderer_);
exif_active_column_.set_cell_data_func(exif_active_renderer_, sigc::mem_fun(this, &ExifPanel::setKeyActive));
exifTree->append_column(exif_active_column_);
exifTree->append_column(*viewcol);
exifTree->set_expander_column(*viewcol);
//exifTree->set_expander_column(*viewcol);
Gtk::TreeView::Column *viewcolv = Gtk::manage(new Gtk::TreeView::Column ("Value"));
Gtk::CellRendererText *render_txtv = Gtk::manage(new Gtk::CellRendererText());
@ -131,14 +139,14 @@ ExifPanel::ExifPanel() :
activate_all_ = addbtn("EXIFPANEL_ACTIVATE_ALL_HINT", "tick.png");
activate_none_ = addbtn("EXIFPANEL_ACTIVATE_NONE_HINT", "box.png");
add = addbtn("EXIFPANEL_ADDEDIT", "add.png");
add = addbtn("EXIFPANEL_ADDEDIT", "edit.png");
reset = addbtn("EXIFPANEL_RESETHINT", "undo.png", "redo.png");
resetAll = addbtn("EXIFPANEL_RESETALLHINT", "undo-all.png", "redo-all.png");
pack_end (*buttons1, Gtk::PACK_SHRINK);
exifTree->get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ExifPanel::exifSelectionChanged));
// exifTree->signal_row_activated().connect (sigc::mem_fun (*this, &ExifPanel::row_activated));
exifTree->signal_row_activated().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowActivated));
reset->signal_clicked().connect ( sigc::mem_fun (*this, &ExifPanel::resetPressed) );
resetAll->signal_clicked().connect ( sigc::mem_fun (*this, &ExifPanel::resetAllPressed) );
@ -146,6 +154,10 @@ ExifPanel::ExifPanel() :
activate_all_->signal_clicked().connect(sigc::mem_fun(*this, &ExifPanel::activateAllPressed));
activate_none_->signal_clicked().connect(sigc::mem_fun(*this, &ExifPanel::activateNonePressed));
exifTree->signal_button_press_event().connect_notify(sigc::mem_fun(*this, &ExifPanel::onExifTreeClick));
exifTree->signal_row_expanded().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowExpanded));
exifTree->signal_row_collapsed().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowCollapsed));
show_all ();
}
@ -157,7 +169,7 @@ void ExifPanel::read (const ProcParams* pp, const ParamsEdited* pedited)
{
disableListener();
*changeList = pp->exif;
*changeList = pp->metadata.exif;
initial_active_keys_.clear();
initial_active_keys_.insert(pp->metadata.exifKeys.begin(), pp->metadata.exifKeys.end());
cur_active_keys_ = initial_active_keys_;
@ -170,54 +182,15 @@ void ExifPanel::read (const ProcParams* pp, const ParamsEdited* pedited)
void ExifPanel::write (ProcParams* pp, ParamsEdited* pedited)
{
pp->exif = *changeList;
std::unordered_set<std::string> prev;
bool all_active = (cur_active_keys_.size() == 1 && *(cur_active_keys_.begin()) == "ALL");
if (!all_active) {
prev = cur_active_keys_;
}
pp->metadata.exifKeys.clear();
bool none_active = true;
auto root = exifTreeModel->children();
// for (auto &entry : root->children()) {
// Glib::ustring key = entry[exifColumns.key];
// prev.erase(key);
// if (entry[exifColumns.active]) {
// pp->metadata.exifKeys.push_back(key);
// none_active = false;
// } else {
// all_active = false;
// }
// }
for (auto &group : root->children()) {
for (auto &entry : group.children()) {
std::string key = entry[exifColumns.key];
prev.erase(key);
if (entry[exifColumns.active]) {
pp->metadata.exifKeys.push_back(key);
none_active = false;
} else {
all_active = false;
}
}
}
if (all_active) {
pp->metadata.exifKeys = { "ALL" };
} else if (!none_active) {
pp->metadata.exifKeys.insert(pp->metadata.exifKeys.end(), prev.begin(), prev.end());
}
pp->metadata.exif = *changeList;
cur_active_keys_ = get_active_keys();
pp->metadata.exifKeys.assign(cur_active_keys_.begin(), cur_active_keys_.end());
std::sort(pp->metadata.exifKeys.begin(), pp->metadata.exifKeys.end());
}
void ExifPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited)
{
*defChangeList = defParams->exif;
*defChangeList = defParams->metadata.exif;
}
void ExifPanel::setImageData (const FramesMetaData* id)
@ -240,11 +213,13 @@ void ExifPanel::addTag(const std::string &key, const std::pair<Glib::ustring, Gl
{
auto root = exifTreeModel->children();
for (auto it = root.rbegin(), end = root.rend(); it != end; ++it) {
auto row = *it;
// for (auto it = root.rbegin(), end = root.rend(); it != end; ++it) {
// auto row = *it;
for (auto &row : root) {
// auto row = *it;
std::string key = row[exifColumns.key];
if (row[exifColumns.is_group] && key == label.first) {
return it->children();
return row./*it->*/children();
}
}
auto it = exifTreeModel->append(root);
@ -253,7 +228,7 @@ void ExifPanel::addTag(const std::string &key, const std::pair<Glib::ustring, Gl
row[exifColumns.editable] = false;
row[exifColumns.edited] = false;
row[exifColumns.key] = label.first;
row[exifColumns.label] = "<i>" + label.first + "</i>";
row[exifColumns.label] = "<b>" + label.first + "</b>";
row[exifColumns.value_nopango] = "";
row[exifColumns.value] = "";
row[exifColumns.is_group] = true;
@ -275,13 +250,13 @@ void ExifPanel::addTag(const std::string &key, const std::pair<Glib::ustring, Gl
row[exifColumns.label] = escapeHtmlChars(label.second);
row[exifColumns.value] = value;//escapeHtmlChars(value);
bool active = (cur_active_keys_.size() == 1 && *(cur_active_keys_.begin()) == "ALL") || cur_active_keys_.find(key) != cur_active_keys_.end();
bool active = all_keys_active() || cur_active_keys_.find(key) != cur_active_keys_.end();
row[exifColumns.active] = active;
if (edited) {
row[exifColumns.icon] = editicon;
} else if (editable) {
row[exifColumns.icon] = keepicon;
// } else if (editable) {
// row[exifColumns.icon] = keepicon;
}
}
@ -318,15 +293,16 @@ void ExifPanel::refreshTags()
pos = s.find('.');
if (pos != std::string::npos) {
g = s.substr(0, pos);
s = s.substr(pos+1);
// s = s.substr(pos+1);
}
s = tag.tagLabel();
return std::make_pair(g, Glib::ustring(s));
};
try {
rtengine::Exiv2Metadata meta(fn);
meta.load();
auto& exif = meta.exifData();
Exiv2::ExifData exif = meta.exifData();
const auto to_value =
[&](Exiv2::Exifdatum &tag) -> Glib::ustring
@ -338,6 +314,9 @@ void ExifPanel::refreshTags()
return "<i>(Not shown)</i>";
};
if (const rtengine::FramesData *fd = dynamic_cast<const rtengine::FramesData *>(idata)) {
fd->fillBasicTags(exif);
}
for (const auto& p : *changeList) {
try {
@ -346,12 +325,18 @@ void ExifPanel::refreshTags()
}
}
for (const auto& p : editableTags) {
const auto pos = exif.findKey(Exiv2::ExifKey(p.first));
for (auto& p : editableTags) {
Exiv2::ExifKey k(p.first);
const auto pos = exif.findKey(k);
bool edited = false;
Glib::ustring value = "";
auto lbl = std::make_pair(M("EXIFPANEL_BASIC_GROUP"), k.tagLabel());
p.second = k.tagLabel();
if (pos != exif.end() && pos->size()) {
const bool edited = changeList->find(pos->key()) != changeList->end();
addTag(pos->key(), to_label(*pos), pos->print(&exif), true, edited);
edited = changeList->find(pos->key()) != changeList->end();
value = pos->print(&exif);
}
addTag(p.first, lbl, value, true, edited);
}
std::set<std::string> keyset;
for (const auto& tag : exif) {
@ -406,6 +391,7 @@ void ExifPanel::resetIt(const Gtk::TreeModel::const_iterator& iter)
void ExifPanel::resetPressed()
{
cur_active_keys_ = get_active_keys();
std::vector<Gtk::TreeModel::Path> sel = exifTree->get_selection()->get_selected_rows();
@ -490,10 +476,13 @@ void ExifPanel::addPressed ()
hb2->show ();
if (dialog->run () == Gtk::RESPONSE_OK) {
cur_active_keys_ = get_active_keys();
auto key = editableTags[tcombo->get_active_row_number()].first;
const auto value = ventry->get_text();
(*changeList)[key] = value;
cur_active_keys_.insert(key);
if (!all_keys_active()) {
cur_active_keys_.insert(key);
}
refreshTags();
notifyListener();
}
@ -556,6 +545,11 @@ void ExifPanel::onKeyActiveToggled(const Glib::ustring &path)
for (auto &c : row.children()) {
c[exifColumns.active] = b;
}
} else if (!b) {
it = row.parent();
if (it) {
(*it)[exifColumns.active] = b;
}
}
notifyListener();
}
@ -568,3 +562,78 @@ void ExifPanel::setKeyActive(Gtk::CellRenderer *renderer, const Gtk::TreeModel::
static_cast<Gtk::CellRendererToggle *>(renderer)->set_active(row[exifColumns.active]);
}
bool ExifPanel::all_keys_active() const
{
return (cur_active_keys_.size() == 1 && *(cur_active_keys_.begin()) == "*");
}
std::unordered_set<std::string> ExifPanel::get_active_keys() const
{
bool all_active = true;
std::unordered_set<std::string> ret;
auto root = exifTreeModel->children();
for (auto &group : root->children()) {
for (auto &entry : group.children()) {
std::string key = entry[exifColumns.key];
if (entry[exifColumns.active]) {
ret.insert(key);
} else {
all_active = false;
}
}
}
if (all_active) {
ret.clear();
ret.insert("*");
}
return ret;
}
void ExifPanel::onExifTreeClick(GdkEventButton *event)
{
Gtk::TreeModel::Path pth;
Gtk::TreeViewColumn *col;
int cell_x;
int cell_y;
if (exifTree->get_path_at_pos(event->x, event->y, pth, col, cell_x, cell_y) && col == exifTree->get_column(1) && cell_x <= 22) {
auto it = exifTreeModel->get_iter(pth);
auto row = *it;
if (row[exifColumns.is_group]) {
if (exifTree->row_expanded(pth)) {
exifTree->collapse_row(pth);
} else {
exifTree->expand_row(pth, false);
}
}
}
}
void ExifPanel::onExifRowExpanded(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path)
{
auto row = *it;
if (row[exifColumns.is_group]) {
row[exifColumns.icon] = open_icon_;
}
}
void ExifPanel::onExifRowCollapsed(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path)
{
auto row = *it;
if (row[exifColumns.is_group]) {
row[exifColumns.icon] = closed_icon_;
}
}
void ExifPanel::onExifRowActivated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column)
{
auto it = exifTreeModel->get_iter(path);
auto row = *it;
if (row[exifColumns.editable]) {
addPressed();
}
}

View File

@ -50,7 +50,8 @@ private:
class ExifColumns : public Gtk::TreeModelColumnRecord
{
public:
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > icon;
// Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> expander_icon;
Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf>> icon;
Gtk::TreeModelColumn<std::string> key;
Gtk::TreeModelColumn<Glib::ustring> label;
Gtk::TreeModelColumn<Glib::ustring> value;
@ -71,10 +72,13 @@ private:
add(editable);
add(active);
add(is_group);
// add(expander_icon);
}
};
Glib::RefPtr<Gdk::Pixbuf> keepicon;
//Glib::RefPtr<Gdk::Pixbuf> keepicon;
Glib::RefPtr<Gdk::Pixbuf> editicon;
Glib::RefPtr<Gdk::Pixbuf> open_icon_;
Glib::RefPtr<Gdk::Pixbuf> closed_icon_;
ExifColumns exifColumns;
Gtk::TreeView* exifTree;
@ -90,7 +94,7 @@ private:
Gtk::CellRendererToggle exif_active_renderer_;
Gtk::TreeView::Column exif_active_column_;
const std::vector<std::pair<std::string, Glib::ustring>> editableTags;
std::vector<std::pair<std::string, Glib::ustring>> editableTags;
std::unordered_set<std::string> initial_active_keys_;
std::unordered_set<std::string> cur_active_keys_;
@ -106,7 +110,14 @@ private:
void setKeyActive(Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &it);
void onKeyActiveToggled(const Glib::ustring &path);
bool all_keys_active() const;
std::unordered_set<std::string> get_active_keys() const;
void onExifTreeClick(GdkEventButton *event);
void onExifRowExpanded(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path);
void onExifRowCollapsed(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path);
void onExifRowActivated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column);
public:
ExifPanel ();

View File

@ -467,8 +467,8 @@ void IPTCPanel::read (const ProcParams* pp, const ParamsEdited* pedited)
disableListener ();
changeList->clear();
if (!pp->iptc.empty()) {
*changeList = pp->iptc;
if (!pp->metadata.iptc.empty()) {
*changeList = pp->metadata.iptc;
} else {
*changeList = *embeddedData;
}
@ -480,13 +480,13 @@ void IPTCPanel::read (const ProcParams* pp, const ParamsEdited* pedited)
void IPTCPanel::write (ProcParams* pp, ParamsEdited* pedited)
{
pp->iptc = *changeList;
pp->metadata.iptc = *changeList;
}
void IPTCPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited)
{
*defChangeList = defParams->iptc;
*defChangeList = defParams->metadata.iptc;
}
void IPTCPanel::setImageData (const FramesMetaData* id)

View File

@ -6612,15 +6612,15 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng
// Exif changes are added to the existing ones
if (exif) {
for (procparams::ExifPairs::const_iterator i = mods.exif.begin(); i != mods.exif.end(); ++i) {
toEdit.exif[i->first] = i->second;
for (procparams::ExifPairs::const_iterator i = mods.metadata.exif.begin(); i != mods.metadata.exif.end(); ++i) {
toEdit.metadata.exif[i->first] = i->second;
}
}
// IPTC changes are added to the existing ones
if (iptc) {
for (procparams::IPTCPairs::const_iterator i = mods.iptc.begin(); i != mods.iptc.end(); ++i) {
toEdit.iptc[i->first] = i->second;
for (procparams::IPTCPairs::const_iterator i = mods.metadata.iptc.begin(); i != mods.metadata.iptc.end(); ++i) {
toEdit.metadata.iptc[i->first] = i->second;
}
}
}

View File

@ -1215,7 +1215,7 @@ void Thumbnail::saveMetadata()
return;
}
if (pparams->exif.empty() && pparams->iptc.empty()) {
if (pparams->metadata.exif.empty() && pparams->metadata.iptc.empty()) {
return;
}
@ -1224,8 +1224,8 @@ void Thumbnail::saveMetadata()
auto xmp = rtengine::Exiv2Metadata::getXmpSidecar(fname);
rtengine::Exiv2Metadata meta;
meta.xmpData() = std::move(xmp);
meta.setExif(pparams->exif);
meta.setIptc(pparams->iptc);
meta.setExif(pparams->metadata.exif);
meta.setIptc(pparams->metadata.iptc);
meta.saveToXmp(fn);
if (options.rtSettings.verbose) {
std::cout << "saved edited metadata for " << fname << " to "

View File

@ -141,7 +141,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
addfavoritePanel (detailsPanel, dehaze);
addfavoritePanel (advancedPanel, wavelet);
addfavoritePanel(locallabPanel, locallab);
addfavoritePanel (transformPanel, crop);
addfavoritePanel (transformPanel, resize);
addPanel (resize->getPackBox(), prsharpening, 2);
@ -187,7 +187,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit
transformPanelSW = Gtk::manage (new MyScrolledWindow ());
rawPanelSW = Gtk::manage (new MyScrolledWindow ());
advancedPanelSW = Gtk::manage (new MyScrolledWindow ());
locallabPanelSW = Gtk::manage(new MyScrolledWindow());
locallabPanelSW = Gtk::manage(new MyScrolledWindow());
// load panel endings
for (int i = 0; i < 8; i++) {
@ -585,8 +585,8 @@ void ToolPanelCoordinator::profileChange(
// Reset IPTC values when switching procparams from the History
if (event == rtengine::EvHistoryBrowsed) {
mergedParams->iptc.clear();
mergedParams->exif.clear();
mergedParams->metadata.iptc.clear();
mergedParams->metadata.exif.clear();
}
// And apply the partial profile nparams to mergedParams