[niepce] xmp: implement merge missing properties.
- From: Hubert Figuière <hub src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [niepce] xmp: implement merge missing properties.
- Date: Sat, 21 Oct 2017 04:39:57 +0000 (UTC)
commit ae7d84202cd8a1c81a520fe41111d1b511052699
Author: Hubert Figuière <hub figuiere net>
Date: Thu Oct 12 22:05:12 2017 -0400
xmp: implement merge missing properties.
- remove unused C API
- some other cleanup
- bump the exempi version
Cargo.toml | 2 +-
src/engine/db/libmetadata.rs | 2 +-
src/fwk/base/mod.rs | 3 +-
src/fwk/mod.rs | 2 +-
src/fwk/utils/exempi.cpp | 12 ---
src/fwk/utils/exempi.hpp | 7 +-
src/fwk/utils/exempi.rs | 197 ++++++++++++++++++++++++++++++++----------
src/fwk/utils/test2.xmp | 176 +++++++++++++++++++++++++++++++++++++
8 files changed, 335 insertions(+), 66 deletions(-)
---
diff --git a/Cargo.toml b/Cargo.toml
index cedb2b0..0e47c19 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ build = "build.rs"
[dependencies]
chrono = "0.4.0"
-exempi = { version = "2.4.4", git = "https://github.com/hfiguiere/exempi-rs.git" }
+exempi = { version = "2.5.0", git = "https://github.com/hfiguiere/exempi-rs.git" }
glib-sys = "0.4.0"
gio-sys = "0.4.0"
gio = "0.2.0"
diff --git a/src/engine/db/libmetadata.rs b/src/engine/db/libmetadata.rs
index a65a462..f6ab6d4 100644
--- a/src/engine/db/libmetadata.rs
+++ b/src/engine/db/libmetadata.rs
@@ -180,7 +180,7 @@ impl LibMetadata {
let mut schema = exempi::XmpString::new();
let mut name = exempi::XmpString::new();
let mut value = exempi::XmpString::new();
- let mut flags = exempi::IterFlags::empty();
+ let mut flags = exempi::PropFlags::empty();
while iter.next(&mut schema, &mut name, &mut value, &mut flags) {
keywords.push(String::from(value.to_str()));
}
diff --git a/src/fwk/base/mod.rs b/src/fwk/base/mod.rs
index f845971..558ad4c 100644
--- a/src/fwk/base/mod.rs
+++ b/src/fwk/base/mod.rs
@@ -19,9 +19,10 @@
use std::collections::BTreeSet;
-pub mod date;
#[macro_use]
pub mod debug;
+
+pub mod date;
pub mod fractions;
pub mod propertybag;
pub mod propertyvalue;
diff --git a/src/fwk/mod.rs b/src/fwk/mod.rs
index 1d8f3ed..1321fd5 100644
--- a/src/fwk/mod.rs
+++ b/src/fwk/mod.rs
@@ -17,9 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-pub mod utils;
#[macro_use]
pub mod base;
+pub mod utils;
pub mod toolkit;
pub use self::utils::exempi::{
diff --git a/src/fwk/utils/exempi.cpp b/src/fwk/utils/exempi.cpp
index a84ee4a..5ec23c7 100644
--- a/src/fwk/utils/exempi.cpp
+++ b/src/fwk/utils/exempi.cpp
@@ -17,19 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
-#include <time.h>
-
-#include <giomm/file.h>
-#include <glib.h>
-
-#include <exempi/xmp.h>
-#include <exempi/xmpconsts.h>
-
#include "exempi.hpp"
-#include "fwk/base/date.hpp"
-#include "fwk/base/debug.hpp"
-#include "pathutils.hpp"
namespace xmp {
diff --git a/src/fwk/utils/exempi.hpp b/src/fwk/utils/exempi.hpp
index ac88c26..93dcdae 100644
--- a/src/fwk/utils/exempi.hpp
+++ b/src/fwk/utils/exempi.hpp
@@ -1,6 +1,3 @@
-#ifndef __UTILS_EXEMPI_H__
-#define __UTILS_EXEMPI_H__
-
/*
* niepce - utils/exempi.h
*
@@ -20,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma once
+#include <memory>
#include <vector>
#include <string>
@@ -109,5 +108,3 @@ ExempiManagerPtr exempi_manager_new();
fill-column:99
End:
*/
-
-#endif
diff --git a/src/fwk/utils/exempi.rs b/src/fwk/utils/exempi.rs
index 04be75a..02b1260 100644
--- a/src/fwk/utils/exempi.rs
+++ b/src/fwk/utils/exempi.rs
@@ -17,19 +17,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use libc::c_char;
-use std::ffi::CString;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
-use std::ptr;
use chrono::{DateTime, Utc};
use exempi;
use exempi::Xmp;
-type Date = DateTime<Utc>;
-
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";
@@ -40,6 +35,7 @@ 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 struct NsDef {
ns: String,
@@ -89,10 +85,11 @@ impl XmpMeta {
}
pub fn new_from_file(file: &str, sidecar_only: bool) -> Option<XmpMeta> {
+ let mut meta: Option<XmpMeta> = None;
if !sidecar_only {
if let Some(xmpfile) = exempi::XmpFile::open_new(file, exempi::OPEN_READ) {
if let Some(xmp) = xmpfile.get_new_xmp() {
- return Some(XmpMeta {
+ meta = Some(XmpMeta {
xmp: xmp,
keywords: Vec::new(),
keywords_fetched: false
@@ -100,6 +97,8 @@ impl XmpMeta {
}
}
}
+
+ let mut sidecar_meta: Option<XmpMeta> = None;
let filepath = Path::new(file);
let sidecar = filepath.with_extension("xmp");
let sidecaropen = File::open(sidecar);
@@ -109,7 +108,7 @@ impl XmpMeta {
if result.ok().is_some() {
let mut xmp = exempi::Xmp::new();
if xmp.parse(sidecarcontent.into_bytes().as_slice()) {
- return Some(XmpMeta {
+ sidecar_meta = Some(XmpMeta {
xmp: xmp,
keywords: Vec::new(),
keywords_fetched: false
@@ -117,9 +116,72 @@ impl XmpMeta {
}
}
}
+ if meta.is_none() || sidecar_meta.is_none() {
+ if meta.is_some() {
+ return meta;
+ }
+ return sidecar_meta;
+
+ } else {
+ let mut final_meta = sidecar_meta.unwrap();
+ if !meta.as_ref().unwrap().merge_missing_into_xmp(&mut final_meta) {
+ err_out!("xmp merge failed");
+ } else {
+ return Some(final_meta);
+ }
+ }
+ // XXX maybe we should be more permissible. Returning the
+ // built-in meta if merge failed might not be the best option.
None
}
+ ///
+ /// Merge missing properties from self (source) to destination
+ /// struct and array are considerd missing as a whole, not their content.
+ ///
+ pub fn merge_missing_into_xmp(&self, dest: &mut XmpMeta) -> bool {
+ // Merge XMP self into the dest that has more recent.
+ let source_date = self.get_date_property(NS_XAP, "MetadataDate");
+ let dest_date = dest.get_date_property(NS_XAP, "MetadataDate");
+
+ if source_date.is_none() || dest_date.is_none() {
+ dbg_out!("missing metadata date {} {}", source_date.is_some(),
+ dest_date.is_some());
+ return false;
+ }
+ if source_date > dest_date {
+ dbg_out!("file meta is more recent than sidecar");
+ return false;
+ }
+
+ // in properties in source not in destination gets copied over.
+ let mut iter = exempi::XmpIterator::new(&self.xmp, "", "", exempi::ITER_PROPERTIES);
+ {
+ use exempi::XmpString;
+ let mut schema = XmpString::new();
+ let mut name = XmpString::new();
+ let mut value = XmpString::new();
+ let mut option = exempi::PROP_NONE;
+ while iter.next(&mut schema, &mut name, &mut value, &mut option) {
+ if name.to_str().is_empty() {
+ continue;
+ }
+ if option.contains(exempi::PROP_VALUE_IS_ARRAY)
+ || option.contains(exempi::PROP_VALUE_IS_STRUCT) {
+ iter.skip(exempi::ITER_SKIP_SUBTREE);
+ continue;
+ }
+
+ 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);
+ }
+ }
+ }
+
+ true
+ }
+
pub fn serialize_inline(&self) -> String {
if let Some(xmpstr) = self.xmp.serialize_and_format(
exempi::SERIAL_OMITPACKETWRAPPER | exempi::SERIAL_OMITALLFORMATTING,
@@ -185,6 +247,19 @@ impl XmpMeta {
None
}
+ /// Get the date property and return a 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();
+ if let Some(xmpstring) = self.xmp.get_property(ns, property, &mut flags) {
+ if let Ok(date) = DateTime::parse_from_rfc3339(xmpstring.to_str()) {
+ let utc_date = date.with_timezone(&Utc);
+ return Some(utc_date);
+ }
+ }
+ None
+ }
+
pub fn keywords(&mut self) -> &Vec<String> {
if !self.keywords_fetched {
use exempi::XmpString;
@@ -194,7 +269,7 @@ impl XmpMeta {
let mut schema = XmpString::new();
let mut name = XmpString::new();
let mut value = XmpString::new();
- let mut option = exempi::ITER_NONE;
+ let mut option = exempi::PROP_NONE;
while iter.next(&mut schema, &mut name, &mut value, &mut option) {
self.keywords.push(String::from(value.to_str()));
}
@@ -284,46 +359,15 @@ pub extern "C" fn fwk_exempi_manager_delete(em: *mut ExempiManager) {
unsafe { Box::from_raw(em); }
}
-#[no_mangle]
-pub extern "C" fn fwk_xmp_meta_get_orientation(xmp: &XmpMeta) -> i32 {
- if let Some(o) = xmp.orientation() {
- return o;
- }
- 0
-}
-
-#[no_mangle]
-pub extern "C" fn fwk_xmp_meta_get_rating(xmp: &XmpMeta) -> i32 {
- if let Some(r) = xmp.rating() {
- return r;
- }
- 0
-}
-
-#[no_mangle]
-pub extern "C" fn fwk_xmp_meta_get_label(xmp: &XmpMeta) -> *mut c_char {
- if let Some(s) = xmp.label() {
- return CString::new(s.as_bytes()).unwrap().into_raw()
- }
- ptr::null_mut()
-}
-
-#[no_mangle]
-pub extern "C" fn fwk_xmp_meta_get_creation_date(xmp: &XmpMeta) -> *mut Date {
- if let Some(d) = xmp.creation_date() {
- return Box::into_raw(Box::new(d));
- }
- ptr::null_mut()
-}
-
#[cfg(test)]
mod tests {
- #[test]
- fn xmp_meta_works() {
+ use std::path::PathBuf;
+ use super::ExempiManager;
+ use super::XmpMeta;
+ use exempi;
+
+ fn get_xmp_sample_path() -> PathBuf {
use std::env;
- use std::path::PathBuf;
- use super::ExempiManager;
- use super::XmpMeta;
let mut dir: PathBuf;
if let Ok(pdir) = env::var("CARGO_MANIFEST_DIR") {
@@ -334,6 +378,13 @@ mod tests {
} else {
dir = PathBuf::from(".");
}
+ dir
+ }
+
+ #[test]
+ fn xmp_meta_works() {
+
+ let mut dir = get_xmp_sample_path();
dir.push("test.xmp");
let _xmp_manager = ExempiManager::new(None);
@@ -356,6 +407,62 @@ mod tests {
}
}
+ fn test_property_value(meta: &XmpMeta, ns: &str,
+ property: &str, expected_value: &str) {
+ let mut flags: exempi::PropFlags = exempi::PropFlags::empty();
+ let value = meta.xmp.get_property(ns, property, &mut flags);
+ assert!(value.is_some());
+ assert_eq!(value.unwrap().to_str(), expected_value);
+ }
+
+ fn test_property_array_value(meta: &XmpMeta, ns: &str,
+ property: &str, idx: i32, expected_value: &str) {
+ let mut flags: exempi::PropFlags = exempi::PropFlags::empty();
+ let value = meta.xmp.get_array_item(ns, property, idx, &mut flags);
+ assert!(value.is_some());
+ assert_eq!(value.unwrap().to_str(), expected_value);
+ }
+
+ #[test]
+ fn test_merge_missing_into_xmp() {
+ let dir = get_xmp_sample_path();
+
+ let mut source = dir.clone();
+ source.push("test.xmp");
+
+ let mut dest = dir.clone();
+ dest.push("test2.xmp");
+ let _xmp_manager = ExempiManager::new(None);
+
+ if let Some(xmpfile) = source.to_str() {
+ let meta = XmpMeta::new_from_file(xmpfile, true);
+ assert!(meta.is_some());
+ let meta = meta.unwrap();
+
+ if let Some(xmpfile) = dest.to_str() {
+ let dstmeta = XmpMeta::new_from_file(xmpfile, true);
+ assert!(dstmeta.is_some());
+ let mut dstmeta = dstmeta.unwrap();
+
+ let result = meta.merge_missing_into_xmp(&mut dstmeta);
+ assert!(result);
+ // properties that were missing
+ test_property_value(&dstmeta, super::NS_TIFF, "Model", "Canon EOS 20D");
+ test_property_value(&dstmeta, super::NS_AUX, "Lens", "24.0-85.0 mm");
+
+ // Array property that contain less in destination
+ // Shouldn't have changed.
+ test_property_array_value(&dstmeta, super::NS_DC, "subject", 1, "night");
+ test_property_array_value(&dstmeta, super::NS_DC, "subject", 2, "ontario");
+ test_property_array_value(&dstmeta, super::NS_DC, "subject", 3, "ottawa");
+ test_property_array_value(&dstmeta, super::NS_DC, "subject", 4, "parliament of canada");
+ assert!(!dstmeta.xmp.has_property(super::NS_DC, "dc:subject[5]"));
+ }
+ } else {
+ assert!(false);
+ }
+ }
+
#[test]
fn gps_coord_from_works() {
use super::gps_coord_from_xmp;
diff --git a/src/fwk/utils/test2.xmp b/src/fwk/utils/test2.xmp
new file mode 100644
index 0000000..e807b4a
--- /dev/null
+++ b/src/fwk/utils/test2.xmp
@@ -0,0 +1,176 @@
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Public XMP Toolkit Core 3.5">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
+ <tiff:Make>Canon</tiff:Make>
+ <tiff:Orientation>1</tiff:Orientation>
+ <tiff:ImageWidth>3504</tiff:ImageWidth>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:exif="http://ns.adobe.com/exif/1.0/">
+ <exif:ExifVersion>0221</exif:ExifVersion>
+ <exif:ExposureTime>5/1</exif:ExposureTime>
+ <exif:ShutterSpeedValue>-2321928/1000000</exif:ShutterSpeedValue>
+ <exif:FNumber>8/1</exif:FNumber>
+ <exif:ApertureValue>6/1</exif:ApertureValue>
+ <exif:ExposureProgram>1</exif:ExposureProgram>
+ <exif:ISOSpeedRatings>
+ <rdf:Seq>
+ <rdf:li>100</rdf:li>
+ </rdf:Seq>
+ </exif:ISOSpeedRatings>
+ <exif:DateTimeOriginal>2006-12-07T23:37:30-05:00</exif:DateTimeOriginal>
+ <exif:DateTimeDigitized>2006-12-07T23:37:30-05:00</exif:DateTimeDigitized>
+ <exif:ExposureBiasValue>0/2</exif:ExposureBiasValue>
+ <exif:MeteringMode>5</exif:MeteringMode>
+ <exif:Flash rdf:parseType="Resource">
+ <exif:Fired>False</exif:Fired>
+ <exif:Return>0</exif:Return>
+ <exif:Mode>2</exif:Mode>
+ <exif:Function>False</exif:Function>
+ <exif:RedEyeMode>False</exif:RedEyeMode>
+ </exif:Flash>
+ <exif:FocalLength>24/1</exif:FocalLength>
+ <exif:CustomRendered>0</exif:CustomRendered>
+ <exif:ExposureMode>1</exif:ExposureMode>
+ <exif:WhiteBalance>0</exif:WhiteBalance>
+ <exif:SceneCaptureType>0</exif:SceneCaptureType>
+ <exif:FocalPlaneXResolution>3504000/885</exif:FocalPlaneXResolution>
+ <exif:FocalPlaneYResolution>2336000/590</exif:FocalPlaneYResolution>
+ <exif:FocalPlaneResolutionUnit>2</exif:FocalPlaneResolutionUnit>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xap="http://ns.adobe.com/xap/1.0/">
+ <xap:ModifyDate>2006-12-07T23:37:30-05:00</xap:ModifyDate>
+ <xap:MetadataDate>2007-10-12T18:46:36-04:00</xap:MetadataDate>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:aux="http://ns.adobe.com/exif/1.0/aux/">
+ <aux:SerialNumber>420103070</aux:SerialNumber>
+ <aux:LensInfo>24/1 85/1 0/0 0/0</aux:LensInfo>
+ <aux:ImageNumber>185</aux:ImageNumber>
+ <aux:OwnerName>unknown</aux:OwnerName>
+ <aux:Firmware>1.1.0</aux:Firmware>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">
+ <crs:AlreadyApplied>False</crs:AlreadyApplied>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:creator>
+ <rdf:Seq>
+ <rdf:li>unknown</rdf:li>
+ </rdf:Seq>
+ </dc:creator>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>night</rdf:li>
+ <rdf:li>ontario</rdf:li>
+ <rdf:li>ottawa</rdf:li>
+ <rdf:li>parliament of canada</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:crss="http://ns.adobe.com/camera-raw-saved-settings/1.0/"
+ xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/">
+ <crss:SavedSettings>
+ <rdf:Bag>
+ <rdf:li rdf:parseType="Resource">
+ <crss:Name>16/12/06 9:08:59 AM: Import</crss:Name>
+ <crss:Type>Snapshot</crss:Type>
+ <crss:Parameters rdf:parseType="Resource">
+ <crs:WhiteBalance>As Shot</crs:WhiteBalance>
+ <crs:Exposure>0.00</crs:Exposure>
+ <crs:Shadows>5</crs:Shadows>
+ <crs:Brightness>+50</crs:Brightness>
+ <crs:Contrast>+25</crs:Contrast>
+ <crs:Saturation>0</crs:Saturation>
+ <crs:Sharpness>25</crs:Sharpness>
+ <crs:LuminanceSmoothing>0</crs:LuminanceSmoothing>
+ <crs:ColorNoiseReduction>25</crs:ColorNoiseReduction>
+ <crs:ChromaticAberrationR>0</crs:ChromaticAberrationR>
+ <crs:ChromaticAberrationB>0</crs:ChromaticAberrationB>
+ <crs:VignetteAmount>0</crs:VignetteAmount>
+ <crs:ShadowTint>0</crs:ShadowTint>
+ <crs:RedHue>0</crs:RedHue>
+ <crs:RedSaturation>0</crs:RedSaturation>
+ <crs:GreenHue>0</crs:GreenHue>
+ <crs:GreenSaturation>0</crs:GreenSaturation>
+ <crs:BlueHue>0</crs:BlueHue>
+ <crs:BlueSaturation>0</crs:BlueSaturation>
+ <crs:FillLight>0</crs:FillLight>
+ <crs:Vibrance>0</crs:Vibrance>
+ <crs:HighlightRecovery>0</crs:HighlightRecovery>
+ <crs:Clarity>0</crs:Clarity>
+ <crs:Defringe>0</crs:Defringe>
+ <crs:HueAdjustmentRed>0</crs:HueAdjustmentRed>
+ <crs:HueAdjustmentOrange>0</crs:HueAdjustmentOrange>
+ <crs:HueAdjustmentYellow>0</crs:HueAdjustmentYellow>
+ <crs:HueAdjustmentGreen>0</crs:HueAdjustmentGreen>
+ <crs:HueAdjustmentAqua>0</crs:HueAdjustmentAqua>
+ <crs:HueAdjustmentBlue>0</crs:HueAdjustmentBlue>
+ <crs:HueAdjustmentPurple>0</crs:HueAdjustmentPurple>
+ <crs:HueAdjustmentMagenta>0</crs:HueAdjustmentMagenta>
+ <crs:SaturationAdjustmentRed>0</crs:SaturationAdjustmentRed>
+ <crs:SaturationAdjustmentOrange>0</crs:SaturationAdjustmentOrange>
+ <crs:SaturationAdjustmentYellow>0</crs:SaturationAdjustmentYellow>
+ <crs:SaturationAdjustmentGreen>0</crs:SaturationAdjustmentGreen>
+ <crs:SaturationAdjustmentAqua>0</crs:SaturationAdjustmentAqua>
+ <crs:SaturationAdjustmentBlue>0</crs:SaturationAdjustmentBlue>
+ <crs:SaturationAdjustmentPurple>0</crs:SaturationAdjustmentPurple>
+ <crs:SaturationAdjustmentMagenta>0</crs:SaturationAdjustmentMagenta>
+ <crs:LuminanceAdjustmentRed>0</crs:LuminanceAdjustmentRed>
+ <crs:LuminanceAdjustmentOrange>0</crs:LuminanceAdjustmentOrange>
+ <crs:LuminanceAdjustmentYellow>0</crs:LuminanceAdjustmentYellow>
+ <crs:LuminanceAdjustmentGreen>0</crs:LuminanceAdjustmentGreen>
+ <crs:LuminanceAdjustmentAqua>0</crs:LuminanceAdjustmentAqua>
+ <crs:LuminanceAdjustmentBlue>0</crs:LuminanceAdjustmentBlue>
+ <crs:LuminanceAdjustmentPurple>0</crs:LuminanceAdjustmentPurple>
+ <crs:LuminanceAdjustmentMagenta>0</crs:LuminanceAdjustmentMagenta>
+ <crs:SplitToningShadowHue>0</crs:SplitToningShadowHue>
+ <crs:SplitToningShadowSaturation>0</crs:SplitToningShadowSaturation>
+ <crs:SplitToningHighlightHue>0</crs:SplitToningHighlightHue>
+ <crs:SplitToningHighlightSaturation>0</crs:SplitToningHighlightSaturation>
+ <crs:SplitToningBalance>0</crs:SplitToningBalance>
+ <crs:ParametricShadows>0</crs:ParametricShadows>
+ <crs:ParametricDarks>0</crs:ParametricDarks>
+ <crs:ParametricLights>0</crs:ParametricLights>
+ <crs:ParametricHighlights>0</crs:ParametricHighlights>
+ <crs:ParametricShadowSplit>25</crs:ParametricShadowSplit>
+ <crs:ParametricMidtoneSplit>50</crs:ParametricMidtoneSplit>
+ <crs:ParametricHighlightSplit>75</crs:ParametricHighlightSplit>
+ <crs:SharpenRadius>+1.0</crs:SharpenRadius>
+ <crs:SharpenDetail>25</crs:SharpenDetail>
+ <crs:SharpenEdgeMasking>0</crs:SharpenEdgeMasking>
+ <crs:ConvertToGrayscale>False</crs:ConvertToGrayscale>
+ <crs:ToneCurveName>Medium Contrast</crs:ToneCurveName>
+ <crs:ToneCurve>
+ <rdf:Seq>
+ <rdf:li>0, 0</rdf:li>
+ <rdf:li>32, 22</rdf:li>
+ <rdf:li>64, 56</rdf:li>
+ <rdf:li>128, 128</rdf:li>
+ <rdf:li>192, 196</rdf:li>
+ <rdf:li>255, 255</rdf:li>
+ </rdf:Seq>
+ </crs:ToneCurve>
+ </crss:Parameters>
+ </rdf:li>
+ </rdf:Bag>
+ </crss:SavedSettings>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:lr="http://ns.adobe.com/lightroom/1.0/">
+ <lr:hierarchicalSubject>
+ <rdf:Bag>
+ <rdf:li>choir</rdf:li>
+ <rdf:li>night</rdf:li>
+ <rdf:li>ontario|ottawa</rdf:li>
+ <rdf:li>ontario|ottawa|parliament of canada</rdf:li>
+ </rdf:Bag>
+ </lr:hierarchicalSubject>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]