1 #include "image_io/jpeg/jpeg_segment.h"
2
3 #include <cctype>
4 #include <iomanip>
5 #include <sstream>
6 #include <string>
7
8 namespace photos_editing_formats {
9 namespace image_io {
10
11 using std::string;
12 using std::stringstream;
13
14 /// Finds the character allowing it to be preceded by whitespace characters.
15 /// @param segment The segment in which to look for the character.
16 /// @param start_location The location at which to start looking.
17 /// @param value The character value to look for.
18 /// @return The location of the character or segment.GetEnd() if not found,
19 /// of non whitespace characters are found first.
SkipWhiteSpaceFindChar(const JpegSegment & segment,size_t start_location,char value)20 static size_t SkipWhiteSpaceFindChar(const JpegSegment& segment,
21 size_t start_location, char value) {
22 for (size_t location = start_location; location < segment.GetEnd();
23 ++location) {
24 ValidatedByte validated_byte = segment.GetValidatedByte(location);
25 if (!validated_byte.is_valid) {
26 return segment.GetEnd();
27 }
28 if (validated_byte.value == Byte(value)) {
29 return location;
30 }
31 if (!std::isspace(validated_byte.value)) {
32 return segment.GetEnd();
33 }
34 }
35 return segment.GetEnd();
36 }
37
GetVariablePayloadSize() const38 size_t JpegSegment::GetVariablePayloadSize() const {
39 if (!GetMarker().HasVariablePayloadSize()) {
40 return 0;
41 }
42 size_t payload_location = GetPayloadLocation();
43 ValidatedByte hi = GetValidatedByte(payload_location);
44 ValidatedByte lo = GetValidatedByte(payload_location + 1);
45 if (!hi.is_valid || !lo.is_valid) {
46 return 0;
47 }
48 return static_cast<size_t>(hi.value) << 8 | static_cast<size_t>(lo.value);
49 }
50
BytesAtLocationStartWith(size_t location,const char * str) const51 bool JpegSegment::BytesAtLocationStartWith(size_t location,
52 const char* str) const {
53 while (*str && Contains(location)) {
54 ValidatedByte validated_byte = GetValidatedByte(location++);
55 if (!validated_byte.is_valid || Byte(*str++) != validated_byte.value) {
56 return false;
57 }
58 }
59 return *str == 0;
60 }
61
BytesAtLocationContain(size_t location,const char * str) const62 bool JpegSegment::BytesAtLocationContain(size_t location,
63 const char* str) const {
64 return Find(location, str) != GetEnd();
65 }
66
Find(size_t location,const char * str) const67 size_t JpegSegment::Find(size_t location, const char* str) const {
68 Byte byte0 = static_cast<Byte>(*str);
69 while ((location = Find(location, byte0)) < GetEnd()) {
70 if (BytesAtLocationStartWith(location, str)) {
71 return location;
72 }
73 ++location;
74 }
75 return GetEnd();
76 }
77
Find(size_t start_location,Byte value) const78 size_t JpegSegment::Find(size_t start_location, Byte value) const {
79 if (!begin_segment_ && !end_segment_) {
80 return GetEnd();
81 }
82 size_t value_location = GetEnd();
83 if (begin_segment_ && !end_segment_) {
84 value_location = begin_segment_->Find(start_location, value);
85 } else {
86 value_location =
87 DataSegment::Find(start_location, value, begin_segment_, end_segment_);
88 }
89 return Contains(value_location) ? value_location : GetEnd();
90 }
91
ExtractXmpPropertyValue(size_t start_location,const char * property_name) const92 std::string JpegSegment::ExtractXmpPropertyValue(
93 size_t start_location, const char* property_name) const {
94 size_t begin_value_location =
95 FindXmpPropertyValueBegin(start_location, property_name);
96 if (begin_value_location != GetEnd()) {
97 size_t end_value_location = FindXmpPropertyValueEnd(begin_value_location);
98 if (end_value_location != GetEnd()) {
99 DataRange data_range(begin_value_location, end_value_location);
100 return ExtractString(data_range);
101 }
102 }
103 return "";
104 }
105
FindXmpPropertyValueBegin(size_t start_location,const char * property_name) const106 size_t JpegSegment::FindXmpPropertyValueBegin(size_t start_location,
107 const char* property_name) const {
108 size_t property_location = Find(start_location, property_name);
109 if (property_location != GetEnd()) {
110 size_t equal_location = SkipWhiteSpaceFindChar(
111 *this, property_location + strlen(property_name), '=');
112 if (equal_location != GetEnd()) {
113 size_t quote_location =
114 SkipWhiteSpaceFindChar(*this, equal_location + 1, '"');
115 if (quote_location != GetEnd()) {
116 return quote_location + 1;
117 }
118 }
119 }
120 return GetEnd();
121 }
122
FindXmpPropertyValueEnd(size_t start_location) const123 size_t JpegSegment::FindXmpPropertyValueEnd(size_t start_location) const {
124 return Find(start_location, Byte('"'));
125 }
126
ExtractString(const DataRange & data_range) const127 std::string JpegSegment::ExtractString(const DataRange& data_range) const {
128 std::string value;
129 if (Contains(data_range.GetBegin()) && data_range.GetEnd() <= GetEnd()) {
130 size_t start_location = data_range.GetBegin();
131 size_t length = data_range.GetLength();
132 value.resize(length, ' ');
133 for (size_t index = 0; index < length; ++index) {
134 ValidatedByte validated_byte = GetValidatedByte(start_location + index);
135 if (!validated_byte.value) { // Invalid bytes have a zero value.
136 value.resize(0);
137 break;
138 }
139 value[index] = static_cast<char>(validated_byte.value);
140 }
141 }
142 return value;
143 }
144
GetPayloadHexDumpStrings(size_t byte_count,std::string * hex_string,std::string * ascii_string) const145 void JpegSegment::GetPayloadHexDumpStrings(size_t byte_count,
146 std::string* hex_string,
147 std::string* ascii_string) const {
148 stringstream ascii_stream;
149 stringstream hex_stream;
150 hex_stream << std::hex << std::uppercase;
151
152 size_t dump_count = GetMarker().IsEntropySegmentDelimiter()
153 ? byte_count
154 : std::min(byte_count, GetLength() - 2);
155 for (size_t index = 0; index < dump_count; ++index) {
156 ValidatedByte payload_byte = GetValidatedByte(GetPayloadLocation() + index);
157 if (!payload_byte.is_valid) {
158 break;
159 }
160 Byte value = payload_byte.value;
161 hex_stream << std::setfill('0') << std::setw(2) << static_cast<int>(value);
162 ascii_stream << (isprint(value) ? static_cast<char>(value) : '.');
163 }
164 size_t current_count = ascii_stream.str().length();
165 for (size_t index = current_count; index < byte_count; ++index) {
166 hex_stream << " ";
167 ascii_stream << ".";
168 }
169 *hex_string = hex_stream.str();
170 *ascii_string = ascii_stream.str();
171 }
172
173 } // namespace image_io
174 } // namespace photos_editing_formats
175