Coverage Report

Created: 2026-02-26 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/exiv2/src/crwimage.cpp
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
  File:      crwimage.cpp
4
  Author(s): Andreas Huggel (ahu) <ahuggel@gmx.net>
5
  History:   28-Aug-05, ahu: created
6
 */
7
// included header files
8
#include "crwimage.hpp"
9
#include "basicio.hpp"
10
#include "config.h"
11
#include "crwimage_int.hpp"
12
#include "error.hpp"
13
#include "futils.hpp"
14
#include "tags.hpp"
15
16
#include <cstring>
17
18
#ifdef EXIV2_DEBUG_MESSAGES
19
#include <iostream>
20
#endif
21
22
// *****************************************************************************
23
// class member definitions
24
namespace Exiv2 {
25
26
186
CrwImage::CrwImage(BasicIo::UniquePtr io, bool /*create*/) : Image(ImageType::crw, mdExif | mdComment, std::move(io)) {
27
186
}  // CrwImage::CrwImage
28
29
0
std::string CrwImage::mimeType() const {
30
0
  return "image/x-canon-crw";
31
0
}
32
33
13
uint32_t CrwImage::pixelWidth() const {
34
13
  auto widthIter = exifData_.findKey(Exiv2::ExifKey("Exif.Photo.PixelXDimension"));
35
13
  if (widthIter != exifData_.end() && widthIter->count() > 0) {
36
0
    return widthIter->toUint32();
37
0
  }
38
13
  return 0;
39
13
}
40
41
13
uint32_t CrwImage::pixelHeight() const {
42
13
  auto heightIter = exifData_.findKey(Exiv2::ExifKey("Exif.Photo.PixelYDimension"));
43
13
  if (heightIter != exifData_.end() && heightIter->count() > 0) {
44
0
    return heightIter->toUint32();
45
0
  }
46
13
  return 0;
47
13
}
48
49
0
void CrwImage::setIptcData(const IptcData& /*iptcData*/) {
50
  // not supported
51
0
  throw(Error(ErrorCode::kerInvalidSettingForImage, "IPTC metadata", "CRW"));
52
0
}
53
54
186
void CrwImage::readMetadata() {
55
#ifdef EXIV2_DEBUG_MESSAGES
56
  std::cerr << "Reading CRW file " << io_->path() << "\n";
57
#endif
58
186
  if (io_->open()) {
59
0
    throw Error(ErrorCode::kerDataSourceOpenFailed, io_->path(), strError());
60
0
  }
61
186
  IoCloser closer(*io_);
62
  // Ensure that this is the correct image type
63
186
  if (!isCrwType(*io_, false)) {
64
0
    if (io_->error() || io_->eof())
65
0
      throw Error(ErrorCode::kerFailedToReadImageData);
66
0
    throw Error(ErrorCode::kerNotACrwImage);
67
0
  }
68
186
  clearMetadata();
69
186
  DataBuf file(io().size());
70
186
  io_->read(file.data(), file.size());
71
72
186
  CrwParser::decode(this, io_->mmap(), io_->size());
73
74
186
}  // CrwImage::readMetadata
75
76
0
void CrwImage::writeMetadata() {
77
#ifdef EXIV2_DEBUG_MESSAGES
78
  std::cerr << "Writing CRW file " << io_->path() << "\n";
79
#endif
80
  // Read existing image
81
0
  DataBuf buf;
82
0
  if (io_->open() == 0) {
83
0
    IoCloser closer(*io_);
84
    // Ensure that this is the correct image type
85
0
    if (isCrwType(*io_, false)) {
86
      // Read the image into a memory buffer
87
0
      buf.alloc(io_->size());
88
0
      io_->read(buf.data(), buf.size());
89
0
      if (io_->error() || io_->eof()) {
90
0
        buf.reset();
91
0
      }
92
0
    }
93
0
  }
94
95
0
  Blob blob;
96
0
  CrwParser::encode(blob, buf.c_data(), buf.size(), this);
97
98
  // Write new buffer to file
99
0
  MemIo tempIo;
100
0
  tempIo.write((!blob.empty() ? blob.data() : nullptr), blob.size());
101
0
  io_->close();
102
0
  io_->transfer(tempIo);  // may throw
103
104
0
}  // CrwImage::writeMetadata
105
106
186
void CrwParser::decode(CrwImage* pCrwImage, const byte* pData, size_t size) {
107
  // Parse the image, starting with a CIFF header component
108
186
  Internal::CiffHeader header;
109
186
  header.read(pData, size);
110
186
  header.decode(*pCrwImage);
111
112
  // a hack to get absolute offset of preview image inside CRW structure
113
186
  if (auto preview = header.findComponent(0x2007, 0x0000)) {
114
0
    (pCrwImage->exifData())["Exif.Image2.JPEGInterchangeFormat"] = static_cast<uint32_t>(preview->pData() - pData);
115
0
    (pCrwImage->exifData())["Exif.Image2.JPEGInterchangeFormatLength"] = static_cast<uint32_t>(preview->size());
116
0
  }
117
186
}  // CrwParser::decode
118
119
0
void CrwParser::encode(Blob& blob, const byte* pData, size_t size, const CrwImage* pCrwImage) {
120
  // Parse image, starting with a CIFF header component
121
0
  Internal::CiffHeader header;
122
0
  if (size != 0) {
123
0
    header.read(pData, size);
124
0
  }
125
126
  // Encode Exif tags from image into the CRW parse tree and write the
127
  // structure to the binary image blob
128
0
  Internal::CrwMap::encode(header, *pCrwImage);
129
0
  header.write(blob);
130
0
}
131
132
// *************************************************************************
133
// free functions
134
186
Image::UniquePtr newCrwInstance(BasicIo::UniquePtr io, bool create) {
135
186
  auto image = std::make_unique<CrwImage>(std::move(io), create);
136
186
  if (!image->good()) {
137
0
    return nullptr;
138
0
  }
139
186
  return image;
140
186
}
141
142
24.9k
bool isCrwType(BasicIo& iIo, bool advance) {
143
24.9k
  bool result = true;
144
24.9k
  byte tmpBuf[14];
145
24.9k
  iIo.read(tmpBuf, 14);
146
24.9k
  if (iIo.error() || iIo.eof()) {
147
69
    return false;
148
69
  }
149
24.8k
  if (('I' != tmpBuf[0] || 'I' != tmpBuf[1]) && ('M' != tmpBuf[0] || 'M' != tmpBuf[1])) {
150
11.0k
    result = false;
151
11.0k
  }
152
24.8k
  if (result && std::memcmp(tmpBuf + 6, Internal::CiffHeader::signature(), 8) != 0) {
153
13.1k
    result = false;
154
13.1k
  }
155
24.8k
  if (!advance || !result)
156
24.8k
    iIo.seek(-14, BasicIo::cur);
157
24.8k
  return result;
158
24.9k
}
159
160
}  // namespace Exiv2