1 #include "image_io/jpeg/jpeg_segment_builder.h"
2
3 #include "image_io/jpeg/jpeg_marker.h"
4
5 namespace photos_editing_formats {
6 namespace image_io {
7
8 using std::string;
9
10 // The strings needed to build the xml data associated with XMP data. See
11 // https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/
12 // XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart1.pdf
13 const char kXmpMetaPrefix[] = "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">";
14 const char kXmpMetaSuffix[] = "</x:xmpmeta>";
15 const char kRdfPrefix[] =
16 "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\""
17 "xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">";
18 const char kRdfSuffix[] = "</rdf:RDF>";
19 const char kRdfDescriptionPrefix[] = "<rdf:Description rdf:about=\"\"";
20 const char kRdfDescriptionSuffix[] = "/>";
21
SetPayloadSize(ByteBuffer * byte_buffer)22 bool JpegSegmentBuilder::SetPayloadSize(ByteBuffer* byte_buffer) {
23 std::uint16_t size = byte_buffer->GetSize();
24 if (size == byte_buffer->GetSize() && size >= 4) {
25 return byte_buffer->SetBigEndianValue(2, size - 2);
26 }
27 return false;
28 }
29
GetByteDataValues() const30 string JpegSegmentBuilder::GetByteDataValues() const {
31 string values;
32 for (const auto& byte_datum : byte_data_) {
33 if (!byte_datum.IsValid()) {
34 return "";
35 }
36 values += byte_datum.GetValue();
37 if (byte_datum.GetType() == ByteData::kAscii0) {
38 values.append(1, 0);
39 }
40 }
41 return values;
42 }
43
AddMarkerAndSize(Byte marker_type,size_t size)44 void JpegSegmentBuilder::AddMarkerAndSize(Byte marker_type, size_t size) {
45 JpegMarker marker(marker_type);
46 string hex_string = marker.GetHexString("FF");
47 if (marker.HasVariablePayloadSize()) {
48 hex_string += ByteData::Byte2Hex((size >> 8) & 0xFF);
49 hex_string += ByteData::Byte2Hex(size & 0xFF);
50 }
51 byte_data_.emplace_back(ByteData::kHex, hex_string);
52 }
53
AddMarkerAndSizePlaceholder(Byte marker_type)54 size_t JpegSegmentBuilder::AddMarkerAndSizePlaceholder(Byte marker_type) {
55 JpegMarker marker(marker_type);
56 string hex_string = marker.GetHexString("FF");
57 if (marker.HasVariablePayloadSize()) {
58 hex_string += "0000";
59 }
60 byte_data_.emplace_back(ByteData::kHex, hex_string);
61 return byte_data_.size() - 1;
62 }
63
ReplaceSizePlaceholder(size_t index,size_t size)64 bool JpegSegmentBuilder::ReplaceSizePlaceholder(size_t index, size_t size) {
65 if (index >= byte_data_.size() || size < 2 || size > 0xFFFF) {
66 return false;
67 }
68 const ByteData& byte_datum = byte_data_[index];
69 if (byte_datum.GetType() != ByteData::kHex) {
70 return false;
71 }
72 string value = byte_datum.GetValue();
73 if (value.length() < 4) {
74 return false;
75 }
76 Byte flag, type;
77 if (!ByteData::Hex2Byte(value[0], value[1], &flag) ||
78 !ByteData::Hex2Byte(value[2], value[3], &type)) {
79 return false;
80 }
81 JpegMarker marker(type);
82 if (flag != JpegMarker::kStart || !marker.IsValid() ||
83 !marker.HasVariablePayloadSize()) {
84 return false;
85 }
86 value.replace(2, 2, ByteData::Byte2Hex((size >> 8) & 0xFF));
87 value.replace(4, 2, ByteData::Byte2Hex(size & 0xFF));
88 byte_data_[index] = ByteData(ByteData::kHex, value);
89 return true;
90 }
91
AddExtendedXmpHeader(const std::string & xmp_guid)92 void JpegSegmentBuilder::AddExtendedXmpHeader(const std::string& xmp_guid) {
93 string guid_value(xmp_guid);
94 guid_value.resize(kXmpGuidSize, '0');
95 byte_data_.emplace_back(ByteData::kAscii0, kXmpExtendedId);
96 byte_data_.emplace_back(ByteData::kAscii, guid_value);
97 byte_data_.emplace_back(ByteData::kAscii, string(8, '0'));
98 }
99
AddXmpMetaPrefix()100 void JpegSegmentBuilder::AddXmpMetaPrefix() {
101 byte_data_.emplace_back(ByteData::kAscii, kXmpMetaPrefix);
102 }
103
AddXmpMetaSuffix()104 void JpegSegmentBuilder::AddXmpMetaSuffix() {
105 byte_data_.emplace_back(ByteData::kAscii, kXmpMetaSuffix);
106 }
107
AddRdfPrefix()108 void JpegSegmentBuilder::AddRdfPrefix() {
109 byte_data_.emplace_back(ByteData::kAscii, kRdfPrefix);
110 }
111
AddRdfSuffix()112 void JpegSegmentBuilder::AddRdfSuffix() {
113 byte_data_.emplace_back(ByteData::kAscii, kRdfSuffix);
114 }
115
AddRdfDescriptionPrefix()116 void JpegSegmentBuilder::AddRdfDescriptionPrefix() {
117 byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionPrefix);
118 }
119
AddRdfDescriptionSuffix()120 void JpegSegmentBuilder::AddRdfDescriptionSuffix() {
121 byte_data_.emplace_back(ByteData::kAscii, kRdfDescriptionSuffix);
122 }
123
AddXmpPropertyPrefix(const std::string & property_name)124 void JpegSegmentBuilder::AddXmpPropertyPrefix(
125 const std::string& property_name) {
126 string property_name_equals_quote = property_name + "=\"";
127 byte_data_.emplace_back(ByteData::kAscii, property_name_equals_quote);
128 }
129
AddXmpPropertySuffix()130 void JpegSegmentBuilder::AddXmpPropertySuffix() {
131 byte_data_.emplace_back(ByteData::kAscii, "\"");
132 }
133
AddXmpPropertyNameAndValue(const std::string & property_name,const std::string & property_value)134 void JpegSegmentBuilder::AddXmpPropertyNameAndValue(
135 const std::string& property_name, const std::string& property_value) {
136 AddXmpPropertyPrefix(property_name);
137 byte_data_.emplace_back(ByteData::kAscii, property_value);
138 AddXmpPropertySuffix();
139 }
140
AddApp1XmpMarkerAndXmpExtendedHeader(const std::string & xmp_guid)141 void JpegSegmentBuilder::AddApp1XmpMarkerAndXmpExtendedHeader(
142 const std::string& xmp_guid) {
143 AddMarkerAndSizePlaceholder(JpegMarker::kAPP1);
144 AddExtendedXmpHeader(xmp_guid);
145 }
146
AddXmpAndRdfPrefixes()147 void JpegSegmentBuilder::AddXmpAndRdfPrefixes() {
148 AddXmpMetaPrefix();
149 AddRdfPrefix();
150 AddRdfDescriptionPrefix();
151 }
152
AddXmpAndRdfSuffixes()153 void JpegSegmentBuilder::AddXmpAndRdfSuffixes() {
154 AddRdfDescriptionSuffix();
155 AddRdfSuffix();
156 AddXmpMetaSuffix();
157 }
158
159 } // namespace image_io
160 } // namespace photos_editing_formats
161