@@ -37,6 +37,9 @@ use crate::{
37
37
AsyncSigner , ClaimGeneratorInfo , Signer ,
38
38
} ;
39
39
40
+ /// Version of the Builder Archive file
41
+ const ARCHIVE_VERSION : & str = "1" ;
42
+
40
43
/// A Manifest Definition
41
44
/// This is used to define a manifest and is used to build a ManifestStore
42
45
/// A Manifest is a collection of ingredients and assertions
@@ -259,8 +262,7 @@ impl Builder {
259
262
let mut resource = Vec :: new ( ) ;
260
263
stream. read_to_end ( & mut resource) ?;
261
264
// add the resource and set the resource reference
262
- self . resources
263
- . add ( self . definition . instance_id . clone ( ) , resource) ?;
265
+ self . resources . add ( & self . definition . instance_id , resource) ?;
264
266
self . definition . thumbnail = Some ( ResourceRef :: new (
265
267
format,
266
268
self . definition . instance_id . clone ( ) ,
@@ -371,25 +373,36 @@ impl Builder {
371
373
let mut zip = ZipWriter :: new ( stream) ;
372
374
let options =
373
375
FileOptions :: default ( ) . compression_method ( zip:: CompressionMethod :: Stored ) ;
376
+ // write a version file
377
+ zip. start_file ( "version.txt" , options)
378
+ . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
379
+ zip. write_all ( ARCHIVE_VERSION . as_bytes ( ) ) ?;
380
+ // write the manifest.json file
374
381
zip. start_file ( "manifest.json" , options)
375
382
. map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
376
383
zip. write_all ( & serde_json:: to_vec ( self ) ?) ?;
377
- // add a folder to the zip file
384
+ // add resource files to a resources folder
378
385
zip. start_file ( "resources/" , options)
379
386
. map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
380
387
for ( id, data) in self . resources . resources ( ) {
381
388
zip. start_file ( format ! ( "resources/{}" , id) , options)
382
389
. map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
383
390
zip. write_all ( data) ?;
384
391
}
385
- for ( index, ingredient) in self . definition . ingredients . iter ( ) . enumerate ( ) {
386
- zip. start_file ( format ! ( "ingredients/{}/" , index) , options)
387
- . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
388
- for ( id, data) in ingredient. resources ( ) . resources ( ) {
389
- //println!("adding ingredient {}/{}", index, id);
390
- zip. start_file ( format ! ( "ingredients/{}/{}" , index, id) , options)
391
- . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
392
- zip. write_all ( data) ?;
392
+ // Write the manifest_data files
393
+ // The filename is filesystem safe version of the associated manifest_label
394
+ // with a .c2pa extension inside a "manifests" folder.
395
+ zip. start_file ( "manifests/" , options)
396
+ . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
397
+ for ingredient in self . definition . ingredients . iter ( ) {
398
+ if let Some ( manifest_label) = ingredient. active_manifest ( ) {
399
+ if let Some ( manifest_data) = ingredient. manifest_data ( ) {
400
+ // Convert to valid archive / file path name
401
+ let manifest_name = manifest_label. replace ( [ ':' ] , "_" ) + ".c2pa" ;
402
+ zip. start_file ( format ! ( "manifests/{manifest_name}" ) , options)
403
+ . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
404
+ zip. write_all ( & manifest_data) ?;
405
+ }
393
406
}
394
407
}
395
408
zip. finish ( )
@@ -408,14 +421,16 @@ impl Builder {
408
421
/// * If the archive cannot be read.
409
422
pub fn from_archive ( stream : impl Read + Seek ) -> Result < Self > {
410
423
let mut zip = ZipArchive :: new ( stream) . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
411
- let mut manifest = zip
424
+ // First read the manifest.json file.
425
+ let mut manifest_file = zip
412
426
. by_name ( "manifest.json" )
413
427
. map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
414
- let mut manifest_json = Vec :: new ( ) ;
415
- manifest . read_to_end ( & mut manifest_json ) ?;
428
+ let mut manifest_buf = Vec :: new ( ) ;
429
+ manifest_file . read_to_end ( & mut manifest_buf ) ?;
416
430
let mut builder: Builder =
417
- serde_json:: from_slice ( & manifest_json) . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
418
- drop ( manifest) ;
431
+ serde_json:: from_slice ( & manifest_buf) . map_err ( |e| Error :: OtherError ( Box :: new ( e) ) ) ?;
432
+ drop ( manifest_file) ;
433
+ // Load all the files in the resources folder.
419
434
for i in 0 ..zip. len ( ) {
420
435
let mut file = zip
421
436
. by_index ( i)
@@ -432,6 +447,29 @@ impl Builder {
432
447
//println!("adding resource {}", id);
433
448
builder. resources . add ( id, data) ?;
434
449
}
450
+
451
+ // Load the c2pa_manifests.
452
+ // We add the manifest data to any ingredient that has a matching active_manfiest label.
453
+ if file. name ( ) . starts_with ( "manifests/" ) && file. name ( ) != "manifests/" {
454
+ let mut data = Vec :: new ( ) ;
455
+ file. read_to_end ( & mut data) ?;
456
+ let manifest_label = file
457
+ . name ( )
458
+ . split ( '/' )
459
+ . nth ( 1 )
460
+ . ok_or ( Error :: BadParam ( "Invalid manifest path" . to_string ( ) ) ) ?;
461
+ let manifest_label = manifest_label. replace ( [ '_' ] , ":" ) ;
462
+ for ingredient in builder. definition . ingredients . iter_mut ( ) {
463
+ if let Some ( active_manifest) = ingredient. active_manifest ( ) {
464
+ if manifest_label. starts_with ( active_manifest) {
465
+ ingredient. set_manifest_data ( data. clone ( ) ) ?;
466
+ }
467
+ }
468
+ }
469
+ }
470
+
471
+ // Keep this for temporary unstable api support (un-versioned).
472
+ // Earlier method used numbered library folders instead of manifests.
435
473
if file. name ( ) . starts_with ( "ingredients/" ) && file. name ( ) != "ingredients/" {
436
474
let mut data = Vec :: new ( ) ;
437
475
file. read_to_end ( & mut data) ?;
@@ -465,7 +503,7 @@ impl Builder {
465
503
// add the default claim generator info for this library
466
504
claim_generator_info. push ( ClaimGeneratorInfo :: default ( ) ) ;
467
505
468
- // build the claim_generator string since this is required
506
+ // Build the claim_generator string since this is required
469
507
let claim_generator: String = claim_generator_info
470
508
. iter ( )
471
509
. map ( |s| {
@@ -493,7 +531,7 @@ impl Builder {
493
531
claim. add_claim_generator_info ( claim_info) ;
494
532
}
495
533
496
- // add claim metadata
534
+ // Add claim metadata
497
535
if let Some ( metadata_vec) = metadata {
498
536
for m in metadata_vec {
499
537
claim. add_claim_metadata ( m) ;
@@ -972,6 +1010,11 @@ mod tests {
972
1010
. add ( "thumbnail1.jpg" , TEST_IMAGE . to_vec ( ) )
973
1011
. unwrap ( ) ;
974
1012
1013
+ builder
1014
+ . resources
1015
+ . add ( "prompt.txt" , "a random prompt" )
1016
+ . unwrap ( ) ;
1017
+
975
1018
builder
976
1019
. add_assertion ( "org.life.meaning" , & TestAssertion { answer : 42 } )
977
1020
. unwrap ( ) ;
0 commit comments