summaryrefslogtreecommitdiff
path: root/src/gcontainer/gcontainer.cc
blob: 1179778c2d3de4d458bf5e78cb30a107686bc8d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#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/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::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 photos_editing_formats::image_io::ReportErrorPolicy;
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_file_name must be a JPEG file.
// 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(const string& input_file_name,
                             std::shared_ptr<DataSegment>* image_data_segment,
                             DataRange* first_image_range) {
  if (first_image_range == nullptr) {
    return false;
  }

  // Get the input and output setup.
  MessageHandler::Get()->ClearMessages();
  auto data_segment =
      ReadEntireFile(input_file_name, ReportErrorPolicy::kReportError);
  if (!data_segment) {
    return false;
  }

  // Get the jpeg info and first image range from the input.
  DataSegmentDataSource data_source(data_segment);
  JpegInfoBuilder jpeg_info_builder;
  jpeg_info_builder.SetImageLimit(1);
  JpegScanner jpeg_scanner;
  jpeg_scanner.Run(&data_source, &jpeg_info_builder);
  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()) {
    MessageHandler::Get()->ReportMessage(Message::kPrematureEndOfDataError,
                                         "No Images Found");
    return false;
  }

  *image_data_segment = data_segment;
  *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) {
  auto output_stream =
      OpenOutputFile(output_file_name, ReportErrorPolicy::kReportError);
  if (!output_stream) {
    return false;
  }

  OStreamDataDestination output_destination(std::move(output_stream));
  output_destination.SetName(output_file_name);

  DataRange image_range;
  std::shared_ptr<DataSegment> data_segment;
  if (!ExtractFirstImageInJpeg(input_file_name, &data_segment, &image_range)) {
    return false;
  }

  output_destination.StartTransfer();
  DataSegmentDataSource data_source(data_segment);
  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, ReportErrorPolicy::kReportError);
    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 string& input_file_name,
                         size_t file_start_offset, size_t file_length,
                         string* out_file_contents) {
  if (out_file_contents == nullptr || file_start_offset < 0 ||
      file_length == 0) {
    return false;
  }

  DataRange image_range;
  std::shared_ptr<DataSegment> data_segment;
  if (!ExtractFirstImageInJpeg(input_file_name, &data_segment, &image_range)) {
    return false;
  }

  size_t image_bytes_end_offset = image_range.GetEnd();
  size_t image_file_end = data_segment->GetEnd();
  size_t file_start_in_image = image_bytes_end_offset + file_start_offset;
  size_t file_end_in_image = file_start_in_image + file_length;
  if (image_file_end < 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.
  std::ifstream input_file_stream(input_file_name);
  input_file_stream.seekg(file_range.GetBegin());
  out_file_contents->resize(file_range_size);
  input_file_stream.read(&(*out_file_contents)[0], file_range_size);
  return true;
}

}  // namespace gcontainer
}  // namespace image_io
}  // namespace photos_editing_formats