c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

c2pa_io.rs (5561B)


      1 // Copyright 2022 Adobe. All rights reserved.
      2 // This file is licensed to you under the Apache License,
      3 // Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
      4 // or the MIT license (http://opensource.org/licenses/MIT),
      5 // at your option.
      6 
      7 // Unless required by applicable law or agreed to in writing,
      8 // this software is distributed on an "AS IS" BASIS, WITHOUT
      9 // WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
     10 // implied. See the LICENSE-MIT and LICENSE-APACHE files for the
     11 // specific language governing permissions and limitations under
     12 // each license.
     13 
     14 use std::{fs::File, path::Path};
     15 
     16 use crate::{
     17     asset_io::{
     18         AssetIO, CAIRead, CAIReadWrite, CAIReader, CAIWriter, ComposedManifestRef,
     19         HashBlockObjectType, HashObjectPositions,
     20     },
     21     error::{Error, Result},
     22 };
     23 
     24 static SUPPORTED_TYPES: [&str; 3] = [
     25     "c2pa",
     26     "application/c2pa",
     27     "application/x-c2pa-manifest-store",
     28 ];
     29 
     30 /// Supports working with ".c2pa" files containing only manifest store data
     31 pub struct C2paIO {}
     32 
     33 impl CAIReader for C2paIO {
     34     fn read_cai(&self, asset_reader: &mut dyn CAIRead) -> Result<Vec<u8>> {
     35         let mut cai_data = Vec::new();
     36         // read the whole file
     37         asset_reader.read_to_end(&mut cai_data)?;
     38         Ok(cai_data)
     39     }
     40 
     41     // C2PA files have no xmp data
     42     fn read_xmp(&self, _asset_reader: &mut dyn CAIRead) -> Option<String> {
     43         None
     44     }
     45 }
     46 
     47 impl CAIWriter for C2paIO {
     48     fn write_cai(
     49         &self,
     50         _input_stream: &mut dyn CAIRead,
     51         output_stream: &mut dyn CAIReadWrite,
     52         store_bytes: &[u8],
     53     ) -> Result<()> {
     54         // just write the store bytes and ingore the input stream
     55         output_stream.write_all(store_bytes)?;
     56         Ok(())
     57     }
     58 
     59     fn get_object_locations_from_stream(
     60         &self,
     61         __input_stream: &mut dyn CAIRead,
     62     ) -> Result<Vec<HashObjectPositions>> {
     63         // there is no data to hash
     64         Ok(vec![])
     65     }
     66 
     67     fn remove_cai_store_from_stream(
     68         &self,
     69         _input_stream: &mut dyn CAIRead,
     70         _output_stream: &mut dyn CAIReadWrite,
     71     ) -> Result<()> {
     72         // nothing to do here, just return Ok
     73         Ok(())
     74     }
     75 }
     76 
     77 impl AssetIO for C2paIO {
     78     fn read_cai_store(&self, asset_path: &Path) -> Result<Vec<u8>> {
     79         let mut f = File::open(asset_path)?;
     80         self.read_cai(&mut f)
     81     }
     82 
     83     fn save_cai_store(&self, asset_path: &std::path::Path, store_bytes: &[u8]) -> Result<()> {
     84         // just save the data in a file
     85         std::fs::write(asset_path, store_bytes)
     86             .map_err(|_err| Error::BadParam("C2PA write error".to_owned()))?;
     87 
     88         Ok(())
     89     }
     90 
     91     fn get_object_locations(
     92         &self,
     93         _asset_path: &std::path::Path,
     94     ) -> Result<Vec<HashObjectPositions>> {
     95         let hop = HashObjectPositions {
     96             offset: 0,
     97             length: 0,
     98             htype: HashBlockObjectType::Cai,
     99         };
    100 
    101         Ok(vec![hop])
    102     }
    103 
    104     fn remove_cai_store(&self, _asset_path: &Path) -> Result<()> {
    105         Ok(())
    106     }
    107 
    108     fn new(_asset_type: &str) -> Self
    109     where
    110         Self: Sized,
    111     {
    112         C2paIO {}
    113     }
    114 
    115     fn get_handler(&self, asset_type: &str) -> Box<dyn AssetIO> {
    116         Box::new(C2paIO::new(asset_type))
    117     }
    118 
    119     fn get_reader(&self) -> &dyn CAIReader {
    120         self
    121     }
    122 
    123     fn get_writer(&self, asset_type: &str) -> Option<Box<dyn CAIWriter>> {
    124         Some(Box::new(C2paIO::new(asset_type)))
    125     }
    126 
    127     fn supported_types(&self) -> &[&str] {
    128         &SUPPORTED_TYPES
    129     }
    130 
    131     fn composed_data_ref(&self) -> Option<&dyn ComposedManifestRef> {
    132         Some(self)
    133     }
    134 }
    135 
    136 impl ComposedManifestRef for C2paIO {
    137     // Return entire CAI block as Vec<u8>
    138     fn compose_manifest(&self, manifest_data: &[u8], _format: &str) -> Result<Vec<u8>> {
    139         Ok(manifest_data.to_vec())
    140     }
    141 }
    142 
    143 #[cfg(test)]
    144 #[cfg(feature = "file_io")]
    145 pub mod tests {
    146     #![allow(clippy::expect_used)]
    147     #![allow(clippy::unwrap_used)]
    148 
    149     use tempfile::tempdir;
    150 
    151     use super::{AssetIO, C2paIO, CAIReader, CAIWriter};
    152     use crate::{
    153         status_tracker::OneShotStatusTracker,
    154         store::Store,
    155         utils::test::{fixture_path, temp_dir_path, temp_signer},
    156     };
    157 
    158     #[test]
    159     fn c2pa_io_parse() {
    160         let path = fixture_path("C.jpg");
    161 
    162         let temp_dir = tempdir().expect("temp dir");
    163         let temp_path = temp_dir_path(&temp_dir, "test.c2pa");
    164 
    165         let c2pa_io = C2paIO {};
    166         let manifest = crate::jumbf_io::load_jumbf_from_file(&path).expect("read_cai_store");
    167         c2pa_io
    168             .save_cai_store(&temp_path, &manifest)
    169             .expect("save cai store");
    170 
    171         let store = Store::load_from_asset(&temp_path, false, &mut OneShotStatusTracker::new())
    172             .expect("loading store");
    173 
    174         let signer = temp_signer();
    175 
    176         let manifest2 = store.to_jumbf(signer.as_ref()).expect("to_jumbf");
    177         assert_eq!(&manifest, &manifest2);
    178     }
    179 
    180     #[test]
    181     #[cfg(feature = "file_io")]
    182     fn c2pa_stream_io() {
    183         use std::io::{empty, Cursor};
    184         let path = fixture_path("C.jpg");
    185 
    186         let c2pa_io = C2paIO {};
    187         let manifest = crate::jumbf_io::load_jumbf_from_file(&path).expect("load_jumbf_from_file");
    188         let mut output_stream = Cursor::new(Vec::new());
    189         c2pa_io
    190             .write_cai(&mut empty(), &mut output_stream, &manifest)
    191             .expect("write_cai");
    192 
    193         output_stream.set_position(0);
    194         let manifest2 = c2pa_io.read_cai(&mut output_stream).expect("read_cai");
    195 
    196         assert_eq!(&manifest, &manifest2);
    197     }
    198 }