c2pa-cpp
C++ API for the C2PA SDK
Loading...
Searching...
No Matches
c2pa.hpp
Go to the documentation of this file.
1// Copyright 2024 Adobe. All rights reserved.
2// This file is licensed to you under the Apache License,
3// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
4// or the MIT license (http://opensource.org/licenses/MIT),
5// at your option.
6// Unless required by applicable law or agreed to in writing,
7// this software is distributed on an "AS IS" BASIS, WITHOUT
8// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or
9// implied. See the LICENSE-MIT and LICENSE-APACHE files for the
10// specific language governing permissions and limitations under
11// each license.
12
13/// @file c2pa.hpp
14/// @brief C++ wrapper for the C2PA C library.
15/// @details This is used for creating and verifying C2PA manifests.
16/// This is an early version, and has not been fully tested.
17/// Thread safety is not guaranteed due to the use of errno and etc.
18
19#ifndef C2PA_H
20#define C2PA_H
21
22// Suppress unused function warning for GCC/Clang
23#ifdef __GNUC__
24#pragma GCC diagnostic push
25#pragma GCC diagnostic ignored "-Wunused-function"
26#endif
27
28// Suppress unused function warning for MSVC
29#ifdef _MSC_VER
30#pragma warning(push)
31#pragma warning(disable : 4505)
32#endif
33
34#include <cerrno>
35#include <filesystem>
36#include <fstream>
37#include <functional>
38#include <istream>
39#include <ostream>
40#include <string>
41#include <vector>
42#include <optional>
43#include <memory>
44#include <utility>
45
46#include "c2pa.h"
47
48// NOOP for now, can use later to define static library
49#define C2PA_CPP_API
50
51namespace c2pa
52{
53 /// @typedef SignerInfo
54 /// @brief Type alias for C2paSignerInfo from the C API.
55 typedef C2paSignerInfo SignerInfo;
56
57 // Forward declarations for context types
58 class Settings;
59 class Context;
60 class IContextProvider;
61 class Signer;
62
63 /// @brief Result codes for C API operations (matches C API return convention).
64 enum class OperationResult : int {
65 Success = 0, ///< Operation succeeded
66 Error = -1 ///< Operation failed (check C2paException for details)
67 };
68
69 /// @brief Stream/FFI error codes (maps to errno values used by the C layer).
70 enum class StreamError : int {
71 InvalidArgument = EINVAL,
72 IoError = EIO,
73 NoBufferSpace = ENOBUFS
74 };
75
76 /// @brief Set errno from StreamError and return error sentinel.
77 /// @param e The StreamError value to convert to errno.
78 /// @return OperationResult::Error (-1) for use as C API error return.
79 inline int stream_error_return(StreamError e) noexcept {
80 errno = static_cast<int>(e);
81 return static_cast<int>(OperationResult::Error);
82 }
83
84 /// @brief Exception class for C2pa errors.
85 /// This class is used to throw exceptions for errors encountered by the C2pa library via c2pa_error().
86 class C2PA_CPP_API C2paException : public std::exception
87 {
88 public:
89 /// @brief Default constructor.
90 /// @details Creates an exception and retrieves the error message from the C2PA library.
92
93 /// @brief Construct an exception with a custom error message.
94 /// @param message The error message.
95 explicit C2paException(std::string message);
96
97 ~C2paException() override = default;
98
99 C2paException(const C2paException&) = default;
100
102
104
106
107 /// @brief Get the exception message.
108 /// @return Null-terminated error message string.
109 const char* what() const noexcept override;
110
111 private:
112 std::string message_;
113 };
114
115 /// @brief Interface for types that can provide C2PA context functionality.
116 /// @details This interface can be implemented by external libraries to provide
117 /// custom context implementations (e.g. AdobeContext wrappers).
118 /// Reader and Builder accept a shared_ptr<IContextProvider> and extend
119 /// the provider's lifetime for as long as the Reader/Builder exists.
120 ///
121 /// @par Progress callback lifetime (post-0322d67)
122 /// If the native C2paContext* held by the provider was configured with a progress
123 /// callback, the provider MUST keep the heap-owned ProgressCallbackFunc alive at
124 /// least as long as the native context. The native side stores only a raw pointer
125 /// into that heap block and will call it until the context is freed. Destroy
126 /// order inside the provider: free the native C2paContext* first (stops further
127 /// callback invocations), then release the callback storage. The built-in
128 /// Context class enforces this via member declaration order; external providers
129 /// that adopt a native context built via ContextBuilder::release() must do the
130 /// same.
131 ///
132 /// @par Move semantics
133 /// Move construction and move assignment are defaulted. After move, the moved-from
134 /// object is left in a valid but unspecified state: is_valid() may be false and
135 /// c_context() may return nullptr. Implementations that own a C2paContext* (e.g. Context)
136 /// must set the source's handle to nullptr on move to avoid double-free; callers must
137 /// not use a moved-from provider without checking is_valid() first.
138 ///
139 /// @par Implementation Requirements for is_valid()
140 /// The is_valid() method exists to support implementations that may have:
141 /// - Optional or lazy context initialization
142 /// - Contexts that can be invalidated or moved
143 /// - A "no context" state as part of their lifecycle
144 ///
145 /// @par Why Both c_context() and is_valid()?
146 /// While c_context() can return nullptr, is_valid() provides:
147 /// 1. A boolean check without pointer inspection (yes/no answer for intialization)
148 /// 2. Forward compatibility for implementations with complex context lifecycles (lazy load)
149 ///
150 /// @par Impact on Reader and Builder
151 /// Reader and Builder constructors validate that a provider both exists and
152 /// is_valid() returns true before using c_context(). This ensures that:
153 /// - External implementations cannot be used in an uninitialized state
154 /// - A consistent validation pattern exists across all context-using classes
155 /// - Errors are caught early at construction time rather than during operations
156 ///
157 /// @par Standard Context Implementation
158 /// The built-in Context class always returns true from is_valid() after
159 /// successful construction, as it validates the context pointer in its constructor.
160 /// External implementations may have different invariants.
162 public:
163 virtual ~IContextProvider() noexcept = default;
164
165 /// @brief Get the underlying C2PA context pointer for FFI operations.
166 /// @return Pointer to C2paContext, or nullptr if not available.
167 /// @note Provider retains ownership; pointer valid for provider's lifetime.
168 [[nodiscard]] virtual C2paContext* c_context() const noexcept = 0;
169
170 /// @brief Check if this provider has a valid context.
171 /// @return true if context is available, false otherwise.
172 /// @note For standard Context objects, this always returns true after construction.
173 /// External implementations may return false to indicate uninitialized or
174 /// invalidated state. Reader and Builder constructors check this before use.
175 /// @warning Implementations must ensure is_valid() == true implies c_context() != nullptr.
176 [[nodiscard]] virtual bool is_valid() const noexcept = 0;
177
178 protected:
179 IContextProvider() = default;
180
182 IContextProvider& operator=(const IContextProvider&) = delete;
183 };
184
185 /// @brief (C2PA SDK) Settings configuration object for creating contexts.
186 /// @details Settings can be configured via JSON strings or programmatically
187 /// via set() and update() methods. Once passed to Context::ContextBuilder,
188 /// the settings are copied into the context and the Settings
189 /// object can be reused or discarded.
190 ///
191 /// @par Validity
192 /// Settings uses is_valid() to indicate whether the object holds a valid underlying
193 /// C settings handle. After move-from, is_valid() is false. set(), update(), and callers
194 /// passing this object to the C API must ensure is_valid() is true or check before use.
196 public:
197 /// @brief Create default settings.
199
200 /// @brief Create settings from a configuration string.
201 /// @param data Configuration data in JSON format.
202 /// @param format Format of the data ("json").
203 /// @throws C2paException if parsing fails.
204 Settings(const std::string& data, const std::string& format);
205
206 // Move semantics
207 Settings(Settings&&) noexcept;
208 Settings& operator=(Settings&&) noexcept;
209
210 // Non-copyable
211 Settings(const Settings&) = delete;
212 Settings& operator=(const Settings&) = delete;
213
214 ~Settings() noexcept;
215
216 /// @brief Check if this Settings object is valid (holds a C settings handle).
217 /// @return true if the object can be used (set, update, c_settings, or passed to Context/ContextBuilder).
218 /// @return false if moved-from (or otherwise invalid). Callers must check before use.
219 [[nodiscard]] bool is_valid() const noexcept;
220
221 /// @brief Set a single configuration value by path.
222 /// @param path Dot-separated path to the setting (e.g., "verify.verify_after_sign").
223 /// @param json_value JSON-encoded value to set.
224 /// @return Reference to this Settings for method chaining.
225 /// @throws C2paException if the path or value is invalid.
226 Settings& set(const std::string& path, const std::string& json_value);
227
228 /// @brief Merge configuration from a JSON string (latest configuration wins).
229 /// @param data Configuration data in JSON format.
230 /// @return Reference to this Settings for method chaining.
231 /// @throws C2paException if parsing fails, or if this object is invalid.
232 /// @note This is the recommended overload when configuration is JSON.
233 Settings& update(const std::string& data) { return update(data, "json"); }
234
235 /// @brief Merge configuration from a std::string (latest configuration wins).
236 /// @param data Configuration data in JSON or TOML format.
237 /// @param format Format of the data ("json" or "toml").
238 /// @return Reference to this Settings for method chaining.
239 /// @throws C2paException if parsing fails, or if this object is invalid.
240 Settings& update(const std::string& data, const std::string& format);
241
242 /// @brief Get the raw C FFI settings pointer.
243 /// @return Pointer to C2paSettings when is_valid() is true; nullptr when invalid.
244 /// @note Callers passing this to the C API should check is_valid() first and treat nullptr as invalid.
245 [[nodiscard]] C2paSettings* c_settings() const noexcept;
246
247 private:
248 C2paSettings* settings_ptr;
249 };
250
251 /// @brief Phase values reported to the ProgressCallbackFunc.
252 ///
253 /// @details A scoped C++ mirror of `C2paProgressPhase` from c2pa.h.
254 /// Values are verified at compile time to match the C enum, so any
255 /// future divergence in c2pa-rs will be caught as a build error.
256 ///
257 /// Phases emitted during a typical sign cycle (in order):
258 /// AddingIngredient → Thumbnail → Hashing → Signing → Embedding →
259 /// (if verify_after_sign) VerifyingManifest → VerifyingSignature →
260 /// VerifyingAssetHash → VerifyingIngredient
261 ///
262 /// Phases emitted during reading:
263 /// Reading → VerifyingManifest → VerifyingSignature →
264 /// VerifyingAssetHash → VerifyingIngredient
265 enum class ProgressPhase : uint8_t {
266 Reading = 0,
272 Thumbnail = 6,
273 Hashing = 7,
274 Signing = 8,
275 Embedding = 9,
277 Writing = 11,
278 FetchingOCSP = 12,
280 };
281
282 /// @brief Type alias for the progress callback passed to ContextBuilder::with_progress_callback().
283 ///
284 /// @details The callback is invoked at each major phase of signing and reading operations.
285 /// Returning false from the callback aborts the operation with an
286 /// OperationCancelled error (equivalent to calling Context::cancel()).
287 ///
288 /// @param phase Current operation phase.
289 /// @param step 1-based step index within the phase.
290 /// 0 = indeterminate (use as liveness signal); resets to 1 at each new phase.
291 /// @param total 0 = indeterminate; 1 = single-shot; >1 = determinate (step/total = fraction).
292 /// @return true to continue the operation, false to request cancellation.
293 ///
294 /// @note The callback must not throw. If it throws, the implementation catches the
295 /// exception and reports cancellation to the underlying library (same as returning
296 /// false); the original exception is not propagated. Prefer returning false or
297 /// using Context::cancel() instead of throwing.
298 ///
299 using ProgressCallbackFunc = std::function<bool(ProgressPhase phase, uint32_t step, uint32_t total)>;
300
301 /// @brief C2PA context implementing IContextProvider.
302 /// @details Context objects manage C2PA SDK configuration and state.
303 /// Contexts can be created via direct construction or the ContextBuilder:
304 ///
305 /// Direct construction:
306 /// @code
307 /// c2pa::Context ctx; // default
308 /// c2pa::Context ctx(settings); // from Settings
309 /// c2pa::Context ctx(json); // from JSON string
310 /// @endcode
311 ///
312 /// ContextBuilder (for multi-step configuration):
313 /// @code
314 /// auto ctx = c2pa::Context::ContextBuilder()
315 /// .with_settings(settings)
316 /// .with_json(json)
317 /// .create_context();
318 /// @endcode
319 ///
320 /// Builder and Reader take the context by reference (IContextProvider&).
321 /// The context object must outlive the Builder or Reader instance.
323 public:
324 /// @brief ContextBuilder for creating customized Context instances.
325 /// @details Provides a builder pattern for configuring contexts with multiple settings.
326 /// Note: create_context() consumes the builder.
327 /// @note For most use cases, prefer direct construction via the Context constructors.
328 /// The ContextBuilder is useful when you need to layer multiple configuration
329 /// sources (e.g. with_settings() followed by with_json()).
331 public:
333 ~ContextBuilder() noexcept;
334
335 // Move semantics
337 ContextBuilder& operator=(ContextBuilder&&) noexcept;
338
339 // Non-copyable
341 ContextBuilder& operator=(const ContextBuilder&) = delete;
342
343 /// @brief Check if the builder is in a valid state.
344 /// @return true if the builder can be used, false if moved from.
345 [[nodiscard]] bool is_valid() const noexcept;
346
347 /// @brief Configure with Settings object.
348 /// @param settings Settings to use (will be copied into the context). Must be valid (is_valid() true).
349 /// @return Reference to this ContextBuilder for method chaining.
350 /// @throws C2paException if settings are invalid or settings.is_valid() is false.
351 ContextBuilder& with_settings(const Settings& settings);
352
353 /// @brief Configure settings with JSON string.
354 /// @param json JSON configuration string.
355 /// @return Reference to this ContextBuilder for method chaining.
356 /// @throws C2paException if JSON is invalid.
357 ContextBuilder& with_json(const std::string& json);
358
359 /// @brief Configure settings from a JSON settings file.
360 /// @param settings_path Full path to the JSON settings file.
361 /// @return Reference to this ContextBuilder for method chaining.
362 /// @throws C2paException if file cannot be read or JSON is invalid.
363 ContextBuilder& with_json_settings_file(const std::filesystem::path& settings_path);
364
365 /// @brief Set a Signer on the context being built.
366 /// @details After this call the source Signer object is consumed and must
367 /// not be reused, as it becomes part to the context and tied to it.
368 /// If settings also contain a signer, the programmatic signer
369 /// set through this API will be used for signing.
370 /// @param signer Signer to put into the context.
371 /// @return Reference to this ContextBuilder for method chaining.
372 /// @throws C2paException if the builder or signer is invalid.
373 ContextBuilder& with_signer(Signer&& signer);
374
375 /// @brief Attach a progress callback to the context being built.
376 ///
377 /// @details The callback is invoked at each major phase of signing and
378 /// reading operations performed with the resulting context.
379 /// Return false from the callback to abort the current operation
380 /// with an OperationCancelled error.
381 ///
382 /// Phases emitted during a typical sign cycle (in order):
383 /// VerifyingIngredient → VerifyingManifest → VerifyingSignature →
384 /// VerifyingAssetHash → Thumbnail → Hashing → Signing → Embedding →
385 /// (if verify_after_sign) VerifyingManifest → … → VerifyingIngredient
386 ///
387 /// Phases emitted during reading:
388 /// Reading → VerifyingManifest → VerifyingSignature →
389 /// VerifyingAssetHash → VerifyingIngredient
390 ///
391 /// @param callback A callable matching ProgressCallbackFunc. The callback is
392 /// heap-allocated and owned by the resulting Context. Calling this method
393 /// more than once on the same builder replaces the previous callback.
394 /// The callable must not throw when invoked (see ProgressCallbackFunc).
395 /// @return Reference to this ContextBuilder for method chaining.
396 /// @throws C2paException if the builder is invalid or the C API call fails.
397 ///
398 ContextBuilder& with_progress_callback(ProgressCallbackFunc callback);
399
400 /// @brief Set a custom HTTP resolver callback on the context being built.
401 /// @details The callback is invoked synchronously whenever the SDK needs to
402 /// make an HTTP request (remote manifest fetch, OCSP, timestamp, etc.).
403 /// The callback receives a C2paHttpRequest and must fill in a C2paHttpResponse.
404 /// The response body must be allocated with malloc(); the underlying native library will call free().
405 /// @param user_data Opaque user pointer passed to every callback invocation (may be nullptr).
406 /// @param callback Function pointer matching C2paHttpResolverCallback.
407 /// @return Reference to this ContextBuilder for method chaining.
408 /// @throws C2paException if the resolver could not be set or callback is null.
409 ContextBuilder& with_http_resolver(void* user_data, C2paHttpResolverCallback callback);
410
411 /// @brief Create a Context from the current builder configuration.
412 /// @return A new Context instance.
413 /// @throws C2paException if context creation fails.
414 /// @note This consumes the builder. After calling this, is_valid() returns false.
415 [[nodiscard]] Context create_context();
416
417 /// @brief Result of ContextBuilder::release(): the raw native builder handle
418 /// paired with the heap-owned progress callback (if any).
419 /// @details The caller takes joint ownership. If `callback_owner` is non-null,
420 /// the native side stores a raw pointer into it and will invoke the
421 /// callback until the native context built from `builder` is freed.
422 /// Therefore `callback_owner` must remain alive at least until the
423 /// native context returned by c2pa_context_builder_build(builder) is
424 /// freed via c2pa_free(). Release order: native context first, then
425 /// `callback_owner` goes out of scope.
427 C2paContextBuilder* builder;
428 std::unique_ptr<ProgressCallbackFunc> callback_owner;
429 };
430
431 /// @brief Release ownership of the underlying C2paContextBuilder handle and
432 /// its progress-callback heap block (if any) to the caller.
433 /// @details After this call is_valid() returns false. The previous
434 /// C2paContextBuilder* overload was unsound when a progress callback
435 /// was pending: the C++ builder destructor would free the heap
436 /// ProgressCallbackFunc while the native side still held its pointer,
437 /// yielding a use-after-free on the first progress tick.
438 /// Callers must keep ReleasedBuilder::callback_owner alive for as
439 /// long as the native context built from ReleasedBuilder::builder
440 /// exists.
441 /// @return ReleasedBuilder holding both the native builder and (optionally)
442 /// the owning unique_ptr for the progress callback. Fields are null
443 /// when moved-from.
444 [[nodiscard]] ReleasedBuilder release() noexcept;
445
446 private:
447 // pending_callback_ is declared before context_builder
448 // so destruction (reverse of declaration) frees context_builder first, while
449 // the callback heap block is still live, then releases pending_callback_.
450 std::unique_ptr<ProgressCallbackFunc> pending_callback_;
451 C2paContextBuilder* context_builder;
452 };
453
454 // Direct construction
455 /// @brief Create a Context with default settings.
456 /// @throws C2paException if context creation fails.
458
459 /// @brief Create a Context configured with a Settings object.
460 /// @param settings Settings configuration to apply. Must be valid (settings.is_valid() true).
461 /// @throws C2paException if settings are invalid, settings.is_valid() is false, or context creation fails.
462 explicit Context(const Settings& settings);
463
464 /// @brief Create a Context configured with a JSON string.
465 /// @param json JSON configuration string.
466 /// @throws C2paException if JSON is invalid or context creation fails.
467 explicit Context(const std::string& json);
468
469 /// @brief Create a Context with a Settings object and a Signer.
470 /// @param settings Settings configuration to apply.
471 /// @param signer Signer to move into the context. Consumed after this call.
472 /// The programmatic Signer from the signer parameter
473 /// takes priority over the Signer in settings, so use this API
474 /// when wanting to explicitly set a Signer (or override the Signer in settings).
475 /// @throws C2paException if settings or signer are invalid, or context creation fails.
476 Context(const Settings& settings, Signer&& signer);
477
478 // Non-copyable, moveable
479 Context(const Context&) = delete;
480 Context& operator=(const Context&) = delete;
481 Context(Context&&) noexcept;
482 Context& operator=(Context&&) noexcept;
483
484 ~Context() noexcept override;
485
486 // IContextProvider implementation
487 /// @brief Get the underlying C2PA context pointer.
488 /// @return C2paContext pointer when is_valid() is true; nullptr when moved-from (is_valid() false).
489 /// @note Callers must check is_valid() before using the result; do not pass nullptr to the C API.
490 [[nodiscard]] C2paContext* c_context() const noexcept override;
491
492 /// @brief Check if this Context has a valid context (validity check for context-like types).
493 /// @return true when the object holds a valid C context; false when moved-from.
494 /// @note After move, is_valid() is false and c_context() returns nullptr.
495 [[nodiscard]] bool is_valid() const noexcept override;
496
497 /// @brief Internal constructor from raw FFI pointer (prefer public constructors).
498 /// @param ctx Raw C2paContext pointer — Context takes ownership.
499 /// @throws C2paException if ctx is nullptr.
500 explicit Context(C2paContext* ctx);
501
502 /// @brief "Adopt" a native context together with its heap-owned progress callback.
503 /// @param ctx Native context pointer. Context takes ownership.
504 /// @param callback Heap-owned progress callback whose raw pointer the native
505 /// context may hold. May be null.
506 Context(C2paContext* ctx,
507 std::unique_ptr<ProgressCallbackFunc> callback) noexcept;
508
509 /// @brief Request cancellation of any in-progress operation on this context.
510 ///
511 /// @details Safe to call from another thread while this Context remains valid
512 /// and is not being destroyed or moved concurrently with this call.
513 /// While a signing or reading operation is running on a valid Context,
514 /// the operation is aborted with an OperationCancelled error at the
515 /// next progress checkpoint. Has no effect if no operation is currently
516 /// in progress, or if this object is moved-from (is_valid() is false).
517 ///
518 void cancel() noexcept;
519
520 private:
521 // Member order matters: `context` is declared before `callback_owner_` so
522 // destruction (reverse of declaration) frees the heap callback block after
523 // the native context has been freed.
524 C2paContext* context;
525
526 /// Heap-owned ProgressCallbackFunc, non-null only when the Context was built
527 /// with a progress callback via ContextBuilder::with_progress_callback().
528 /// Destroyed after `context`.
529 std::unique_ptr<ProgressCallbackFunc> callback_owner_;
530 };
531
532 /// @brief Get the version of the C2PA library.
533 /// @return Version string.
534 std::string C2PA_CPP_API version();
535
536 /// @brief Load C2PA settings from a string in a given format.
537 /// @param data The configuration data to load.
538 /// @param format The mimetype of the string.
539 /// @throws C2paException for errors encountered by the C2PA library.
540 /// @deprecated Use Context constructors or Context::ContextBuilder instead for better thread safety.
541 [[deprecated("Use Context::from_json() or Context::from_settings() instead")]]
542 void C2PA_CPP_API load_settings(const std::string& data, const std::string& format);
543
544 /// @brief Read a file and return the manifest JSON.
545 /// @param source_path The path to the file to read.
546 /// @param data_dir Optional directory to store binary resources.
547 /// @return Optional string containing the manifest JSON if a manifest was found.
548 /// @throws C2paException for errors encountered by the C2PA library.
549 /// @deprecated Use Reader object instead.
550 [[deprecated("Use Reader object instead")]]
551 std::optional<std::string> C2PA_CPP_API read_file(const std::filesystem::path &source_path, const std::optional<std::filesystem::path> data_dir = std::nullopt);
552
553 /// @brief Read a file and return an ingredient JSON.
554 /// @param source_path The path to the file to read.
555 /// @param data_dir The directory to store binary resources.
556 /// @return String containing the ingredient JSON.
557 /// @throws C2paException for errors encountered by the C2PA library.
558 /// @deprecated Use Reader and Builder.add_ingredient instead.
559 [[deprecated("Use Reader and Builder.add_ingredient")]]
560 std::string C2PA_CPP_API read_ingredient_file(const std::filesystem::path &source_path, const std::filesystem::path &data_dir);
561
562 /// @brief Add a manifest and sign a file.
563 /// @param source_path The path to the asset to be signed.
564 /// @param dest_path The path to write the signed file to.
565 /// @param manifest The manifest JSON to add to the file.
566 /// @param signer_info The signer info to use for signing.
567 /// @param data_dir Optional directory to store binary resources.
568 /// @throws C2paException for errors encountered by the C2PA library.
569 /// @deprecated Use Builder.sign instead.
570 [[deprecated("Use Builder.sign instead")]]
571 void C2PA_CPP_API sign_file(const std::filesystem::path &source_path,
572 const std::filesystem::path &dest_path,
573 const char *manifest,
574 SignerInfo *signer_info,
575 const std::optional<std::filesystem::path> data_dir = std::nullopt);
576
577 /// @defgroup StreamWrappers Stream wrappers for C2PA C API
578 /// @brief C++ stream types that adapt stream types to C2paStream.
579 ///
580 /// The C2PA C API expects a C2paStream with four callbacks: reader, writer, seeker, flusher.
581 /// The contract for each callback is:
582 /// - reader(context, buffer, size): read up to size bytes into buffer; return bytes read, or -1 on error (set errno).
583 /// - writer(context, buffer, size): write size bytes from buffer; return bytes written, or -1 on error (set errno).
584 /// - seeker(context, offset, whence): seek to offset (whence = Start/Current/End); return new position or -1 (set errno).
585 /// - flusher(context): flush; return 0 on success, -1 on error (set errno).
586
587 /// @brief Input stream IStream wrapper for C2paStream.
588 /// @details This class is used to wrap an input stream for use with the C2PA library.
589 class C2PA_CPP_API CppIStream : public C2paStream
590 {
591 public:
592 /// @brief Pointer to the underlying C2paStream.
593 C2paStream *c_stream;
594
595 /// @brief Construct an input stream wrapper from a std::istream-derived object.
596 /// @tparam IStream Type derived from std::istream.
597 /// @param istream The input stream to wrap (must be open and valid).
598 /// @throws C2paException if stream wrapper creation fails.
599 template <typename IStream>
600 explicit CppIStream(IStream &istream) {
601 static_assert(std::is_base_of<std::istream, IStream>::value,
602 "Stream must be derived from std::istream");
603 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(&istream), reader, seeker, writer, flusher);
604 if (c_stream == nullptr) {
605 throw C2paException("Failed to create input stream wrapper: is stream open and valid?");
606 }
607 }
608
609 CppIStream(const CppIStream &) = delete;
610
611 CppIStream &operator=(const CppIStream &) = delete;
612
613 CppIStream(CppIStream &&) = delete;
614
616
618
619 private:
620 /// @brief Reader callback implementation.
621 /// @param context Stream context pointer.
622 /// @param buffer Buffer to read into.
623 /// @param size Number of bytes to read.
624 /// @return Number of bytes read, or -1 on error (sets errno).
625 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
626
627 /// @brief Writer callback implementation (not used for input streams).
628 /// @param context Stream context pointer.
629 /// @param buffer Buffer to write from.
630 /// @param size Number of bytes to write.
631 /// @return -1 (always fails for input streams).
632 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
633
634 /// @brief Seeker callback implementation.
635 /// @param context Stream context pointer.
636 /// @param offset Offset to seek to.
637 /// @param whence Seek mode (Start/Current/End).
638 /// @return New stream position, or -1 on error (sets errno).
639 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
640
641 /// @brief Flusher callback implementation (no-op for input streams).
642 /// @param context Stream context pointer.
643 /// @return 0 on success.
644 static intptr_t flusher(StreamContext *context);
645
646 friend class Reader;
647 };
648
649 /// @brief Output stream OStream wrapper for C2paStream.
650 /// @details This class is used to wrap an output stream for use with the C2PA library.
651 class C2PA_CPP_API CppOStream : public C2paStream
652 {
653 public:
654 /// @brief Pointer to the underlying C2paStream.
655 C2paStream *c_stream;
656
657 /// @brief Construct an output stream wrapper from a std::ostream-derived object.
658 /// @tparam OStream Type derived from std::ostream.
659 /// @param ostream The output stream to wrap (must be open and valid).
660 /// @throws C2paException if stream wrapper creation fails.
661 template <typename OStream>
662 explicit CppOStream(OStream &ostream) {
663 static_assert(std::is_base_of<std::ostream, OStream>::value, "Stream must be derived from std::ostream");
664 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(&ostream), reader, seeker, writer, flusher);
665 if (c_stream == nullptr) {
666 throw C2paException("Failed to create output stream wrapper: is stream open and valid?");
667 }
668 }
669
670 CppOStream(const CppOStream &) = delete;
671
672 CppOStream &operator=(const CppOStream &) = delete;
673
674 CppOStream(CppOStream &&) = delete;
675
677
679
680 private:
681 /// @brief Reader callback implementation (not used for output streams).
682 /// @param context Stream context pointer.
683 /// @param buffer Buffer to read into.
684 /// @param size Number of bytes to read.
685 /// @return -1 (always fails for output streams).
686 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
687
688 /// @brief Writer callback implementation.
689 /// @param context Stream context pointer.
690 /// @param buffer Buffer to write from.
691 /// @param size Number of bytes to write.
692 /// @return Number of bytes written, or -1 on error (sets errno).
693 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
694
695 /// @brief Seeker callback implementation.
696 /// @param context Stream context pointer.
697 /// @param offset Offset to seek to.
698 /// @param whence Seek mode (Start/Current/End).
699 /// @return New stream position, or -1 on error (sets errno).
700 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
701
702 /// @brief Flusher callback implementation.
703 /// @param context Stream context pointer.
704 /// @return 0 on success, -1 on error (sets errno).
705 static intptr_t flusher(StreamContext *context);
706 };
707
708 /// @brief IOStream Class wrapper for C2paStream.
709 /// @details This class is used to wrap an input/output stream for use with the C2PA library.
710 class C2PA_CPP_API CppIOStream : public C2paStream
711 {
712 public:
713 /// @brief Pointer to the underlying C2paStream.
714 C2paStream *c_stream;
715
716 /// @brief Construct an I/O stream wrapper from a std::iostream-derived object.
717 /// @tparam IOStream Type derived from std::iostream.
718 /// @param iostream The I/O stream to wrap (must be open and valid).
719 /// @throws C2paException if stream wrapper creation fails.
720 template <typename IOStream>
721 CppIOStream(IOStream &iostream) {
722 static_assert(std::is_base_of<std::iostream, IOStream>::value, "Stream must be derived from std::iostream");
723 c_stream = c2pa_create_stream(reinterpret_cast<StreamContext *>(&iostream), reader, seeker, writer, flusher);
724 if (c_stream == nullptr) {
725 throw C2paException("Failed to create I/O stream wrapper: is stream open and valid?");
726 }
727 }
728
729 CppIOStream(const CppIOStream &) = delete;
730
732
734
736
738
739 private:
740 /// @brief Reader callback implementation.
741 /// @param context Stream context pointer.
742 /// @param buffer Buffer to read into.
743 /// @param size Number of bytes to read.
744 /// @return Number of bytes read, or -1 on error (sets errno).
745 static intptr_t reader(StreamContext *context, uint8_t *buffer, intptr_t size);
746
747 /// @brief Writer callback implementation.
748 /// @param context Stream context pointer.
749 /// @param buffer Buffer to write from.
750 /// @param size Number of bytes to write.
751 /// @return Number of bytes written, or -1 on error (sets errno).
752 static intptr_t writer(StreamContext *context, const uint8_t *buffer, intptr_t size);
753
754 /// @brief Seeker callback implementation.
755 /// @param context Stream context pointer.
756 /// @param offset Offset to seek to.
757 /// @param whence Seek mode (Start/Current/End).
758 /// @return New stream position, or -1 on error (sets errno).
759 static intptr_t seeker(StreamContext *context, intptr_t offset, C2paSeekMode whence);
760
761 /// @brief Flusher callback implementation.
762 /// @param context Stream context pointer.
763 /// @return 0 on success, -1 on error (sets errno).
764 static intptr_t flusher(StreamContext *context);
765 };
766
767 /// @brief Reader class for reading a manifest.
768 /// @details This class is used to read and validate a manifest from a stream or file.
769 /// Resources are managed using RAII; member order ensures cpp_stream (which
770 /// holds a C stream pointing at the ifstream) is destroyed before owned_stream.
772 {
773 private:
774 C2paReader *c2pa_reader;
775 std::unique_ptr<std::ifstream> owned_stream; // Owns file stream when created from path
776 std::unique_ptr<CppIStream> cpp_stream; // Wraps stream for C API; destroyed before owned_stream
777 std::shared_ptr<IContextProvider> context_ref;
778
779 void init_from_context(IContextProvider& context, const std::string &format, std::istream &stream);
780 void init_from_context(IContextProvider& context, const std::filesystem::path &source_path);
781 Reader() : c2pa_reader(nullptr) {}
782
783 public:
784 /// @brief Create a Reader from a context and stream.
785 /// @param context Context provider; used at construction to configure settings.
786 /// @param format The mime format of the stream.
787 /// @param stream The input stream to read from.
788 /// @throws C2paException if context.is_valid() returns false,
789 /// or for other errors encountered by the C2PA library.
790 /// @deprecated Use Reader(std::shared_ptr<IContextProvider>, format, stream) instead.
791 /// The reference overload does not extend the lifetime of the context, which can
792 /// be problematic when progress callbacks fire after the context is destroyed.
793 [[deprecated("Use Reader(std::shared_ptr<IContextProvider>, format, stream) instead. "
794 "The reference overload does not extend the lifetime of the context, which can "
795 "be problematic when progress callbacks fire after the context is destroyed.")]]
796 Reader(IContextProvider& context, const std::string &format, std::istream &stream);
797
798 /// @brief Create a Reader from a context and file path.
799 /// @param context Context provider; used at construction only to configure settings.
800 /// @param source_path The path to the file to read.
801 /// @throws C2paException if context.is_valid() returns false,
802 /// or for other errors encountered by the C2PA library.
803 /// @note Prefer using the streaming APIs if possible.
804 /// @deprecated Use Reader(std::shared_ptr<IContextProvider>, source_path) instead.
805 /// The reference overload does not extend the lifetime of the context, which can
806 /// be problematic when progress callbacks fire after the context is destroyed.
807 [[deprecated("Use Reader(std::shared_ptr<IContextProvider>, source_path) instead. "
808 "The reference overload does not extend the lifetime of the context, which can "
809 "be problematic when progress callbacks fire after the context is destroyed.")]]
810 Reader(IContextProvider& context, const std::filesystem::path &source_path);
811
812 /// @brief Create a Reader from a shared context and stream.
813 /// @details The Reader retains a shared reference to the context, keeping it
814 /// alive for the lifetime of the Reader.
815 /// @param context Shared context provider.
816 /// @param format The mime format of the stream.
817 /// @param stream The input stream to read from.
818 /// @throws C2paException if context is null or context->is_valid() returns false.
819 Reader(std::shared_ptr<IContextProvider> context, const std::string &format, std::istream &stream);
820
821 /// @brief Create a Reader from a shared context and file path.
822 /// @details The Reader retains a shared reference to the context, keeping it
823 /// alive for the lifetime of the Reader.
824 /// @param context Shared context provider.
825 /// @param source_path The path to the file to read.
826 /// @throws C2paException if context is null or context->is_valid() returns false.
827 Reader(std::shared_ptr<IContextProvider> context, const std::filesystem::path &source_path);
828
829 /// @brief Create a Reader from a stream (will use global settings if any loaded).
830 /// @details The validation_status field in the JSON contains validation results.
831 /// @param format The mime format of the stream.
832 /// @param stream The input stream to read from.
833 /// @throws C2paException for errors encountered by the C2PA library.
834 /// @deprecated Use Reader(IContextProvider& context, format, stream) instead.
835 [[deprecated("Use Reader(IContextProvider& context, format, stream) instead")]]
836 Reader(const std::string &format, std::istream &stream);
837
838 /// @brief Create a Reader from a file path (will use global settings if any loaded).
839 /// @param source_path The path to the file to read.
840 /// @throws C2paException for errors encountered by the C2PA library.
841 /// @deprecated Use Reader(IContextProvider& context, source_path) instead.
842 /// @note Prefer using the streaming APIs if possible.
843 [[deprecated("Use Reader(IContextProvider& context, source_path) instead")]]
844 Reader(const std::filesystem::path &source_path);
845
846 /// @brief Try to open a Reader from a context and file path when the asset may lack C2PA data.
847 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
848 /// @throws C2paException for errors other than a missing manifest (e.g. invalid asset).
849 /// @throws std::system_error if the file cannot be opened.
850 /// @deprecated Use from_asset(std::shared_ptr<IContextProvider>, source_path) instead.
851 /// The reference overload does not extend the lifetime of the context, which can
852 /// cause a use-after-free crash when progress callbacks fire after the context is
853 /// destroyed.
854 [[deprecated("Use from_asset(std::shared_ptr<IContextProvider>, source_path) instead. "
855 "The reference overload does not extend the lifetime of the context, which can "
856 "be problematic when progress callbacks fire after the context is destroyed.")]]
857 static std::optional<Reader> from_asset(IContextProvider& context, const std::filesystem::path &source_path);
858
859 /// @brief Try to create a Reader from a context and stream when the asset may lack C2PA data.
860 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
861 /// @throws C2paException for errors other than a missing manifest.
862 /// @deprecated Use from_asset(std::shared_ptr<IContextProvider>, format, stream) instead.
863 /// The reference overload does not extend the lifetime of the context, which can
864 /// cause a use-after-free crash when progress callbacks fire after the context is
865 /// destroyed.
866 [[deprecated("Use from_asset(std::shared_ptr<IContextProvider>, format, stream) instead. "
867 "The reference overload does not extend the lifetime of the context, which can "
868 "be problematic when progress callbacks fire after the context is destroyed.")]]
869 static std::optional<Reader> from_asset(IContextProvider& context, const std::string &format, std::istream &stream);
870
871 /// @brief Try to open a Reader from a shared context and file path when the asset may lack C2PA data.
872 /// @details The Reader retains a shared reference to the context if C2PA data is found.
873 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
874 /// @throws C2paException for errors other than a missing manifest.
875 static std::optional<Reader> from_asset(std::shared_ptr<IContextProvider> context, const std::filesystem::path &source_path);
876
877 /// @brief Try to create a Reader from a shared context and stream when the asset may lack C2PA data.
878 /// @details The Reader retains a shared reference to the context if C2PA data is found.
879 /// @return A Reader if JUMBF (c2pa/manifest) data is present; std::nullopt if none.
880 /// @throws C2paException for errors other than a missing manifest.
881 static std::optional<Reader> from_asset(std::shared_ptr<IContextProvider> context, const std::string &format, std::istream &stream);
882
883 // Non-copyable
884 Reader(const Reader&) = delete;
885
886 Reader& operator=(const Reader&) = delete;
887
888 Reader(Reader&& other) noexcept
889 : c2pa_reader(std::exchange(other.c2pa_reader, nullptr)),
890 owned_stream(std::move(other.owned_stream)),
891 cpp_stream(std::move(other.cpp_stream)),
892 context_ref(std::move(other.context_ref)) {
893 }
894
895 Reader& operator=(Reader&& other) noexcept {
896 if (this != &other) {
897 c2pa_free(c2pa_reader);
898 c2pa_reader = std::exchange(other.c2pa_reader, nullptr);
899 owned_stream = std::move(other.owned_stream);
900 cpp_stream = std::move(other.cpp_stream);
901 context_ref = std::move(other.context_ref);
902 }
903 return *this;
904 }
905
907
908 /// @brief Check if the reader was created from an embedded manifest.
909 /// @return true if the manifest was embedded in the asset, false if external.
910 /// @throws C2paException for errors encountered by the C2PA library.
911 [[nodiscard]] inline bool is_embedded() const {
912 return c2pa_reader_is_embedded(c2pa_reader);
913 }
914
915 /// @brief Returns the remote url of the manifest if this `Reader`
916 /// obtained the manifest remotely
917 /// @return Optional string containing the remote URL, or std::nullopt if manifest was embedded.
918 /// @throws C2paException for errors encountered by the C2PA library.
919 [[nodiscard]] std::optional<std::string> remote_url() const;
920
921 /// @brief Get the manifest as a JSON string.
922 /// @return The manifest as a JSON string.
923 /// @throws C2paException for errors encountered by the C2PA library.
924 std::string json() const;
925
926 /// @brief Get a resource from the reader and write it to a file.
927 /// @param uri The URI of the resource.
928 /// @param path The file path to write the resource to.
929 /// @return The number of bytes written.
930 /// @throws C2paException for errors encountered by the C2PA library.
931 /// @note Prefer using the streaming APIs if possible.
932 int64_t get_resource(const std::string &uri, const std::filesystem::path &path);
933
934 /// @brief Get a resource from the reader and write it to an output stream.
935 /// @param uri The URI of the resource.
936 /// @param stream The output stream to write the resource to.
937 /// @return The number of bytes written.
938 /// @throws C2paException for errors encountered by the C2PA library.
939 int64_t get_resource(const std::string &uri, std::ostream &stream);
940
941 /// @brief Get the raw C2paReader pointer.
942 /// @return The raw C2paReader pointer.
943 /// @note This is intended for internal API use and compatibility with C APIs.
944 C2paReader* get_api_internal_raw_reader() const { return c2pa_reader; }
945
946 /// @brief Get a list of mime types that the SDK can read manifests from.
947 /// @return Vector of supported MIME type strings.
948 static std::vector<std::string> supported_mime_types();
949 };
950
951 /// @brief Signer callback function type.
952 /// @details This function type is used to create a callback function for signing.
953 /// The callback receives data to sign and returns the signature.
954 /// @param data The data to sign.
955 /// @return The signature as a vector of bytes.
956 using SignerFunc = std::vector<unsigned char>(const std::vector<unsigned char> &);
957
958 /// @brief Signer class for creating a Signer
959 /// @details This class is used to create a signer from a signing algorithm, certificate, and TSA URI.
960 /// Supports both callback-based and direct signing methods.
962 {
964
965 private:
966 C2paSigner *signer;
967
968 /// @brief Transfers ownership of the underlying C2paSigner pointer out
969 /// of this wrapper, without freeing it.
970 /// @details Used by ContextBuilder::with_signer() to pass the raw pointer
971 /// to c2pa_context_builder_set_signer(), which takes ownership on
972 /// the Rust side via Box::from_raw. After this call the Signer
973 /// wrapper holds nullptr and its destructor is a no-op.
974 /// This is not the same as c2pa_signer_free(), which destroys
975 /// the signer. Similar to std::unique_ptr::release().
976 /// @return Raw C2paSigner pointer, or nullptr if already released.
977 C2paSigner* release() noexcept {
978 return std::exchange(signer, nullptr);
979 }
980
981 /// @brief Validate a TSA URI string.
982 /// @param tsa_uri The TSA URI to validate.
983 /// @return Validated C-string pointer.
984 static const char *validate_tsa_uri(const std::string &tsa_uri);
985
986 /// @brief Validate an optional TSA URI.
987 /// @param tsa_uri The optional TSA URI to validate.
988 /// @return Validated C-string pointer, or nullptr if nullopt.
989 static const char *validate_tsa_uri(const std::optional<std::string> &tsa_uri);
990
991 public:
992 /// @brief Create a Signer from a callback function.
993 /// @param callback The callback function to use for signing.
994 /// @param alg The signing algorithm to use (e.g., C2paSigningAlg::PS256).
995 /// @param sign_cert The signing certificate in PEM format.
996 /// @param tsa_uri The timestamp authority URI for time-stamping.
997 /// @throws C2paException if signer creation fails.
998 Signer(SignerFunc *callback, C2paSigningAlg alg, const std::string &sign_cert, const std::string &tsa_uri);
999
1000 /// @brief Create a signer from a Signer pointer and take ownership of that pointer.
1001 /// @param c_signer The C2paSigner pointer (must be non-null).
1002 Signer(C2paSigner *c_signer) : signer(c_signer) {
1003 if (!c_signer) {
1004 throw C2paException("Signer can not be null");
1005 }
1006 }
1007
1008 /// @brief Create a Signer from signing credentials.
1009 /// @param alg Signing algorithm name (e.g., "ps256", "es256").
1010 /// @param sign_cert Signing certificate in PEM format.
1011 /// @param private_key Private key in PEM format.
1012 /// @param tsa_uri Optional timestamp authority URI.
1013 /// @throws C2paException if signer creation fails.
1014 Signer(const std::string &alg, const std::string &sign_cert, const std::string &private_key, const std::optional<std::string> &tsa_uri = std::nullopt);
1015
1016 Signer(const Signer&) = delete;
1017
1018 Signer& operator=(const Signer&) = delete;
1019
1020 /// @brief Move constructor.
1021 /// @param other Signer to move from.
1022 Signer(Signer&& other) noexcept : signer(std::exchange(other.signer, nullptr)) {
1023 }
1024
1025 Signer& operator=(Signer&& other) noexcept {
1026 if (this != &other) {
1027 c2pa_free(signer);
1028 signer = std::exchange(other.signer, nullptr);
1029 }
1030 return *this;
1031 }
1032
1034
1035 /// @brief Get the size to reserve for a signature for this Signer.
1036 /// @return Reserved size for the signature in bytes.
1037 uintptr_t reserve_size();
1038
1039 /// @brief Get the underlying C2paSigner pointer.
1040 /// @return Pointer to the C2paSigner object.
1041 C2paSigner *c2pa_signer() const noexcept;
1042 };
1043
1044 /// @brief Builder class for creating a manifest.
1045 /// @details This class is used to create a manifest from a json std::string and add resources and ingredients to the manifest.
1047 {
1048 private:
1049 C2paBuilder *builder;
1050 std::shared_ptr<IContextProvider> context_ref;
1051
1052 void init_from_context(IContextProvider& context);
1053 void init_from_context(IContextProvider& context, const std::string &manifest_json);
1054
1055 public:
1056 /// @brief Create a Builder from a context with an empty manifest.
1057 /// @param context Context provider; used at construction to configure settings.
1058 /// @throws C2paException if context.is_valid() returns false,
1059 /// or for other errors encountered by the C2PA library.
1060 /// @deprecated Use Builder(std::shared_ptr<IContextProvider>) instead.
1061 /// The reference overload does not extend the lifetime of the context, which can
1062 /// be problematic when progress callbacks fire after the context is destroyed.
1063 [[deprecated("Use Builder(std::shared_ptr<IContextProvider>) instead. "
1064 "The reference overload does not extend the lifetime of the context, which can "
1065 "be problematic when progress callbacks fire after the context is destroyed.")]]
1066 explicit Builder(IContextProvider& context);
1067
1068 /// @brief Create a Builder from a context and manifest JSON string.
1069 /// @param context Context provider; used at construction to configure settings.
1070 /// @param manifest_json The manifest JSON string.
1071 /// @throws C2paException if context.is_valid() returns false,
1072 /// or for other errors encountered by the C2PA library.
1073 /// @deprecated Use Builder(std::shared_ptr<IContextProvider>, manifest_json) instead.
1074 /// The reference overload does not extend the lifetime of the context, which can
1075 /// be problematic when progress callbacks fire after the context is destroyed.
1076 [[deprecated("Use Builder(std::shared_ptr<IContextProvider>, manifest_json) instead. "
1077 "The reference overload does not extend the lifetime of the context, which can "
1078 "be problematic when progress callbacks fire after the context is destroyed.")]]
1079 Builder(IContextProvider& context, const std::string &manifest_json);
1080
1081 /// @brief Create a Builder from a shared context with an empty manifest.
1082 /// @details The Builder retains a shared reference to the context, keeping it
1083 /// alive for the lifetime of the Builder. This is the preferred
1084 /// constructor when the Context may be destroyed before the Builder.
1085 /// @param context Shared context provider.
1086 /// @throws C2paException if context is null or context->is_valid() returns false.
1087 explicit Builder(std::shared_ptr<IContextProvider> context);
1088
1089 /// @brief Create a Builder from a shared context and manifest JSON string.
1090 /// @details The Builder retains a shared reference to the context, keeping it
1091 /// alive for the lifetime of the Builder.
1092 /// @param context Shared context provider.
1093 /// @param manifest_json The manifest JSON string.
1094 /// @throws C2paException if context is null or context->is_valid() returns false.
1095 Builder(std::shared_ptr<IContextProvider> context, const std::string &manifest_json);
1096
1097 /// @brief Create a Builder from a manifest JSON string (will use global settings if any loaded).
1098 /// @param manifest_json The manifest JSON string.
1099 /// @throws C2paException for errors encountered by the C2PA library.
1100 /// @deprecated Use Builder(IContextProvider& context, manifest_json) instead.
1101 [[deprecated("Use Builder(IContextProvider& context, manifest_json) instead")]]
1102 Builder(const std::string &manifest_json);
1103
1104 /// @brief Create a Builder from a raw C FFI builder.
1105 /// @param builder Raw C2paBuilder pointer to wrap.
1106 /// @throws C2paException if builder is nullptr.
1107 explicit Builder(C2paBuilder *builder);
1108
1109 Builder(const Builder&) = delete;
1110
1111 Builder& operator=(const Builder&) = delete;
1112
1113 Builder(Builder&& other) noexcept
1114 : builder(std::exchange(other.builder, nullptr)),
1115 context_ref(std::move(other.context_ref)) {
1116 }
1117
1118 Builder& operator=(Builder&& other) noexcept {
1119 if (this != &other) {
1120 c2pa_free(builder);
1121 builder = std::exchange(other.builder, nullptr);
1122 context_ref = std::move(other.context_ref);
1123 }
1124 return *this;
1125 }
1126
1128
1129 /// @brief Get the underlying C2paBuilder pointer.
1130 /// @return Pointer managed by this wrapper.
1131 C2paBuilder *c2pa_builder() const noexcept;
1132
1133 /// @brief Set or update the manifest definition.
1134 /// @param manifest_json The manifest JSON string.
1135 /// @return Reference to this Builder for method chaining.
1136 /// @throws C2pa::C2paException for errors encountered by the C2PA library.
1137 Builder& with_definition(const std::string &manifest_json);
1138
1139 /// @brief Set the no-embed flag to prevent embedding the manifest in the asset.
1140 /// @details When set, the manifest will be stored externally rather than embedded.
1141 void set_no_embed();
1142
1143 /// @brief Set the remote URL.
1144 /// @param remote_url The remote URL to set.
1145 /// @throws C2paException for errors encountered by the C2PA library.
1146 void set_remote_url(const std::string &remote_url);
1147
1148 /// @brief Set the base path for loading resources from files.
1149 /// @details When set, resources are loaded from files relative to this path.
1150 /// If not set, resources are loaded from memory.
1151 /// @param base_path The base directory path.
1152 /// @throws C2paException for errors encountered by the C2PA library.
1153 /// @deprecated This method is planned to be deprecated in a future release.
1154 /// Usage should be limited and temporary. Use add_resource instead.
1155 void set_base_path(const std::string &base_path);
1156
1157 /// @brief Add a resource to the builder from a stream.
1158 /// @param uri The URI identifier for the resource.
1159 /// @param source The input stream to read the resource from.
1160 /// @throws C2paException for errors encountered by the C2PA library.
1161 void add_resource(const std::string &uri, std::istream &source);
1162
1163 /// @brief Add a resource to the builder from a file.
1164 /// @param uri The URI identifier for the resource.
1165 /// @param source_path The path to the resource file.
1166 /// @throws C2paException for errors encountered by the C2PA library.
1167 /// @note Prefer using the streaming APIs if possible.
1168 void add_resource(const std::string &uri, const std::filesystem::path &source_path);
1169
1170 /// @brief Add an ingredient to the builder from a stream.
1171 /// @param ingredient_json Any fields of the ingredient you want to define.
1172 /// @param format The mime format of the ingredient.
1173 /// @param source The input stream to read the ingredient from.
1174 /// @throws C2paException for errors encountered by the C2PA library.
1175 void add_ingredient(const std::string &ingredient_json, const std::string &format, std::istream &source);
1176
1177 /// @brief Add an ingredient to the builder from a file.
1178 /// @param ingredient_json Any fields of the ingredient you want to define.
1179 /// @param source_path The path to the ingredient file.
1180 /// @throws C2paException for errors encountered by the C2PA library.
1181 /// @note Prefer using the streaming APIs if possible.
1182 void add_ingredient(const std::string &ingredient_json, const std::filesystem::path &source_path);
1183
1184 /// @brief Add an action to the manifest.
1185 /// @param action_json JSON string containing the action data.
1186 /// @throws C2paException for errors encountered by the C2PA library.
1187 void add_action(const std::string &action_json);
1188
1189 /// @brief Set the intent for this Builder, controlling what kind of manifest to create.
1190 /// @param intent The intent type: Create, Edit, or Update.
1191 /// @param digital_source_type Required for Create intent. Describes how the asset was produced.
1192 /// Defaults to Empty.
1193 /// @throws C2paException if the intent cannot be set.
1194 void set_intent(C2paBuilderIntent intent, C2paDigitalSourceType digital_source_type = Empty);
1195
1196 /// @brief Sign an input stream and write the signed data to an output stream.
1197 /// @param format The mime format of the output stream.
1198 /// @param source The input stream to sign.
1199 /// @param dest The output stream to write the signed data to.
1200 /// @param signer The Signer object to use for signing.
1201 /// @return A vector containing the signed manifest bytes.
1202 /// @throws C2paException for errors encountered by the C2PA library.
1203 /// @deprecated Use sign(const string&, std::istream&, std::iostream&, Signer&) instead.
1204 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::ostream &dest, Signer &signer);
1205
1206 /// @brief Sign an input stream and write the signed data to an I/O stream.
1207 /// @param format The mime format of the output.
1208 /// @param source The input stream to sign.
1209 /// @param dest The I/O stream to write the signed data to.
1210 /// @param signer The Signer object to use for signing.
1211 /// @return A vector containing the signed manifest bytes.
1212 /// @throws C2paException for errors encountered by the C2PA library.
1213 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::iostream &dest, Signer &signer);
1214
1215 /// @brief Sign a file and write the signed data to an output file.
1216 /// @param source_path The path to the file to sign.
1217 /// @param dest_path The path to write the signed file to.
1218 /// @param signer The signer object to use for signing.
1219 /// @return A vector containing the signed manifest bytes.
1220 /// @throws C2paException for errors encountered by the C2PA library.
1221 /// @note Prefer using the streaming APIs if possible.
1222 std::vector<unsigned char> sign(const std::filesystem::path &source_path, const std::filesystem::path &dest_path, Signer &signer);
1223
1224 /// @brief Sign using the signer from the Builder's Context.
1225 /// @details The Signer may have been set programmatically via
1226 /// ContextBuilder::with_signer(), or configured in settings JSON.
1227 /// If both programmatic and settings signers are present,
1228 /// the programmatic signer takes priority.
1229 /// @param format The mime format of the output.
1230 /// @param source The input stream to sign.
1231 /// @param dest The I/O stream to write the signed data to.
1232 /// @return A vector containing the signed manifest bytes.
1233 /// @throws C2paException if the context has no signer or on other errors.
1234 std::vector<unsigned char> sign(const std::string &format, std::istream &source, std::iostream &dest);
1235
1236 /// @brief Sign a file using the signer from the Builder's Context.
1237 /// @details The signer may have been set programmatically via
1238 /// ContextBuilder::with_signer(), or configured in settings JSON.
1239 /// If both programmatic and settings signers are present,
1240 /// the programmatic signer takes priority.
1241 /// @param source_path The path to the file to sign.
1242 /// @param dest_path The path to write the signed file to.
1243 /// @return A vector containing the signed manifest bytes.
1244 /// @throws C2paException if the context has no signer or on other errors.
1245 std::vector<unsigned char> sign(const std::filesystem::path &source_path, const std::filesystem::path &dest_path);
1246
1247 /// @brief Create a Builder from an archived Builder stream.
1248 /// @param archive The input stream to read the archive from.
1249 /// @return A new Builder instance loaded from the archive.
1250 /// @throws C2paException for errors encountered by the C2PA library.
1251 static Builder from_archive(std::istream &archive);
1252
1253 /// @brief Create a Builder from an archive.
1254 /// @param archive_path The path to the archive file.
1255 /// @return A new Builder instance loaded from the archive.
1256 /// @throws C2paException for errors encountered by the C2PA library.
1257 /// @note Prefer using the streaming APIs if possible.
1258 static Builder from_archive(const std::filesystem::path &archive_path);
1259
1260 /// @brief Load an archive into this builder.
1261 /// @details Replaces the current definition with the archived builder state.
1262 /// @param archive The input stream to read the archive from.
1263 /// @return Reference to this builder for method chaining.
1264 /// @throws C2paException for errors encountered by the C2PA library.
1265 /// @note This allows setting a context before loading the archive, preserving context settings.
1266 Builder& with_archive(std::istream &archive);
1267
1268 /// @brief Write the builder to an archive stream.
1269 /// @param dest The output stream to write the archive to.
1270 /// @throws C2paException for errors encountered by the C2PA library.
1271 void to_archive(std::ostream &dest);
1272
1273 /// @brief Write the builder to an archive file.
1274 /// @param dest_path The path to write the archive file to.
1275 /// @throws C2paException for errors encountered by the C2PA library.
1276 /// @note Prefer using the streaming APIs if possible.
1277 void to_archive(const std::filesystem::path &dest_path);
1278
1279 /// @brief Create a hashed placeholder from the builder.
1280 /// @param reserved_size The size required for a signature from the intended signer (in bytes).
1281 /// @param format The mime format or extension of the asset.
1282 /// @return A vector containing the hashed placeholder bytes.
1283 /// @throws C2paException for errors encountered by the C2PA library.
1284 std::vector<unsigned char> data_hashed_placeholder(uintptr_t reserved_size, const std::string &format);
1285
1286 /// @brief Sign a Builder using the specified signer and data hash.
1287 /// @param signer The signer to use for signing.
1288 /// @param data_hash The data hash ranges to sign (must contain hashes unless an asset is provided).
1289 /// @param format The mime format for embedding. Use "c2pa" for an unformatted result.
1290 /// @param asset Optional asset to hash according to the data_hash information.
1291 /// @return A vector containing the signed embeddable data.
1292 /// @throws C2paException for errors encountered by the C2PA library.
1293 std::vector<unsigned char> sign_data_hashed_embeddable(Signer &signer, const std::string &data_hash, const std::string &format, std::istream *asset = nullptr);
1294
1295 /// @brief Convert unformatted manifest data to an embeddable format.
1296 /// @param format The format for embedding.
1297 /// @param data Unformatted manifest data from sign_data_hashed_embeddable using "c2pa" format.
1298 /// @return A formatted copy of the data.
1299 static std::vector<unsigned char> format_embeddable(const std::string &format, std::vector<unsigned char> &data);
1300
1301 /// @brief Check if the given format requires a placeholder embedding step.
1302 /// @details Returns false for BoxHash-capable formats when prefer_box_hash is enabled in
1303 /// the context settings (no placeholder needed — hash covers the full asset).
1304 /// Always returns true for BMFF formats (MP4, etc.) regardless of settings.
1305 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1306 /// @return true if placeholder() must be called and embedded before sign_embeddable(); false otherwise.
1307 /// @throws C2paException on error.
1308 bool needs_placeholder(const std::string &format);
1309
1310 /// @brief Create a composed placeholder manifest to embed in the asset.
1311 /// @details The signer (and its reserve size) are obtained from the Builder's Context.
1312 /// For BMFF assets, the placeholder includes a BmffHash assertion with
1313 /// default exclusions for the manifest UUID box.
1314 /// Returns empty bytes for formats that do not need a placeholder (BoxHash).
1315 /// The placeholder size is stored internally so sign_embeddable() returns bytes
1316 /// of exactly the same size, enabling in-place patching.
1317 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1318 /// @return Composed placeholder bytes ready to embed into the asset.
1319 /// @throws C2paException on error.
1320 std::vector<unsigned char> placeholder(const std::string &format);
1321
1322 /// @brief Register the byte ranges where the placeholder was embedded (DataHash workflow).
1323 /// @details Call this after embedding the placeholder bytes into the asset and before
1324 /// update_hash_from_stream(). The exclusions replace the dummy ranges set by
1325 /// placeholder() so the asset hash covers all bytes except the manifest slot.
1326 /// Exclusions are (start, length) pairs in asset byte coordinates.
1327 /// @param exclusions Vector of (start, length) pairs describing the embedded placeholder region.
1328 /// @throws C2paException if no DataHash assertion exists or on other error.
1329 void set_data_hash_exclusions(const std::vector<std::pair<uint64_t, uint64_t>> &exclusions);
1330
1331 /// @brief Compute and store the asset hash by reading a stream.
1332 /// @details Automatically detects the hard binding type from the builder state:
1333 /// - DataHash: uses exclusion ranges already registered via set_data_hash_exclusions().
1334 /// - BmffHash: uses path-based exclusions from the BMFF assertion (UUID box, mdat).
1335 /// - BoxHash: hashes each format-specific box individually.
1336 /// Call set_data_hash_exclusions() before this for DataHash workflows.
1337 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1338 /// @param stream The asset stream to hash. Must include the embedded placeholder bytes.
1339 /// @throws C2paException on error.
1340 void update_hash_from_stream(const std::string &format, std::istream &stream);
1341
1342 /// @brief Sign and return the final manifest bytes, ready for embedding.
1343 /// @details Operates in two modes:
1344 /// - Placeholder mode (after placeholder()): zero-pads the signed manifest to the
1345 /// pre-committed placeholder size, enabling in-place patching of the asset.
1346 /// - Direct mode (no placeholder): returns the actual signed manifest size.
1347 /// Requires a valid hard binding assertion (set via update_hash_from_stream()).
1348 /// The signer is obtained from the Builder's Context.
1349 /// @param format The MIME type or extension of the asset (e.g. "image/jpeg", "video/mp4").
1350 /// @return Signed manifest bytes ready to embed into the asset.
1351 /// @throws C2paException on error.
1352 std::vector<unsigned char> sign_embeddable(const std::string &format);
1353
1354 /// @brief Get a list of mime types that the Builder supports.
1355 /// @return Vector of supported MIME type strings.
1356 static std::vector<std::string> supported_mime_types();
1357
1358 private:
1359 explicit Builder(std::istream &archive);
1360 };
1361}
1362
1363// Restore warnings
1364#ifdef __GNUC__
1365#pragma GCC diagnostic pop
1366#endif
1367
1368#ifdef _MSC_VER
1369#pragma warning(pop)
1370#endif
1371
1372#endif // C2PA_H
#define C2PA_CPP_API
Definition c2pa.hpp:49
Builder class for creating a manifest.
Definition c2pa.hpp:1047
Builder(const std::string &manifest_json)
Create a Builder from a manifest JSON string (will use global settings if any loaded).
Builder & operator=(const Builder &)=delete
Builder(Builder &&other) noexcept
Definition c2pa.hpp:1113
Builder(C2paBuilder *builder)
Create a Builder from a raw C FFI builder.
Builder(const Builder &)=delete
Builder(std::shared_ptr< IContextProvider > context, const std::string &manifest_json)
Create a Builder from a shared context and manifest JSON string.
C2paBuilder * c2pa_builder() const noexcept
Get the underlying C2paBuilder pointer.
Builder & operator=(Builder &&other) noexcept
Definition c2pa.hpp:1118
Builder(std::shared_ptr< IContextProvider > context)
Create a Builder from a shared context with an empty manifest.
Builder(IContextProvider &context)
Create a Builder from a context with an empty manifest.
Builder(IContextProvider &context, const std::string &manifest_json)
Create a Builder from a context and manifest JSON string.
Exception class for C2pa errors. This class is used to throw exceptions for errors encountered by the...
Definition c2pa.hpp:87
C2paException & operator=(C2paException &&)=default
~C2paException() override=default
C2paException(const C2paException &)=default
const char * what() const noexcept override
Get the exception message.
C2paException(std::string message)
Construct an exception with a custom error message.
C2paException & operator=(const C2paException &)=default
C2paException(C2paException &&)=default
C2paException()
Default constructor.
ContextBuilder for creating customized Context instances.
Definition c2pa.hpp:330
ReleasedBuilder release() noexcept
Release ownership of the underlying C2paContextBuilder handle and its progress-callback heap block (i...
C2PA context implementing IContextProvider.
Definition c2pa.hpp:322
IOStream Class wrapper for C2paStream.
Definition c2pa.hpp:711
CppIOStream & operator=(const CppIOStream &)=delete
CppIOStream(const CppIOStream &)=delete
CppIOStream & operator=(CppIOStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:714
CppIOStream(IOStream &iostream)
Construct an I/O stream wrapper from a std::iostream-derived object.
Definition c2pa.hpp:721
CppIOStream(CppIOStream &&)=delete
Input stream IStream wrapper for C2paStream.
Definition c2pa.hpp:590
CppIStream & operator=(const CppIStream &)=delete
CppIStream(CppIStream &&)=delete
CppIStream & operator=(CppIStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:593
CppIStream(IStream &istream)
Construct an input stream wrapper from a std::istream-derived object.
Definition c2pa.hpp:600
CppIStream(const CppIStream &)=delete
Output stream OStream wrapper for C2paStream.
Definition c2pa.hpp:652
CppOStream(OStream &ostream)
Construct an output stream wrapper from a std::ostream-derived object.
Definition c2pa.hpp:662
CppOStream(CppOStream &&)=delete
CppOStream & operator=(CppOStream &&)=delete
C2paStream * c_stream
Pointer to the underlying C2paStream.
Definition c2pa.hpp:655
CppOStream & operator=(const CppOStream &)=delete
CppOStream(const CppOStream &)=delete
Interface for types that can provide C2PA context functionality.
Definition c2pa.hpp:161
virtual ~IContextProvider() noexcept=default
Reader class for reading a manifest.
Definition c2pa.hpp:772
Reader(std::shared_ptr< IContextProvider > context, const std::filesystem::path &source_path)
Create a Reader from a shared context and file path.
static std::optional< Reader > from_asset(std::shared_ptr< IContextProvider > context, const std::string &format, std::istream &stream)
Try to create a Reader from a shared context and stream when the asset may lack C2PA data.
bool is_embedded() const
Check if the reader was created from an embedded manifest.
Definition c2pa.hpp:911
std::string json() const
Get the manifest as a JSON string.
static std::optional< Reader > from_asset(IContextProvider &context, const std::string &format, std::istream &stream)
Try to create a Reader from a context and stream when the asset may lack C2PA data.
int64_t get_resource(const std::string &uri, const std::filesystem::path &path)
Get a resource from the reader and write it to a file.
Reader(const std::string &format, std::istream &stream)
Create a Reader from a stream (will use global settings if any loaded).
int64_t get_resource(const std::string &uri, std::ostream &stream)
Get a resource from the reader and write it to an output stream.
Reader(std::shared_ptr< IContextProvider > context, const std::string &format, std::istream &stream)
Create a Reader from a shared context and stream.
static std::optional< Reader > from_asset(IContextProvider &context, const std::filesystem::path &source_path)
Try to open a Reader from a context and file path when the asset may lack C2PA data.
Reader(Reader &&other) noexcept
Definition c2pa.hpp:888
Reader(const Reader &)=delete
Reader & operator=(const Reader &)=delete
std::optional< std::string > remote_url() const
Returns the remote url of the manifest if this Reader obtained the manifest remotely.
static std::optional< Reader > from_asset(std::shared_ptr< IContextProvider > context, const std::filesystem::path &source_path)
Try to open a Reader from a shared context and file path when the asset may lack C2PA data.
Reader(IContextProvider &context, const std::string &format, std::istream &stream)
Create a Reader from a context and stream.
Reader & operator=(Reader &&other) noexcept
Definition c2pa.hpp:895
Reader(const std::filesystem::path &source_path)
Create a Reader from a file path (will use global settings if any loaded).
C2paReader * get_api_internal_raw_reader() const
Get the raw C2paReader pointer.
Definition c2pa.hpp:944
Reader(IContextProvider &context, const std::filesystem::path &source_path)
Create a Reader from a context and file path.
static std::vector< std::string > supported_mime_types()
Get a list of mime types that the SDK can read manifests from.
(C2PA SDK) Settings configuration object for creating contexts.
Definition c2pa.hpp:195
Settings & update(const std::string &data)
Merge configuration from a JSON string (latest configuration wins).
Definition c2pa.hpp:233
C2paSettings * c_settings() const noexcept
Get the raw C FFI settings pointer.
Settings(const std::string &data, const std::string &format)
Create settings from a configuration string.
Settings()
Create default settings.
Settings(Settings &&) noexcept
Settings & update(const std::string &data, const std::string &format)
Merge configuration from a std::string (latest configuration wins).
Signer class for creating a Signer.
Definition c2pa.hpp:962
Signer & operator=(Signer &&other) noexcept
Definition c2pa.hpp:1025
uintptr_t reserve_size()
Get the size to reserve for a signature for this Signer.
C2paSigner * c2pa_signer() const noexcept
Get the underlying C2paSigner pointer.
Signer(SignerFunc *callback, C2paSigningAlg alg, const std::string &sign_cert, const std::string &tsa_uri)
Create a Signer from a callback function.
Signer(C2paSigner *c_signer)
Create a signer from a Signer pointer and take ownership of that pointer.
Definition c2pa.hpp:1002
Signer & operator=(const Signer &)=delete
Signer(Signer &&other) noexcept
Move constructor.
Definition c2pa.hpp:1022
Signer(const Signer &)=delete
Signer(const std::string &alg, const std::string &sign_cert, const std::string &private_key, const std::optional< std::string > &tsa_uri=std::nullopt)
Create a Signer from signing credentials.
Definition c2pa.hpp:52
OperationResult
Result codes for C API operations (matches C API return convention).
Definition c2pa.hpp:64
@ Success
Operation succeeded.
@ Error
Operation failed (check C2paException for details)
ProgressPhase
Phase values reported to the ProgressCallbackFunc.
Definition c2pa.hpp:265
std::function< bool(ProgressPhase phase, uint32_t step, uint32_t total)> ProgressCallbackFunc
Type alias for the progress callback passed to ContextBuilder::with_progress_callback().
Definition c2pa.hpp:299
int stream_error_return(StreamError e) noexcept
Set errno from StreamError and return error sentinel.
Definition c2pa.hpp:79
C2paSignerInfo SignerInfo
Type alias for C2paSignerInfo from the C API.
Definition c2pa.hpp:55
StreamError
Stream/FFI error codes (maps to errno values used by the C layer).
Definition c2pa.hpp:70
std::vector< unsigned char >(const std::vector< unsigned char > &) SignerFunc
Signer callback function type.
Definition c2pa.hpp:956
Result of ContextBuilder::release(): the raw native builder handle paired with the heap-owned progres...
Definition c2pa.hpp:426
C2paContextBuilder * builder
Definition c2pa.hpp:427
std::unique_ptr< ProgressCallbackFunc > callback_owner
Definition c2pa.hpp:428