use std::io::Cursor;
use anyhow::Result;
use c2pa::{
assertions::{c2pa_action, c2pa_reason, Action},
settings::Settings,
validation_results::ValidationState,
Builder, BuilderIntent, Context, Reader,
};
use serde_json::json;
const SOURCE_IMAGE: &[u8] = include_bytes!("../tests/fixtures/earth_apollo17.jpg");
fn main() -> Result<()> {
let format = "image/jpeg";
let settings =
Settings::new().with_json(include_str!("../tests/fixtures/test_settings.json"))?;
let context = Context::new().with_settings(settings)?.into_shared();
let mut builder = Builder::from_shared_context(&context);
builder.set_intent(BuilderIntent::Create(
c2pa::DigitalSourceType::DigitalCapture,
));
builder.add_assertion(
"c2pa.metadata",
&json!({
"@context": {
"exif": "http://ns.adobe.com/exif/1.0/",
"tiff": "http://ns.adobe.com/tiff/1.0/"
},
"exif:GPSLatitude": "39,21.102N",
"exif:GPSLongitude": "74,26.5737W",
"tiff:Make": "CameraCompany",
"tiff:Model": "Shooter S1"
}),
)?;
let mut source = Cursor::new(SOURCE_IMAGE);
let mut signed_asset = Cursor::new(Vec::new());
builder.save_to_stream(format, &mut source, &mut signed_asset)?;
signed_asset.set_position(0);
let reader = Reader::from_shared_context(&context).with_stream(format, &mut signed_asset)?;
let manifest = reader
.active_manifest()
.ok_or(anyhow::anyhow!("no manifest"))?;
let redacted_uri = manifest
.assertion_references()
.find(|r| r.url().contains("c2pa.metadata"))
.map(|r| r.url())
.expect("c2pa.metadata assertion reference should exist");
println!("Redaction target URI: {redacted_uri}");
let mut update_builder = Builder::from_shared_context(&context);
update_builder.set_intent(BuilderIntent::Update);
update_builder.definition.redactions = Some(vec![redacted_uri.clone()]);
let redacted_action = Action::new(c2pa_action::REDACTED)
.set_reason(c2pa_reason::PII_PRESENT)
.set_parameter("redacted", &redacted_uri)?;
update_builder.add_action(redacted_action)?;
signed_asset.set_position(0);
let mut output = Cursor::new(Vec::new());
update_builder.save_to_stream(format, &mut signed_asset, &mut output)?;
output.set_position(0);
let reader = Reader::from_shared_context(&context).with_stream(format, &mut output)?;
assert_eq!(reader.validation_state(), ValidationState::Trusted);
let manifest = reader
.active_manifest()
.ok_or(anyhow::anyhow!("no manifest"))?;
assert!(
manifest
.redactions()
.is_some_and(|r| r.iter().any(|uri| uri == &redacted_uri)),
"active manifest should list the redacted URI"
);
println!("Verified - c2pa.metadata assertion successfully redacted");
println!("\nFull manifest store:\n{}", reader.json());
Ok(())
}
#[cfg(test)]
mod tests {
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::*;
use super::*;
#[test]
fn test_redaction_example() -> Result<()> {
main()
}
}