#include "image_io/jpeg/jpeg_segment.h" #include #include #include #include namespace photos_editing_formats { namespace image_io { using std::string; using std::stringstream; /// Finds the character allowing it to be preceded by whitespace characters. /// @param segment The segment in which to look for the character. /// @param start_location The location at which to start looking. /// @param value The character value to look for. /// @return The location of the character or segment.GetEnd() if not found, /// of non whitespace characters are found first. static size_t SkipWhiteSpaceFindChar(const JpegSegment& segment, size_t start_location, char value) { for (size_t location = start_location; location < segment.GetEnd(); ++location) { ValidatedByte validated_byte = segment.GetValidatedByte(location); if (!validated_byte.is_valid) { return segment.GetEnd(); } if (validated_byte.value == Byte(value)) { return location; } if (!std::isspace(validated_byte.value)) { return segment.GetEnd(); } } return segment.GetEnd(); } size_t JpegSegment::GetVariablePayloadSize() const { if (!GetMarker().HasVariablePayloadSize()) { return 0; } size_t payload_location = GetPayloadLocation(); ValidatedByte hi = GetValidatedByte(payload_location); ValidatedByte lo = GetValidatedByte(payload_location + 1); if (!hi.is_valid || !lo.is_valid) { return 0; } return static_cast(hi.value) << 8 | static_cast(lo.value); } bool JpegSegment::BytesAtLocationStartWith(size_t location, const char* str) const { while (*str && Contains(location)) { ValidatedByte validated_byte = GetValidatedByte(location++); if (!validated_byte.is_valid || Byte(*str++) != validated_byte.value) { return false; } } return *str == 0; } bool JpegSegment::BytesAtLocationContain(size_t location, const char* str) const { return Find(location, str) != GetEnd(); } size_t JpegSegment::Find(size_t location, const char* str) const { Byte byte0 = static_cast(*str); while ((location = Find(location, byte0)) < GetEnd()) { if (BytesAtLocationStartWith(location, str)) { return location; } ++location; } return GetEnd(); } size_t JpegSegment::Find(size_t start_location, Byte value) const { if (!begin_segment_ && !end_segment_) { return GetEnd(); } size_t value_location = GetEnd(); if (begin_segment_ && !end_segment_) { value_location = begin_segment_->Find(start_location, value); } else { value_location = DataSegment::Find(start_location, value, begin_segment_, end_segment_); } return Contains(value_location) ? value_location : GetEnd(); } std::string JpegSegment::ExtractXmpPropertyValue( size_t start_location, const char* property_name) const { size_t begin_value_location = FindXmpPropertyValueBegin(start_location, property_name); if (begin_value_location != GetEnd()) { size_t end_value_location = FindXmpPropertyValueEnd(begin_value_location); if (end_value_location != GetEnd()) { DataRange data_range(begin_value_location, end_value_location); return ExtractString(data_range); } } return ""; } size_t JpegSegment::FindXmpPropertyValueBegin(size_t start_location, const char* property_name) const { size_t property_location = Find(start_location, property_name); if (property_location != GetEnd()) { size_t equal_location = SkipWhiteSpaceFindChar( *this, property_location + strlen(property_name), '='); if (equal_location != GetEnd()) { size_t quote_location = SkipWhiteSpaceFindChar(*this, equal_location + 1, '"'); if (quote_location != GetEnd()) { return quote_location + 1; } } } return GetEnd(); } size_t JpegSegment::FindXmpPropertyValueEnd(size_t start_location) const { return Find(start_location, Byte('"')); } std::string JpegSegment::ExtractString(const DataRange& data_range) const { std::string value; if (Contains(data_range.GetBegin()) && data_range.GetEnd() <= GetEnd()) { size_t start_location = data_range.GetBegin(); size_t length = data_range.GetLength(); value.resize(length, ' '); for (size_t index = 0; index < length; ++index) { ValidatedByte validated_byte = GetValidatedByte(start_location + index); if (!validated_byte.value) { // Invalid bytes have a zero value. value.resize(0); break; } value[index] = static_cast(validated_byte.value); } } return value; } void JpegSegment::GetPayloadHexDumpStrings(size_t byte_count, std::string* hex_string, std::string* ascii_string) const { stringstream ascii_stream; stringstream hex_stream; hex_stream << std::hex << std::uppercase; size_t dump_count = GetMarker().IsEntropySegmentDelimiter() ? byte_count : std::min(byte_count, GetLength() - 2); for (size_t index = 0; index < dump_count; ++index) { ValidatedByte payload_byte = GetValidatedByte(GetPayloadLocation() + index); if (!payload_byte.is_valid) { break; } Byte value = payload_byte.value; hex_stream << std::setfill('0') << std::setw(2) << static_cast(value); ascii_stream << (isprint(value) ? static_cast(value) : '.'); } size_t current_count = ascii_stream.str().length(); for (size_t index = current_count; index < byte_count; ++index) { hex_stream << " "; ascii_stream << "."; } *hex_string = hex_stream.str(); *ascii_string = ascii_stream.str(); } } // namespace image_io } // namespace photos_editing_formats