use std::{
io::{self, Cursor, Read, Seek},
sync::Arc,
};
use anyhow::Result;
use c2pa::{
validation_results::ValidationState, Builder, Context, DigitalSourceType, Reader, Settings,
};
use serde_json::json;
const INGREDIENT_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg");
const SOURCE_IMAGE: &[u8] = include_bytes!("../tests/fixtures/earth_apollo17.jpg");
fn manifest_def(title: &str, format: &str) -> String {
json!({
"title": title,
"format": format,
"claim_generator_info": [
{
"name": "c2pa test",
"version": env!("CARGO_PKG_VERSION")
}
]
})
.to_string()
}
fn capture_ingredient<R>(format: &str, stream: &mut R, context: &Arc<Context>) -> Result<Vec<u8>>
where
R: Read + Seek + Send,
{
let mut builder = Builder::from_shared_context(context);
builder.add_ingredient_from_stream(
json!({
"title": "Archived Ingredient",
"relationship": "parentOf",
"label": "test_ingredient"
})
.to_string(),
format,
stream,
)?;
builder.add_action(json!(
{
"action": "c2pa.opened",
"parameters": {
"ingredientIds": ["test_ingredient"],
}
}
))?;
let signer = context.signer()?;
let output = builder.sign(
signer,
"application/c2pa",
&mut io::empty(),
&mut io::empty(),
)?;
Ok(output)
}
fn main() -> Result<()> {
const INGREDIENT_ID: &str = "ingredient_1"; const INGREDIENT_TITLE: &str = "Restored Ingredient";
const FORMAT: &str = "image/jpeg";
let mut ingredient_source = Cursor::new(INGREDIENT_IMAGE);
let settings =
Settings::new().with_json(include_str!("../tests/fixtures/test_settings.json"))?;
let context = Context::new().with_settings(settings)?.into_shared();
let ingredient_c2pa = capture_ingredient(FORMAT, &mut ingredient_source, &context)?;
let mut builder = Builder::from_shared_context(&context)
.with_definition(manifest_def("Builder Sample", FORMAT))?;
builder.set_intent(c2pa::BuilderIntent::Create(DigitalSourceType::Empty));
builder.add_ingredient_from_stream(
json!({ "title": INGREDIENT_TITLE,
"relationship": "componentOf", "label": INGREDIENT_ID })
.to_string(),
"application/c2pa",
&mut Cursor::new(ingredient_c2pa),
)?;
builder.add_action(json!(
{
"action": "c2pa.placed",
"parameters": {
"ingredientIds": [INGREDIENT_ID],
}
}
))?;
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 source = Cursor::new(SOURCE_IMAGE);
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)?;
println!("{}", reader.json());
assert_eq!(reader.validation_state(), ValidationState::Trusted);
assert_eq!(
reader.active_manifest().unwrap().ingredients()[0]
.title()
.unwrap(),
INGREDIENT_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_builder_sample() -> Result<()> {
main()
}
}