Home | History | Annotate | Download | only in jpeg
      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