diff --git a/include/c2pa.h b/include/c2pa.h index 4e7b49d..62cb536 100644 --- a/include/c2pa.h +++ b/include/c2pa.h @@ -318,6 +318,19 @@ int c2pa_reader_resource_to_stream(struct C2paReader *reader_ptr, const char *uri, struct CStream *stream); +/** + * Writes all the resources in a Reader to a folder. + * + * # Parameters + * * reader_ptr: pointer to a Reader. + * * folder_path: pointer to a C string with the path to the folder. + * + * # Errors + * Returns -1 if there were errors, otherwise returns 0. + * The error string can be retrieved by calling c2pa_error. + */ +IMPORT extern int c2pa_reader_to_folder(struct C2paReader *reader_ptr, const char *folder_path); + /** * Creates a C2paBuilder from a JSON manifest definition string. * @@ -662,7 +675,7 @@ struct CStream *c2pa_create_stream(struct StreamContext *context, IMPORT extern void c2pa_release_stream(struct CStream *stream); #ifdef __cplusplus -} // extern "C" -#endif // __cplusplus +} // extern "C" +#endif // __cplusplus -#endif /* c2pa_bindings_h */ +#endif /* c2pa_bindings_h */ diff --git a/include/c2pa.hpp b/include/c2pa.hpp index edbe267..ef0c3bd 100644 --- a/include/c2pa.hpp +++ b/include/c2pa.hpp @@ -152,6 +152,13 @@ namespace c2pa /// @return The number of bytes written. /// @throws C2pa::Exception for errors encountered by the C2PA library. int get_resource(const string &uri, std::ostream &stream); + + /// @brief Write the manifest json and all resources to a folder. + /// @details There is no stream version of this function. + /// @param path The path to write the folder to. + /// + /// @throws C2pa::Exception for errors encountered by the C2PA library. + void to_folder(const std::filesystem::path &path); }; /// @brief Signer Callback function type. diff --git a/src/c2pa.cpp b/src/c2pa.cpp index 41e88b5..6129e47 100644 --- a/src/c2pa.cpp +++ b/src/c2pa.cpp @@ -527,6 +527,15 @@ namespace c2pa return result; } + void Reader::to_folder(const std::filesystem::path &path) + { + int result = c2pa_reader_to_folder(c2pa_reader, path.c_str()); + if (result < 0) + { + throw Exception(); + } + } + intptr_t signer_passthrough(const void *context, const unsigned char *data, uintptr_t len, unsigned char *signature, uintptr_t sig_max_len) { try diff --git a/src/c_api.rs b/src/c_api.rs index 4fd7aa7..c23839e 100644 --- a/src/c_api.rs +++ b/src/c_api.rs @@ -465,6 +465,33 @@ pub unsafe extern "C" fn c2pa_reader_resource_to_stream( } } +/// Writes all the resources in a Reader to a folder. +/// +/// # Parameters +/// * reader_ptr: pointer to a Reader. +/// * folder_path: pointer to a C string with the path to the folder. +/// +/// # Errors +/// Returns -1 if there were errors, otherwise returns 0. +/// The error string can be retrieved by calling c2pa_error. +#[no_mangle] +pub unsafe extern "C" fn c2pa_reader_to_folder( + reader_ptr: *mut C2paReader, + folder_path: *const c_char, +) -> c_int { + let c2pa_reader: Box = Box::from_raw(reader_ptr); + let folder_path = from_cstr_null_check_int!(folder_path); + let result = c2pa_reader.to_folder(folder_path); + let _ = Box::into_raw(c2pa_reader); + match result { + Ok(_) => 0, + Err(err) => { + Error::from_c2pa_error(err).set_last(); + -1 + } + } +} + /// Creates a C2paBuilder from a JSON manifest definition string. /// /// # Errors diff --git a/tests/builder.test.cpp b/tests/builder.test.cpp index b3046d7..e85dc0a 100644 --- a/tests/builder.test.cpp +++ b/tests/builder.test.cpp @@ -104,7 +104,7 @@ TEST(Builder, SignStream) dest.seekp(0, std::ios::beg); auto reader = c2pa::Reader("image/jpeg", dest); auto json = reader.json(); - ASSERT_TRUE(json.find("c2pa.training-mining") != std::string::npos); + ASSERT_TRUE(json.find("cawg.training-mining") != std::string::npos); } catch (c2pa::Exception const &e) { diff --git a/tests/reader.test.cpp b/tests/reader.test.cpp index 5f61a77..27e944c 100644 --- a/tests/reader.test.cpp +++ b/tests/reader.test.cpp @@ -10,9 +10,14 @@ // specific language governing permissions and limitations under // each license. -#include + #include +#include +#include #include +#include + +namespace fs = std::filesystem; using nlohmann::json; @@ -33,6 +38,24 @@ TEST(Reader, FileWithManifest) EXPECT_TRUE(manifest_store_json.find("C.jpg") != std::string::npos); }; +TEST(Reader, ToFolder) +{ + // Define the target directory + fs::path target_dir = "../../target/dest"; + + // Delete the target directory if it exists + if (fs::exists(target_dir)) { + fs::remove_all(target_dir); + } + + auto reader = c2pa::Reader("../../tests/fixtures/C.jpg"); + + reader.to_folder(target_dir); + + // Expect the manifest.json file to exist in the target directory + EXPECT_TRUE(fs::exists(target_dir / "manifest.json")); +}; + TEST(Reader, FileNoManifest) { EXPECT_THROW({ auto reader = c2pa::Reader("../../tests/fixtures/A.jpg"); }, c2pa::Exception);