CaptureMoment 0.1.0
Free and open-source non-destructive photo editor
Loading...
Searching...
No Matches
file_serializer_writer.cpp
Go to the documentation of this file.
1
11
12#include <spdlog/spdlog.h>
13#include <exiv2/exiv2.hpp>
14#include <stdexcept>
15#include <magic_enum/magic_enum.hpp>
16
18
20 std::unique_ptr<IXmpProvider> xmp_provider,
21 std::unique_ptr<IXmpPathStrategy> xmp_path_strategy)
22 : m_xmp_provider(std::move(xmp_provider))
23 , m_xmp_path_strategy(std::move(xmp_path_strategy))
24{
25 if (!m_xmp_provider || !m_xmp_path_strategy) {
26 spdlog::error("FileSerializerWriter: Constructor received a null IXmpProvider or IXmpPathStrategy.");
27 throw std::invalid_argument("FileSerializerWriter: IXmpProvider and IXmpPathStrategy cannot be null.");
28 }
29 spdlog::debug("FileSerializerWriter: Constructed with IXmpProvider and IXmpPathStrategy.");
30}
31
32bool FileSerializerWriter::saveToFile(std::string_view source_image_path, std::span<const Operations::OperationDescriptor> operations) const
33{
34 if (source_image_path.empty()) {
35 spdlog::error("FileSerializerWriter::saveToFile: Source image path is empty.");
36 return false;
37 }
38 spdlog::debug("FileSerializerWriter::saveToFile: Attempting to save {} operations for image: {}", operations.size(), source_image_path);
39
40 // Ensure Exiv2 is initialized before any operations
42
43 // Step 0: Determine the XMP file path using the injected strategy
44 std::string xmp_file_path { m_xmp_path_strategy->getXmpPathForImage(source_image_path) };
45 spdlog::debug("FileSerializerWriter::saveToFile: Determined XMP file path: {}", xmp_file_path);
46
47 // Step 1: Convert operations to XMP string, including the source image path metadata
48 std::string xmp_packet { serializeOperationsToXmp(operations, source_image_path) };
49 if (xmp_packet.empty()) {
50 spdlog::error("FileSerializerWriter::saveToFile: Failed to serialize operations to XMP for image: {}", source_image_path);
51 return false;
52 }
53
54 // Step 2: Use the XMP provider to write the packet to the determined XMP file
55 bool write_success { m_xmp_provider->writeXmp(xmp_file_path, xmp_packet) };
56
57 if (write_success) {
58 spdlog::info("FileSerializerWriter::saveToFile: Successfully saved operations to XMP file: {} for image: {}", xmp_file_path, source_image_path);
59 } else {
60 spdlog::error("FileSerializerWriter::saveToFile: IXmpProvider failed to write XMP data to file: {} for image: {}", xmp_file_path, source_image_path);
61 }
62
63 return write_success;
64}
65
66std::string FileSerializerWriter::serializeOperationsToXmp(
67 std::span<const Operations::OperationDescriptor> operations,
68 std::string_view source_image_path) const
69{
70 spdlog::debug("FileSerializerWriter::serializeOperationsToXmp: Serializing {} operations for image: {}", operations.size(), source_image_path);
71
72 // XMP Namespace configuration for "CaptureMoment" (cm)
73 const std::string ns_uri { "https://github.com/YacinoBen/CaptureMoment/" };
74 const std::string ns_prefix { "cm" };
75
76 // Register the namespace for Exiv2 to recognize "Xmp.cm.*" keys correctly
77 // Note: This is generally safe to call multiple times or ensure it's called once globally.
78 Exiv2::XmpProperties::registerNs(ns_uri, ns_prefix);
79
80 Exiv2::XmpData xmp_data;
81
82 try {
83 // Add metadata about the serialization itself
84 xmp_data["Xmp.cm.serializedBy"] = "CaptureMoment";
85 xmp_data["Xmp.cm.version"] = "1.0";
86 // Include the source image path as a metadata field within the XMP
87 xmp_data["Xmp.cm.sourceImagePath"] = std::string(source_image_path);
88
89 // Iterate through operations with index (1-based for readability in XMP)
90 for (size_t i = 0; i < operations.size(); ++i) {
91 const auto& op = operations[i];
92 std::string index_str { std::to_string(i + 1) };
93
94 // Use magic_enum to get the string name of the enum (must match Reader's enum_cast)
95 std::string_view type_name_view { magic_enum::enum_name(op.type) };
96 xmp_data["Xmp.cm.operation[" + index_str + "].type"] = std::string(type_name_view);
97 xmp_data["Xmp.cm.operation[" + index_str + "].name"] = op.name;
98 xmp_data["Xmp.cm.operation[" + index_str + "].enabled"] = op.enabled;
99
100 // Iterate through the parameters map
101 for (const auto& [param_name, param_value] : op.params)
102 {
103 // Serialize the value (variant) to string using the service
104 // No need to check .has_value() as variant always holds a value
105 std::string serialized_param_value { Serializer::serializeParameter(param_value) };
106
107 std::string xmp_param_key { "Xmp.cm.operation[" + index_str + "].param." + param_name };
108 xmp_data[xmp_param_key] = serialized_param_value;
109 }
110 }
111
112 // Serialize the XMP data container to a packet string
113 std::string xmp_packet;
114 if (Exiv2::XmpParser::encode(xmp_packet, xmp_data) != 0) {
115 spdlog::error("FileSerializerWriter::serializeOperationsToXmp: Failed to encode XMP packet.");
116 return {};
117 }
118
119 spdlog::debug("FileSerializerWriter::serializeOperationsToXmp: Successfully serialized to XMP packet (size {}).", xmp_packet.size());
120 return xmp_packet;
121
122 } catch (const Exiv2::Error& e) {
123 spdlog::error("FileSerializerWriter::serializeOperationsToXmp: Exiv2 error during serialization: {}", e.what());
124 return {};
125 } catch (const std::exception& e) {
126 spdlog::error("FileSerializerWriter::serializeOperationsToXmp: General error during serialization: {}", e.what());
127 return {};
128 }
129}
130
131} // namespace CaptureMoment::Core::Serializer
static void initialize()
Initializes the Exiv2 XMP parser if not already done. This function is thread-safe.
FileSerializerWriter(std::unique_ptr< IXmpProvider > xmp_provider, std::unique_ptr< IXmpPathStrategy > xmp_path_strategy)
Constructs a FileSerializerWriter.
bool saveToFile(std::string_view source_image_path, std::span< const Operations::OperationDescriptor > operations) const override
Saves the provided list of operations to an XMP file determined by the path strategy.
Declaration of Exiv2Initializer class.
Declaration of FileSerializerWriter class.
std::string serializeParameter(const Operations::OperationValue &value)
Namespace containing utilities for serializing and deserializing OperationDescriptor parameters....
Declaration of OperationSerialization interface.
CaptureMoment — footer