blob: b97fdd796ef7e79e78b78f9dd087f52031f38b4d [file] [log] [blame]
#include "image_io/gcontainer/gcontainer.h"
#include <fstream>
#include "image_io/base/data_segment.h"
#include "image_io/base/data_segment_data_source.h"
#include "image_io/base/istream_data_source.h"
#include "image_io/base/message_handler.h"
#include "image_io/base/ostream_data_destination.h"
#include "image_io/jpeg/jpeg_info.h"
#include "image_io/jpeg/jpeg_info_builder.h"
#include "image_io/jpeg/jpeg_scanner.h"
#include "image_io/utils/file_utils.h"
namespace photos_editing_formats {
namespace image_io {
namespace gcontainer {
namespace {
using photos_editing_formats::image_io::DataRange;
using photos_editing_formats::image_io::DataSegment;
using photos_editing_formats::image_io::DataSegmentDataSource;
using photos_editing_formats::image_io::IStreamRefDataSource;
using photos_editing_formats::image_io::JpegInfoBuilder;
using photos_editing_formats::image_io::JpegScanner;
using photos_editing_formats::image_io::Message;
using photos_editing_formats::image_io::MessageHandler;
using photos_editing_formats::image_io::OStreamDataDestination;
using std::string;
// Populates first_image_range with the first image (from the header metadata
// to the EOI marker) present in the JPEG file input_file_name. Returns true if
// such a first image is found, false otherwise.
//
// input_jpeg_stream must be a JPEG stream.
// image_data_segment is populated with the DataSegment for
// input_file_name, and is populated only in the successful case.
// first_image_range is populated with the first image found in the input file,
// only if such an image is found.
bool ExtractFirstImageInJpeg(std::istream& input_jpeg_stream,
MessageHandler* message_handler,
DataRange* first_image_range) {
if (first_image_range == nullptr) {
return false;
}
// Get the input and output setup.
if (message_handler) {
message_handler->ClearMessages();
}
// Get the jpeg info and first image range from the input.
IStreamRefDataSource data_source(input_jpeg_stream);
JpegInfoBuilder jpeg_info_builder;
jpeg_info_builder.SetImageLimit(1);
JpegScanner jpeg_scanner(message_handler);
jpeg_scanner.Run(&data_source, &jpeg_info_builder);
data_source.Reset();
if (jpeg_scanner.HasError()) {
return false;
}
const auto& jpeg_info = jpeg_info_builder.GetInfo();
const auto& image_ranges = jpeg_info.GetImageRanges();
if (image_ranges.empty()) {
if (message_handler) {
message_handler->ReportMessage(Message::kPrematureEndOfDataError,
"No Images Found");
}
return false;
}
*first_image_range = image_ranges[0];
return true;
}
} // namespace
bool WriteImageAndFiles(const string& input_file_name,
const std::vector<string>& other_files,
const string& output_file_name) {
MessageHandler message_handler;
auto output_stream = OpenOutputFile(output_file_name, &message_handler);
if (!output_stream) {
return false;
}
OStreamDataDestination output_destination(std::move(output_stream),
&message_handler);
output_destination.SetName(output_file_name);
DataRange image_range;
std::unique_ptr<std::istream> input_stream =
OpenInputFile(input_file_name, &message_handler);
if (!ExtractFirstImageInJpeg(*input_stream, &message_handler, &image_range)) {
return false;
}
output_destination.StartTransfer();
IStreamDataSource data_source(
OpenInputFile(input_file_name, &message_handler));
data_source.TransferData(image_range, image_range.GetLength(),
&output_destination);
size_t bytes_transferred = image_range.GetLength();
for (const string& tack_on_file : other_files) {
if (tack_on_file.empty()) {
continue;
}
auto tack_on_data_segment = ReadEntireFile(tack_on_file, &message_handler);
if (!tack_on_data_segment) {
continue;
}
DataSegmentDataSource tack_on_source(tack_on_data_segment);
DataRange tack_on_range = tack_on_data_segment->GetDataRange();
bytes_transferred += tack_on_range.GetLength();
tack_on_source.TransferData(tack_on_range, tack_on_range.GetLength(),
&output_destination);
}
output_destination.FinishTransfer();
return output_destination.GetBytesTransferred() == bytes_transferred &&
!output_destination.HasError();
}
bool ParseFileAfterImage(const std::string& input_file_name,
size_t file_start_offset, size_t file_length,
std::string* out_file_contents) {
std::ifstream input_stream(input_file_name);
if (!input_stream.is_open()) {
return false;
}
return ParseFileAfterImageFromStream(file_start_offset, file_length,
input_stream, out_file_contents);
}
bool ParseFileAfterImageFromStream(size_t start_offset, size_t length,
std::istream& input_jpeg_stream,
std::string* out_contents) {
if (out_contents == nullptr || start_offset < 0 || length == 0) {
return false;
}
size_t curr_posn = input_jpeg_stream.tellg();
input_jpeg_stream.seekg(0, input_jpeg_stream.end);
size_t stream_size = input_jpeg_stream.tellg();
input_jpeg_stream.seekg(curr_posn, input_jpeg_stream.beg);
DataRange image_range;
MessageHandler message_handler;
if (!ExtractFirstImageInJpeg(input_jpeg_stream, &message_handler,
&image_range)) {
return false;
}
size_t image_bytes_end_offset = image_range.GetEnd();
size_t file_start_in_image = image_bytes_end_offset + start_offset;
size_t file_end_in_image = file_start_in_image + length;
if (stream_size < file_end_in_image) {
// Requested file is past the end of the image file.
return false;
}
// Get the file's contents.
const DataRange file_range(file_start_in_image, file_end_in_image);
size_t file_range_size = file_range.GetLength();
// TODO(miraleung): Consider subclassing image_io/data_destination.h and
// transferring bytes directly into the string. TBD pending additional mime
// type getters.
input_jpeg_stream.seekg(file_range.GetBegin(), input_jpeg_stream.beg);
out_contents->resize(file_range_size);
input_jpeg_stream.read(&(*out_contents)[0], file_range_size);
return true;
}
} // namespace gcontainer
} // namespace image_io
} // namespace photos_editing_formats