use std::io::{Cursor, Seek};
use anyhow::Result;
use c2pa::{
crypto::raw_signature::SigningAlg, settings::Settings, validation_results::ValidationState,
Builder, CallbackSigner, Context, Reader,
};
use serde_json::json;
const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg");
const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub");
const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem");
fn manifest_def(title: &str, format: &str) -> String {
json!({
"title": title,
"format": format,
"claim_generator_info": [
{
"name": "c2pa test",
"version": env!("CARGO_PKG_VERSION")
}
],
"thumbnail": {
"format": format,
"identifier": "manifest_thumbnail.jpg"
},
"ingredients": [
{
"title": "Test",
"format": "image/jpeg",
"instance_id": "12345",
"relationship": "inputTo"
}
],
"assertions": [
{
"label": "c2pa.actions",
"data": {
"actions": [
{
"action": "c2pa.edited",
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia",
"softwareAgent": {
"name": "My AI Tool",
"version": "0.1.0"
}
}
]
}
}
]
}).to_string()
}
fn main() -> Result<()> {
let title = "edited.jpg";
let format = "image/jpeg";
let parent_name = "CA.jpg";
let mut source = Cursor::new(TEST_IMAGE);
let modified_core = toml::toml! {
[core]
debug = true
hash_alg = "sha512"
max_memory_usage = 123456
}
.to_string();
let mut settings =
Settings::new().with_toml(include_str!("../tests/fixtures/test_settings.toml"))?;
settings.update_from_str(&modified_core, "toml")?;
let ed_signer =
|_context: *const (), data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY);
let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS);
let context = Context::new()
.with_settings(settings)?
.with_signer(signer)
.into_shared();
let mut builder =
Builder::from_shared_context(&context).with_definition(manifest_def(title, format))?;
builder.add_ingredient_from_stream(
json!({
"title": parent_name,
"relationship": "parentOf",
"label": "parent_label", })
.to_string(),
format,
&mut source,
)?;
builder.add_action(json!({
"action": "c2pa.opened",
"parameters": {
"ingredientIds": ["parent_label"], }
}))?;
let thumb_uri = builder
.definition
.thumbnail
.as_ref()
.map(|t| t.identifier.clone());
if let Some(uri) = thumb_uri {
if !uri.starts_with("self#jumbf") {
source.rewind()?;
builder.add_resource(&uri, &mut source)?;
}
}
let mut archive = Cursor::new(Vec::new());
builder.to_archive(&mut archive)?;
archive.rewind()?;
let mut builder = Builder::from_shared_context(&context).with_archive(&mut archive)?;
let mut dest = Cursor::new(Vec::new());
builder.save_to_stream(format, &mut source, &mut dest)?;
dest.rewind()?;
let reader = Reader::from_shared_context(&context).with_stream(format, &mut dest)?;
let mut thumbnail = Cursor::new(Vec::new());
if let Some(manifest) = reader.active_manifest() {
if let Some(thumbnail_ref) = manifest.thumbnail_ref() {
reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?;
println!(
"wrote thumbnail {} of size {}",
thumbnail_ref.format,
thumbnail.get_ref().len()
);
}
}
println!("{}", reader.json());
assert_eq!(reader.validation_state(), ValidationState::Trusted);
assert_eq!(reader.active_manifest().unwrap().title().unwrap(), title);
Ok(())
}
#[cfg(test)]
mod tests {
use c2pa_macros::c2pa_test_async;
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::*;
use super::*;
#[c2pa_test_async]
async fn test_api() -> Result<()> {
main()
}
}