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. 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 38 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 51 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 62 bool JpegSegment::BytesAtLocationContain(size_t location, 63 const char* str) const { 64 return Find(location, str) != GetEnd(); 65 } 66 67 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 78 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 92 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 106 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 123 size_t JpegSegment::FindXmpPropertyValueEnd(size_t start_location) const { 124 return Find(start_location, Byte('"')); 125 } 126 127 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 145 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