blob: 61a9c972e3d5a369a520612c7909abbfa4ad2dce [file] [log] [blame]
#include "image_io/jpeg/jpeg_segment.h"
#include <cctype>
#include <iomanip>
#include <sstream>
#include <string>
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<size_t>(hi.value) << 8 | static_cast<size_t>(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<Byte>(*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<char>(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<int>(value);
ascii_stream << (isprint(value) ? static_cast<char>(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