[niepce] Issue #2 - engine: use Exiv2 to import RAW file metadata
- From: Hubert Figuière <hub src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [niepce] Issue #2 - engine: use Exiv2 to import RAW file metadata
- Date: Fri, 21 Dec 2018 16:48:25 +0000 (UTC)
commit 68564dbbd1a34bd6bda98e5e7bf32ced1135f005
Author: Hubert Figuière <hub figuiere net>
Date: Thu Nov 22 00:47:00 2018 -0500
Issue #2 - engine: use Exiv2 to import RAW file metadata
- add Exif to XmpDate
- bump exempi dependency
https://gitlab.gnome.org/GNOME/niepce/issues/2
Cargo.lock | 31 +++++-----
Cargo.toml | 3 +-
src/Makefile.am | 1 +
src/engine/db/libmetadata.rs | 4 +-
src/engine/db/library.rs | 11 ++--
src/fwk/base/date.rs | 13 +---
src/fwk/utils/exempi.rs | 140 ++++++++++++++++++++++++++++++++++---------
src/fwk/utils/exiv2.rs | 132 ++++++++++++++++++++++++++++++++++++++++
src/fwk/utils/mod.rs | 3 +-
src/lib.rs | 2 +
10 files changed, 276 insertions(+), 64 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 08314d4..606a47a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -45,7 +45,7 @@ dependencies = [
"clang-sys 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -175,17 +175,17 @@ dependencies = [
[[package]]
name = "exempi"
version = "2.5.0"
-source =
"git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4#065721c48de36cf8f3e3d89691e3891a3d3230cc"
+source =
"git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c#d5c013caf38351e69bca13a74cb0fd727d752ef4"
dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "exempi-sys 2.4.1 (git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4)",
+ "exempi-sys 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "exempi-sys"
-version = "2.4.1"
-source =
"git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4#065721c48de36cf8f3e3d89691e3891a3d3230cc"
+version = "2.5.0"
+source =
"git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c#d5c013caf38351e69bca13a74cb0fd727d752ef4"
dependencies = [
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -295,7 +295,7 @@ dependencies = [
"glib 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -318,7 +318,7 @@ dependencies = [
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -365,7 +365,7 @@ dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -411,7 +411,7 @@ dependencies = [
[[package]]
name = "lazy_static"
-version = "1.0.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -489,7 +489,7 @@ dependencies = [
"bindgen 0.37.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cbindgen 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4)",
+ "exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c)",
"gettext-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gio-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -497,6 +497,7 @@ dependencies = [
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rexiv2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -562,7 +563,7 @@ dependencies = [
"glib 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gobject-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
"pango-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -834,7 +835,7 @@ name = "thread_local"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -973,8 +974,8 @@ dependencies = [
"checksum clap 2.31.1 (registry+https://github.com/rust-lang/crates.io-index)" =
"5dc18f6f4005132120d9711636b32c46a233fad94df6217fa1d81c5e97a9f200"
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" =
"09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" =
"0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a"
-"checksum exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4)" = "<none>"
-"checksum exempi-sys 2.4.1 (git+https://github.com/hfiguiere/exempi-rs.git?rev=065721c4)" = "<none>"
+"checksum exempi 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c)" = "<none>"
+"checksum exempi-sys 2.5.0 (git+https://github.com/hfiguiere/exempi-rs.git?rev=d5c013c)" = "<none>"
"checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" =
"2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" =
"3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
@@ -995,7 +996,7 @@ dependencies = [
"checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" =
"0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" =
"8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" =
"7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
+"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" =
"f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
"checksum libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"9c3ad660d7cb8c5822cd83d10897b0f1f1526792737a179e73896152f85b88c2"
"checksum libsqlite3-sys 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" =
"d3711dfd91a1081d2458ad2d06ea30a8755256e74038be2ad927d94e1c955ca8"
diff --git a/Cargo.toml b/Cargo.toml
index 5870b21..068e3b0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ build = "build.rs"
[dependencies]
chrono = "0.4.0"
-exempi = { version = "2.5.0", git = "https://github.com/hfiguiere/exempi-rs.git", rev="065721c4" }
+exempi = { version = "2.5.0", git = "https://github.com/hfiguiere/exempi-rs.git", rev="d5c013c" }
gettext-rs = "0.3.0"
glib-sys = "0.7.0"
glib = "0.6.0"
@@ -14,6 +14,7 @@ gio-sys = "0.7.0"
gio = "0.5.0"
gtk-sys = { version = "0.7.0", features = ["v3_16"] }
gtk = "0.5.0"
+lazy_static = "^1.2.0"
libc = "0.2.39"
rexiv2 = "^0.6.0"
rusqlite = { version = "0.14.0", features = ["functions"] }
diff --git a/src/Makefile.am b/src/Makefile.am
index 93306b0..8949ab0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,7 @@ RUST_SOURCES = \
@top_srcdir@/src/fwk/toolkit/mimetype.rs \
@top_srcdir@/src/fwk/toolkit/mod.rs \
@top_srcdir@/src/fwk/utils/exempi.rs \
+ @top_srcdir@/src/fwk/utils/exiv2.rs \
@top_srcdir@/src/fwk/utils/files.rs \
@top_srcdir@/src/fwk/utils/mod.rs \
@top_srcdir@/src/lib.rs \
diff --git a/src/engine/db/libmetadata.rs b/src/engine/db/libmetadata.rs
index d78f09a..1c99a52 100644
--- a/src/engine/db/libmetadata.rs
+++ b/src/engine/db/libmetadata.rs
@@ -117,9 +117,9 @@ impl LibMetadata {
PropertyValue::String(ref s) => {
if s.is_empty() {
return self.xmp.xmp.delete_property(&ix.ns, &ix.property).is_ok();
- } else if !self.xmp
+ } else if self.xmp
.xmp
- .set_property(&ix.ns, &ix.property, s, exempi::PROP_NONE).is_ok()
+ .set_property(&ix.ns, &ix.property, s, exempi::PROP_NONE).is_err()
{
if exempi::get_error() == exempi::Error::BadXPath {
return self.xmp.xmp.set_localized_text(
diff --git a/src/engine/db/library.rs b/src/engine/db/library.rs
index 683b20f..1cc7064 100644
--- a/src/engine/db/library.rs
+++ b/src/engine/db/library.rs
@@ -630,14 +630,13 @@ impl Library {
// Until we get better metadata support for RAW files, we use the Exif reconcile
// from the sidecar JPEG to get the initial metadata.
let meta = if let Some(bundle) = bundle {
- match bundle.bundle_type() {
- libfile::FileType::RAW_JPEG => {
- fwk::XmpMeta::new_from_file(bundle.jpeg(), false)
- },
- _ => fwk::XmpMeta::new_from_file(file, file_type == libfile::FileType::RAW)
+ if bundle.bundle_type() == libfile::FileType::RAW_JPEG {
+ fwk::XmpMeta::new_from_file(bundle.jpeg(), false)
+ } else {
+ fwk::XmpMeta::new_from_file(file, false)
}
} else {
- fwk::XmpMeta::new_from_file(file, file_type == libfile::FileType::RAW)
+ fwk::XmpMeta::new_from_file(file, false)
};
if let Some(ref meta) = meta {
diff --git a/src/fwk/base/date.rs b/src/fwk/base/date.rs
index 0645047..10463bf 100644
--- a/src/fwk/base/date.rs
+++ b/src/fwk/base/date.rs
@@ -29,16 +29,9 @@ pub type Date = chrono::DateTime<chrono::Utc>;
pub fn xmp_date_from(d: &chrono::DateTime<chrono::Utc>) -> exempi::DateTime {
let mut xmp_date = exempi::DateTime::new();
- xmp_date.c.year = d.year();
- xmp_date.c.month = d.month() as i32;
- xmp_date.c.day = d.day() as i32;
- xmp_date.c.hour = d.hour() as i32;
- xmp_date.c.minute = d.minute() as i32;
- xmp_date.c.second = d.second() as i32;
- xmp_date.c.tz_sign = exempi::XmpTzSign::UTC;
- xmp_date.c.tz_hour = 0;
- xmp_date.c.tz_minute = 0;
- xmp_date.c.nano_second = 0;
+ xmp_date.set_date(d.year(), d.month() as i32, d.day() as i32);
+ xmp_date.set_time(d.hour() as i32, d.minute() as i32, d.second() as i32);
+ xmp_date.set_timezone(exempi::XmpTzSign::UTC, 0, 0);
xmp_date
}
diff --git a/src/fwk/utils/exempi.rs b/src/fwk/utils/exempi.rs
index 7ec415d..e303f17 100644
--- a/src/fwk/utils/exempi.rs
+++ b/src/fwk/utils/exempi.rs
@@ -1,7 +1,7 @@
/*
* niepce - fwk/utils/exempi.rs
*
- * Copyright (C) 2017 Hubert Figuière
+ * Copyright (C) 2017-2018 Hubert Figuière
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,17 +25,18 @@ use chrono::{DateTime, Utc};
use exempi;
use exempi::Xmp;
-static NIEPCE_XMP_NAMESPACE: &'static str = "http://xmlns.figuiere.net/ns/niepce/1.0";
-static NIEPCE_XMP_NS_PREFIX: &'static str = "niepce";
-static UFRAW_INTEROP_NAMESPACE: &'static str = "http://xmlns.figuiere.net/ns/ufraw_interop/1.0";
-static UFRAW_INTEROP_NS_PREFIX: &'static str = "ufrint";
+use super::exiv2;
+const NIEPCE_XMP_NAMESPACE: &str = "http://xmlns.figuiere.net/ns/niepce/1.0";
+const NIEPCE_XMP_NS_PREFIX: &str = "niepce";
+const UFRAW_INTEROP_NAMESPACE: &str = "http://xmlns.figuiere.net/ns/ufraw_interop/1.0";
+const UFRAW_INTEROP_NS_PREFIX: &str = "ufrint";
-pub static NS_TIFF: &'static str = "http://ns.adobe.com/tiff/1.0/";
-pub static NS_XAP: &'static str = "http://ns.adobe.com/xap/1.0/";
-pub static NS_EXIF: &'static str = "http://ns.adobe.com/exif/1.0/";
-pub static NS_DC: &'static str = "http://purl.org/dc/elements/1.1/";
-pub static NS_AUX: &'static str = "http://ns.adobe.com/exif/1.0/aux/";
+pub const NS_TIFF: &str = "http://ns.adobe.com/tiff/1.0/";
+pub const NS_XAP: &str = "http://ns.adobe.com/xap/1.0/";
+pub const NS_EXIF: &str = "http://ns.adobe.com/exif/1.0/";
+pub const NS_DC: &str = "http://purl.org/dc/elements/1.1/";
+pub const NS_AUX: &str = "http://ns.adobe.com/exif/1.0/aux/";
pub struct NsDef {
ns: String,
@@ -82,16 +83,21 @@ impl XmpMeta {
}
}
+ pub fn new_with_xmp(xmp: exempi::Xmp) -> XmpMeta {
+ XmpMeta {
+ xmp,
+ keywords: Vec::new(),
+ keywords_fetched: false,
+ }
+ }
+
pub fn new_from_file(file: &str, sidecar_only: bool) -> Option<XmpMeta> {
let mut meta: Option<XmpMeta> = None;
if !sidecar_only {
if let Ok(xmpfile) = exempi::XmpFile::open_new(file, exempi::OPEN_READ) {
- if let Ok(xmp) = xmpfile.get_new_xmp() {
- meta = Some(XmpMeta {
- xmp,
- keywords: Vec::new(),
- keywords_fetched: false
- });
+ meta = match xmpfile.get_new_xmp() {
+ Ok(xmp) => Some(Self::new_with_xmp(xmp)),
+ _ => exiv2::xmp_from_exiv2(file)
}
}
}
@@ -104,12 +110,8 @@ impl XmpMeta {
let mut sidecarcontent = String::new();
if sidecarfile.read_to_string(&mut sidecarcontent).is_ok() {
let mut xmp = exempi::Xmp::new();
- if let Ok(_) = xmp.parse(sidecarcontent.into_bytes().as_slice()) {
- sidecar_meta = Some(XmpMeta {
- xmp,
- keywords: Vec::new(),
- keywords_fetched: false
- });
+ if xmp.parse(sidecarcontent.into_bytes().as_slice()).is_ok() {
+ sidecar_meta = Some(Self::new_with_xmp(xmp));
}
}
}
@@ -147,7 +149,7 @@ impl XmpMeta {
return false;
}
if source_date > dest_date {
- dbg_out!("file meta is more recent than sidecar");
+ dbg_out!("source meta is more recent than sidecar");
return false;
}
@@ -169,11 +171,10 @@ impl XmpMeta {
continue;
}
- if !dest.xmp.has_property(schema.to_str(), name.to_str()) {
- if dest.xmp.set_property(schema.to_str(), name.to_str(),
+ if !dest.xmp.has_property(schema.to_str(), name.to_str()) &&
+ dest.xmp.set_property(schema.to_str(), name.to_str(),
value.to_str(), exempi::PROP_NONE).is_err() {
err_out!("Can set property {}", name);
- }
}
}
}
@@ -239,12 +240,22 @@ impl XmpMeta {
Some(String::from(xmpstring.to_str()))
}
- /// Get the date property and return a DateTime<Utc> parsed
+ /// Get the date property and return a Option<DateTime<Utc>> parsed
/// from the string value.
pub fn get_date_property(&self, ns: &str, property: &str) -> Option<DateTime<Utc>> {
let mut flags: exempi::PropFlags = exempi::PropFlags::empty();
- let xmpstring = try_opt!(self.xmp.get_property(ns, property, &mut flags).ok());
- let date = try_opt!(DateTime::parse_from_rfc3339(xmpstring.to_str()).ok());
+ let property = self.xmp.get_property(ns, property, &mut flags);
+ if property.is_err() {
+ err_out!("Error getting date property {:?}", property.err());
+ return None;
+ }
+ let xmpstring = property.unwrap();
+ let parsed = DateTime::parse_from_rfc3339(xmpstring.to_str());
+ if parsed.is_err() {
+ err_out!("Error parsing property value '{}': {:?}", xmpstring, parsed.err());
+ return None;
+ }
+ let date = parsed.unwrap();
Some(date.with_timezone(&Utc))
}
@@ -324,6 +335,57 @@ pub fn gps_coord_from_xmp(xmps: &str) -> Option<f64> {
Some(deg)
}
+/// Get and XMP date from an Exif date string
+/// XXX Currently assume it is UTC.
+pub fn xmp_date_from_exif(d: &str) -> Option<exempi::DateTime> {
+ let v: Vec<&str> = d.split(' ').collect();
+ if v.len() != 2 {
+ err_out!("Space split failed {:?}", v);
+ return None;
+ }
+
+ let ymd: Vec<&str> = v[0].split(':').collect();
+ if ymd.len() != 3 {
+ err_out!("ymd split failed {:?}", ymd);
+ return None;
+ }
+ let year = try_opt!(i32::from_str_radix(ymd[0], 10).ok());
+ let month = try_opt!(i32::from_str_radix(ymd[1], 10).ok());
+ if month < 1 || month > 12 {
+ return None;
+ }
+ let day = try_opt!(i32::from_str_radix(ymd[2], 10).ok());
+ if day < 1 || day > 31 {
+ return None;
+ }
+ let hms: Vec<&str> = v[1].split(':').collect();
+ if hms.len() != 3 {
+ err_out!("hms split failed {:?}", hms);
+ return None;
+ }
+ let hour = try_opt!(i32::from_str_radix(hms[0], 10).ok());
+ if hour < 0 || hour > 23 {
+ return None;
+ }
+ let min = try_opt!(i32::from_str_radix(hms[1], 10).ok());
+ if min < 0 || min > 59 {
+ return None;
+ }
+ let sec = try_opt!(i32::from_str_radix(hms[2], 10).ok());
+ if sec < 0 || sec > 59 {
+ return None;
+ }
+
+ let mut xmp_date = exempi::DateTime::new();
+
+ xmp_date.set_date(year, month, day);
+ xmp_date.set_time(hour, min, sec);
+ // XXX use an actual timezone
+ xmp_date.set_timezone(exempi::XmpTzSign::UTC, 0, 0);
+
+ Some(xmp_date)
+}
+
#[no_mangle]
pub extern "C" fn fwk_exempi_manager_new() -> *mut ExempiManager {
Box::into_raw(Box::new(ExempiManager::new(None)))
@@ -339,6 +401,7 @@ mod tests {
use std::path::PathBuf;
use super::ExempiManager;
use super::XmpMeta;
+ use super::xmp_date_from_exif;
use exempi;
fn get_xmp_sample_path() -> PathBuf {
@@ -476,4 +539,23 @@ mod tests {
assert!(output.is_some());
assert_eq!(output.unwrap(), 45.49179166666666418450404307805001735687255859375);
}
+
+ #[test]
+ fn test_xmp_date_from_exif() {
+ let d = xmp_date_from_exif("2012:02:17 11:10:49");
+ assert!(d.is_some());
+ let d = d.unwrap();
+ assert_eq!(d.year(), 2012);
+ assert_eq!(d.month(), 02);
+ assert_eq!(d.day(), 17);
+ assert_eq!(d.hour(), 11);
+ assert_eq!(d.minute(), 10);
+ assert_eq!(d.second(), 49);
+
+ let d = xmp_date_from_exif("2012:02:17/11:10:49");
+ assert!(d.is_none());
+
+ let d = xmp_date_from_exif("2012.02.17 11.10.49");
+ assert!(d.is_none());
+ }
}
diff --git a/src/fwk/utils/exiv2.rs b/src/fwk/utils/exiv2.rs
new file mode 100644
index 0000000..fc0e268
--- /dev/null
+++ b/src/fwk/utils/exiv2.rs
@@ -0,0 +1,132 @@
+/*
+ * niepce - fwk/utils/exiv2.rs
+ *
+ * Copyright (C) 2018 Hubert Figuière
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use std::ffi::OsStr;
+use std::collections::HashMap;
+
+use exempi;
+use rexiv2;
+
+use super::exempi::{XmpMeta, NS_TIFF, NS_EXIF, NS_XAP, xmp_date_from_exif};
+
+#[derive(Clone, Copy)]
+enum Conversion {
+ None,
+ ExifDate,
+}
+
+#[derive(Clone)]
+enum Converted {
+ Str(String),
+ Date(Option<exempi::DateTime>),
+}
+
+lazy_static! {
+ static ref exiv2_to_xmp: HashMap<&'static str, (&'static str, &'static str, Conversion)> = {
+ [
+ ("Exif.Photo.DateTimeOriginal", (NS_EXIF, "DateTimeOriginal", Conversion::ExifDate)),
+ ("Exif.Photo.DateTimeDigitized", (NS_XAP, "CreateDate", Conversion::ExifDate)),
+ ("Exif.Image.DateTime", (NS_XAP, "ModifyDate", Conversion::ExifDate)),
+ ("Exif.Image.Make", (NS_TIFF, "Make", Conversion::None)),
+ ("Exif.Image.Model", (NS_TIFF, "Model", Conversion::None))
+ ].iter().cloned().collect()
+ };
+}
+
+fn convert(conversion: Conversion, value: &str) -> Converted {
+ match conversion {
+ Conversion::None => Converted::Str(value.to_string()),
+ Conversion::ExifDate => Converted::Date(xmp_date_from_exif(&value)),
+ }
+}
+
+pub fn xmp_from_exiv2<S: AsRef<OsStr>>(file: S) -> Option<XmpMeta> {
+ if let Ok(meta) = rexiv2::Metadata::new_from_path(file) {
+ let mut xmp = exempi::Xmp::new();
+ let mut all_tags : Vec<String> = vec!();
+ if let Ok(mut tags) = meta.get_exif_tags() {
+ all_tags.append(&mut tags);
+ }
+ if let Ok(mut tags) = meta.get_iptc_tags() {
+ all_tags.append(&mut tags);
+ }
+ if let Ok(mut tags) = meta.get_xmp_tags() {
+ all_tags.append(&mut tags);
+ }
+ for tag in all_tags {
+ if let Some(xmp_prop) = exiv2_to_xmp.get(tag.as_str()) {
+ let tagtype = rexiv2::get_tag_type(&tag);
+ match tagtype {
+ Ok(rexiv2::TagType::AsciiString) => {
+ if let Ok(value) = meta.get_tag_string(&tag) {
+ let converted = convert(xmp_prop.2, &value);
+ match converted {
+ Converted::Str(s) => {
+ xmp.set_property(xmp_prop.0, xmp_prop.1, &s, exempi::PROP_NONE);
+ },
+ Converted::Date(d) => {
+ if let Some(d) = d {
+ xmp.set_property_date(xmp_prop.0, xmp_prop.1, &d, exempi::PROP_NONE);
+ } else {
+ err_out!("Couldn't convert Exif date {}", &value);
+ }
+ }
+ }
+ }
+ },
+ Ok(rexiv2::TagType::UnsignedShort) |
+ Ok(rexiv2::TagType::UnsignedLong) |
+ Ok(rexiv2::TagType::SignedShort) |
+ Ok(rexiv2::TagType::SignedLong) => {
+ // XXX rexiv2 returns an i32, which is a problem for UnsignedLong
+ let value = meta.get_tag_numeric(&tag);
+ xmp.set_property_i32(xmp_prop.0, xmp_prop.1, value, exempi::PROP_NONE);
+ },
+ Ok(rexiv2::TagType::UnsignedRational) |
+ Ok(rexiv2::TagType::SignedRational) => {
+ if let Some(value) = meta.get_tag_rational(&tag) {
+ let value_str = format!("{}/{}", value.numer(), value.denom());
+ xmp.set_property(xmp_prop.0, xmp_prop.1, &value_str, exempi::PROP_NONE);
+ }
+ println!("value {} = {:?}", &tag, meta.get_tag_rational(&tag));
+ },
+ _ => {
+ println!("Unhandled type {:?} for {}", tagtype, &tag);
+ }
+ }
+ } else {
+ println!("Unknown property {}", &tag);
+ }
+ }
+ meta.get_gps_info();
+
+ let mut options = exempi::PROP_NONE;
+ if let Ok(date) = xmp.get_property_date(NS_XAP, "ModifyDate", &mut options) {
+ if xmp.set_property_date(NS_XAP, "MetadataDate", &date, exempi::PROP_NONE).is_err() {
+ err_out!("Error setting MetadataDate");
+ }
+ } else {
+ err_out!("Couldn't get the ModifyDate");
+ }
+ Some(XmpMeta::new_with_xmp(xmp))
+ } else {
+ None
+ }
+}
+
diff --git a/src/fwk/utils/mod.rs b/src/fwk/utils/mod.rs
index 765dc18..7f5f3ac 100644
--- a/src/fwk/utils/mod.rs
+++ b/src/fwk/utils/mod.rs
@@ -1,7 +1,7 @@
/*
* niepce - fwk/utils/mod.rs
*
- * Copyright (C) 2017 Hubert Figuière
+ * Copyright (C) 2017-2018 Hubert Figuière
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,4 +18,5 @@
*/
pub mod exempi;
+pub mod exiv2;
pub mod files;
diff --git a/src/lib.rs b/src/lib.rs
index b614f06..77e5ed1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,6 +26,8 @@ extern crate glib;
extern crate glib_sys;
extern crate gtk;
extern crate gtk_sys;
+#[macro_use]
+extern crate lazy_static;
extern crate libc;
extern crate rexiv2;
extern crate rusqlite;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]