commit 3de41524ce32bb45f1dd100f8ddc6bb009aa10e2
parent b3e85c70b61f511afca9be871a7f6a477e2a9e03
Author: mauricefisher64 <92736594+mauricefisher64@users.noreply.github.com>
Date: Wed, 27 Mar 2024 15:14:32 -0400
Added support for XMP streaming writes for TIFF/DNG (#433)
* TIFF read support
* Start of alternate apporach
* simplify experiment
* More intermeadiate work
* Working support for stripped TIFFs
* More TIFF enhnacements
* SubFile stuff
* DNG push
* DNG bug fixes
* Endian fixes
* Support for GPS IFD
Clippy fixes
* Patch support
bug fixes
* Code cleanup
* Review changes
* clippy fmt fix
* wasm fix
* Fix for linux build
* Address PR comments
* fix up comments
* TIFF streaming write support
Build fixes
* Add streaming xmp writes
* preserve existing XMP
---------
Co-authored-by: mauricefisher64 <maurice_fisher@hotmail.com>
Diffstat:
1 file changed, 74 insertions(+), 10 deletions(-)
diff --git a/sdk/src/asset_handlers/tiff_io.rs b/sdk/src/asset_handlers/tiff_io.rs
@@ -32,6 +32,7 @@ use crate::{
RemoteRefEmbedType,
},
error::{Error, Result},
+ utils::xmp_inmemory_utils::{add_provenance, MIN_XMP},
};
const II: [u8; 2] = *b"II";
@@ -64,6 +65,7 @@ static SUPPORTED_TYPES: [&str; 9] = [
];
// The type of an IFD entry
+#[derive(Debug, PartialEq)]
enum IFDEntryType {
Byte = 1, // 8-bit unsigned integer
Ascii = 2, // 8-bit byte that contains a 7-bit ASCII code; the last byte must be zero
@@ -1371,6 +1373,11 @@ where
None => return None,
};
+ // make sure the tag type is correct
+ if IFDEntryType::from_u16(xmp_ifd_entry.entry_type)? != IFDEntryType::Byte {
+ return None;
+ }
+
// move read point to start of entry
let decoded_offset = decode_offset(xmp_ifd_entry.value_offset, e, big_tiff).ok()?;
asset_reader.seek(SeekFrom::Start(decoded_offset)).ok()?;
@@ -1610,15 +1617,22 @@ impl RemoteRefEmbed for TiffIO {
) -> Result<()> {
match embed_ref {
crate::asset_io::RemoteRefEmbedType::Xmp(manifest_uri) => {
- #[cfg(feature = "xmp_write")]
- {
- crate::embedded_xmp::add_manifest_uri_to_file(asset_path, &manifest_uri)
- }
+ let output_buf = Vec::new();
+ let mut output_stream = Cursor::new(output_buf);
- #[cfg(not(feature = "xmp_write"))]
+ // block so that source file is closed after embed
{
- Err(crate::error::Error::MissingFeature("xmp_write".to_string()))
+ let mut source_stream = std::fs::File::open(asset_path)?;
+ self.embed_reference_to_stream(
+ &mut source_stream,
+ &mut output_stream,
+ RemoteRefEmbedType::Xmp(manifest_uri),
+ )?;
}
+
+ // write will replace exisiting contents
+ std::fs::write(asset_path, output_stream.into_inner())?;
+ Ok(())
}
crate::asset_io::RemoteRefEmbedType::StegoS(_) => Err(Error::UnsupportedType),
crate::asset_io::RemoteRefEmbedType::StegoB(_) => Err(Error::UnsupportedType),
@@ -1628,11 +1642,35 @@ impl RemoteRefEmbed for TiffIO {
fn embed_reference_to_stream(
&self,
- _source_stream: &mut dyn CAIRead,
- _output_stream: &mut dyn CAIReadWrite,
- _embed_ref: RemoteRefEmbedType,
+ source_stream: &mut dyn CAIRead,
+ output_stream: &mut dyn CAIReadWrite,
+ embed_ref: RemoteRefEmbedType,
) -> Result<()> {
- Err(Error::UnsupportedType)
+ match embed_ref {
+ crate::asset_io::RemoteRefEmbedType::Xmp(manifest_uri) => {
+ let xmp = match self.get_reader().read_xmp(source_stream) {
+ Some(xmp) => add_provenance(&xmp, &manifest_uri)?,
+ None => {
+ let xmp = format!("http://ns.adobe.com/xap/1.0/\0 {}", MIN_XMP);
+ add_provenance(&xmp, &manifest_uri)?
+ }
+ };
+
+ let l = u64::value_from(xmp.len())
+ .map_err(|_err| Error::InvalidAsset("value out of range".to_string()))?;
+
+ let entry = IfdClonedEntry {
+ entry_tag: XMP_TAG,
+ entry_type: IFDEntryType::Byte as u16,
+ value_count: l,
+ value_bytes: xmp.as_bytes().to_vec(),
+ };
+ tiff_clone_with_tags(output_stream, source_stream, vec![entry])
+ }
+ crate::asset_io::RemoteRefEmbedType::StegoS(_) => Err(Error::UnsupportedType),
+ crate::asset_io::RemoteRefEmbedType::StegoB(_) => Err(Error::UnsupportedType),
+ crate::asset_io::RemoteRefEmbedType::Watermark(_) => Err(Error::UnsupportedType),
+ }
}
}
@@ -1678,6 +1716,32 @@ pub mod tests {
}
#[test]
+ fn test_write_xmp() {
+ let data = "some data";
+
+ let source = crate::utils::test::fixture_path("TUSCANY.TIF");
+
+ let temp_dir = tempdir().unwrap();
+ let output = temp_dir_path(&temp_dir, "test.tif");
+
+ std::fs::copy(source, &output).unwrap();
+
+ let tiff_io = TiffIO {};
+
+ // save data to tiff
+ let eh = tiff_io.remote_ref_writer_ref().unwrap();
+ eh.embed_reference(&output, RemoteRefEmbedType::Xmp(data.to_string()))
+ .unwrap();
+
+ // read data back
+ let mut output_stream = std::fs::File::open(&output).unwrap();
+ let xmp = tiff_io.read_xmp(&mut output_stream).unwrap();
+ let loaded = crate::utils::xmp_inmemory_utils::extract_provenance(&xmp).unwrap();
+
+ assert_eq!(&loaded, data);
+ }
+
+ #[test]
fn test_remove_manifest() {
let data = "some data";