summaryrefslogtreecommitdiff
path: root/src/jpeg/jpeg_segment_builder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/jpeg/jpeg_segment_builder.cc')
-rw-r--r--src/jpeg/jpeg_segment_builder.cc160
1 files changed, 160 insertions, 0 deletions
diff --git a/src/jpeg/jpeg_segment_builder.cc b/src/jpeg/jpeg_segment_builder.cc
new file mode 100644
index 0000000..aafb525
--- /dev/null
+++ b/src/jpeg/jpeg_segment_builder.cc
@@ -0,0 +1,160 @@
+#include "image_io/jpeg/jpeg_segment_builder.h"
+
+#include "image_io/jpeg/jpeg_marker.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+using std::string;
+
+// The strings needed to build the xml data associated with XMP data. See
+// https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/
+// XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart1.pdf
+const char kXmpMetaPrefix[] = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">";
+const char kXmpMetaSuffix[] = "</x:xmpmeta>";
+const char kRdfPrefix[] =
+ "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
+ "xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">";
+const char kRdfSuffix[] = "</rdf:RDF>";
+const char kRdfDescriptionPrefix[] = "<rdf:Description rdf:about=\"\"";
+const char kRdfDescriptionSuffix[] = "/>";
+
+bool JpegSegmentBuilder::SetPayloadSize(ByteBuffer* byte_buffer) {
+ std::uint16_t size = byte_buffer->GetSize();
+ if (size == byte_buffer->GetSize() && size >= 4) {
+ return byte_buffer->SetBigEndianValue(2, size - 2);
+ }
+ return false;
+}
+
+string JpegSegmentBuilder::GetByteDataValues() const {
+ string values;
+ for (const auto& byte_datum : byte_data_) {
+ if (!byte_datum.IsValid()) {
+ return "";
+ }
+ values += byte_datum.GetValue();
+ if (byte_datum.GetType() == ByteData::kAscii0) {
+ values.append(1, 0);
+ }
+ }
+ return values;
+}
+
+void JpegSegmentBuilder::AddMarkerAndSize(Byte marker_type, size_t size) {
+ JpegMarker marker(marker_type);
+ string hex_string = marker.GetHexString("FF");
+ if (marker.HasVariablePayloadSize()) {
+ hex_string += ByteData::Byte2Hex((size >> 8) & 0xFF);
+ hex_string += ByteData::Byte2Hex(size & 0xFF);
+ }
+ byte_data_.emplace_back(ByteData::kHex, hex_string);
+}
+
+size_t JpegSegmentBuilder::AddMarkerAndSizePlaceholder(Byte marker_type) {
+ JpegMarker marker(marker_type);
+ string hex_string = marker.GetHexString("FF");
+ if (marker.HasVariablePayloadSize()) {
+ hex_string += "0000";
+ }
+ byte_data_.emplace_back(ByteData::kHex, hex_string);
+ return byte_data_.size() - 1;
+}
+
+bool JpegSegmentBuilder::ReplaceSizePlaceholder(size_t index, size_t size) {
+ if (index >= byte_data_.size() || size < 2 || size > 0xFFFF) {
+ return false;
+ }
+ const ByteData& byte_datum = byte_data_[index];
+ if (byte_datum.GetType() != ByteData::kHex) {
+ return false;
+ }
+ string value = byte_datum.GetValue();
+ if (value.length() < 4) {
+ return false;
+ }
+ Byte flag, type;
+ if (!ByteData::Hex2Byte(value[0], value[1], &flag) ||
+ !ByteData::Hex2Byte(value[2], value[3], &type)) {
+ return false;
+ }
+ JpegMarker marker(type);
+ if (flag != JpegMarker::kStart || !marker.IsValid() ||
+ !marker.HasVariablePayloadSize()) {
+ return false;
+ }
+ value.replace(2, 2, ByteData::Byte2Hex((size >> 8) & 0xFF));
+ value.replace(4, 2, ByteData::Byte2Hex(size & 0xFF));
+ byte_data_[index] = ByteData(ByteData::kHex, value);
+ return true;
+}
+
+void JpegSegmentBuilder::AddExtendedXmpHeader(const std::string& xmp_guid) {
+ string guid_value(xmp_guid);
+ guid_value.resize(kXmpGuidSize, '0');
+ byte_data_.emplace_back(ByteData::kAscii0, kXmpExtendedId);
+ byte_data_.emplace_back(ByteData::kAscii, guid_value);
+ byte_data_.emplace_back(ByteData::kAscii, string(8, '0'));
+}
+
+void JpegSegmentBuilder::AddXmpMetaPrefix() {
+ byte_data_.emplace_back(ByteData::kAscii, kXmpMetaPrefix);
+}
+
+void JpegSegmentBuilder::AddXmpMetaSuffix() {
+ byte_data_.emplace_back(ByteData::kAscii, kXmpMetaSuffix);
+}
+
+void JpegSegmentBuilder::AddRdfPrefix() {
+ byte_data_.emplace_back(ByteData::kAscii, kRdfPrefix);
+}
+
+void JpegSegmentBuilder::AddRdfSuffix() {
+ byte_data_.emplace_back(ByteData::kAscii, kRdfSuffix);
+}
+
+void JpegSegmentBuilder::AddRdfDescriptionPrefix() {
+ byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionPrefix);
+}
+
+void JpegSegmentBuilder::AddRdfDescriptionSuffix() {
+ byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionSuffix);
+}
+
+void JpegSegmentBuilder::AddXmpPropertyPrefix(
+ const std::string& property_name) {
+ string property_name_equals_quote = property_name + "=\"";
+ byte_data_.emplace_back(ByteData::kAscii, property_name_equals_quote);
+}
+
+void JpegSegmentBuilder::AddXmpPropertySuffix() {
+ byte_data_.emplace_back(ByteData::kAscii, "\"");
+}
+
+void JpegSegmentBuilder::AddXmpPropertyNameAndValue(
+ const std::string& property_name, const std::string& property_value) {
+ AddXmpPropertyPrefix(property_name);
+ byte_data_.emplace_back(ByteData::kAscii, property_value);
+ AddXmpPropertySuffix();
+}
+
+void JpegSegmentBuilder::AddApp1XmpMarkerAndXmpExtendedHeader(
+ const std::string& xmp_guid) {
+ AddMarkerAndSizePlaceholder(JpegMarker::kAPP1);
+ AddExtendedXmpHeader(xmp_guid);
+}
+
+void JpegSegmentBuilder::AddXmpAndRdfPrefixes() {
+ AddXmpMetaPrefix();
+ AddRdfPrefix();
+ AddRdfDescriptionPrefix();
+}
+
+void JpegSegmentBuilder::AddXmpAndRdfSuffixes() {
+ AddRdfDescriptionSuffix();
+ AddRdfSuffix();
+ AddXmpMetaSuffix();
+}
+
+} // namespace image_io
+} // namespace photos_editing_formats