#include "image_io/gcontainer/gcontainer.h" #include #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& 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 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