You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

883 lines
28 KiB
C++

// SPDX-License-Identifier: GPL-2.0-or-later
// included header files
#include "image.hpp"
#include "config.h"
#include "enforce.hpp"
#include "error.hpp"
#include "futils.hpp"
#include "image_int.hpp"
#include "safe_op.hpp"
#include "slice.hpp"
#ifdef EXV_ENABLE_BMFF
#include "bmffimage.hpp"
#endif // EXV_ENABLE_BMFF
#include "cr2image.hpp"
#include "crwimage.hpp"
#include "epsimage.hpp"
#include "jpgimage.hpp"
#include "mrwimage.hpp"
#ifdef EXV_HAVE_LIBZ
#include "pngimage.hpp"
#endif // EXV_HAVE_LIBZ
#include "bmpimage.hpp"
#include "gifimage.hpp"
#include "jp2image.hpp"
#include "nikonmn_int.hpp"
#include "orfimage.hpp"
#include "pgfimage.hpp"
#include "psdimage.hpp"
#include "rafimage.hpp"
#include "rw2image.hpp"
#include "tags_int.hpp"
#include "tgaimage.hpp"
#include "tiffimage.hpp"
#include "webpimage.hpp"
#include "xmpsidecar.hpp"
#ifdef EXV_ENABLE_VIDEO
#include "asfvideo.hpp"
#include "matroskavideo.hpp"
#include "quicktimevideo.hpp"
#include "riffvideo.hpp"
#endif // EXV_ENABLE_VIDEO
// + standard includes
#include <array>
#include <cstdio>
#include <cstring>
#include <limits>
5 years ago
#include <set>
// *****************************************************************************
namespace {
using namespace Exiv2;
//! Struct for storing image types and function pointers.
struct Registry {
//! Comparison operator to compare a Registry structure with an image type
bool operator==(const ImageType& imageType) const {
return imageType == imageType_;
}
// DATA
ImageType imageType_;
NewInstanceFct newInstance_;
IsThisTypeFct isThisType_;
AccessMode exifSupport_;
AccessMode iptcSupport_;
AccessMode xmpSupport_;
AccessMode commentSupport_;
};
/// \todo Use std::unordered_map for implementing the registry. Avoid to use ImageType::none
constexpr auto registry = std::array{
// image type creation fct type check Exif mode IPTC mode XMP mode Comment mode
//--------------- --------------- ---------- ----------- ----------- ----------- ------------
Registry{ImageType::jpeg, newJpegInstance, isJpegType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
Registry{ImageType::exv, newExvInstance, isExvType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
Registry{ImageType::cr2, newCr2Instance, isCr2Type, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::crw, newCrwInstance, isCrwType, amReadWrite, amNone, amNone, amReadWrite},
Registry{ImageType::mrw, newMrwInstance, isMrwType, amRead, amRead, amRead, amNone},
Registry{ImageType::tiff, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::webp, newWebPInstance, isWebPType, amReadWrite, amNone, amReadWrite, amNone},
Registry{ImageType::dng, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::nef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::pef, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::arw, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone},
Registry{ImageType::rw2, newRw2Instance, isRw2Type, amRead, amRead, amRead, amNone},
Registry{ImageType::sr2, newTiffInstance, isTiffType, amRead, amRead, amRead, amNone},
Registry{ImageType::srw, newTiffInstance, isTiffType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::orf, newOrfInstance, isOrfType, amReadWrite, amReadWrite, amReadWrite, amNone},
#ifdef EXV_HAVE_LIBZ
Registry{ImageType::png, newPngInstance, isPngType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
#endif // EXV_HAVE_LIBZ
Registry{ImageType::pgf, newPgfInstance, isPgfType, amReadWrite, amReadWrite, amReadWrite, amReadWrite},
Registry{ImageType::raf, newRafInstance, isRafType, amRead, amRead, amRead, amNone},
Registry{ImageType::eps, newEpsInstance, isEpsType, amNone, amNone, amReadWrite, amNone},
Registry{ImageType::xmp, newXmpInstance, isXmpType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::gif, newGifInstance, isGifType, amNone, amNone, amNone, amNone},
Registry{ImageType::psd, newPsdInstance, isPsdType, amReadWrite, amReadWrite, amReadWrite, amNone},
Registry{ImageType::tga, newTgaInstance, isTgaType, amNone, amNone, amNone, amNone},
Registry{ImageType::bmp, newBmpInstance, isBmpType, amNone, amNone, amNone, amNone},
Registry{ImageType::jp2, newJp2Instance, isJp2Type, amReadWrite, amReadWrite, amReadWrite, amNone},
// needs to be before bmff because some ftyp files are handled as qt and
// the rest should fall through to bmff
#ifdef EXV_ENABLE_VIDEO
Registry{ImageType::qtime, newQTimeInstance, isQTimeType, amRead, amNone, amRead, amNone},
Registry{ImageType::asf, newAsfInstance, isAsfType, amRead, amNone, amRead, amNone},
Registry{ImageType::riff, newRiffInstance, isRiffType, amRead, amNone, amRead, amNone},
Registry{ImageType::mkv, newMkvInstance, isMkvType, amRead, amNone, amRead, amNone},
#endif // EXV_ENABLE_VIDEO
#ifdef EXV_ENABLE_BMFF
Registry{ImageType::bmff, newBmffInstance, isBmffType, amRead, amRead, amRead, amNone},
#endif // EXV_ENABLE_BMFF
};
std::string pathOfFileUrl(const std::string& url) {
std::string path = url.substr(7);
size_t found = path.find('/');
return (found == std::string::npos) ? path : path.substr(found);
}
} // namespace
// *****************************************************************************
// class member definitions
namespace Exiv2 {
Image::Image(ImageType type, uint16_t supportedMetadata, BasicIo::UniquePtr io) :
io_(std::move(io)), imageType_(type), supportedMetadata_(supportedMetadata) {
}
void Image::printStructure(std::ostream&, PrintStructureOption, size_t /*depth*/) {
throw Error(ErrorCode::kerUnsupportedImageType, io_->path());
}
bool Image::isStringType(uint16_t type) {
return type == Exiv2::asciiString || type == Exiv2::unsignedByte || type == Exiv2::signedByte ||
type == Exiv2::undefined;
}
bool Image::isShortType(uint16_t type) {
return type == Exiv2::unsignedShort || type == Exiv2::signedShort;
}
bool Image::isLongType(uint16_t type) {
return type == Exiv2::unsignedLong || type == Exiv2::signedLong;
}
bool Image::isLongLongType(uint16_t type) {
return type == Exiv2::unsignedLongLong || type == Exiv2::signedLongLong;
}
bool Image::isRationalType(uint16_t type) {
return type == Exiv2::unsignedRational || type == Exiv2::signedRational;
}
bool Image::is2ByteType(uint16_t type) {
return isShortType(type);
}
bool Image::is4ByteType(uint16_t type) {
return isLongType(type) || type == Exiv2::tiffFloat || type == Exiv2::tiffIfd;
}
bool Image::is8ByteType(uint16_t type) {
return isRationalType(type) || isLongLongType(type) || type == Exiv2::tiffIfd8 || type == Exiv2::tiffDouble;
}
bool Image::isPrintXMP(uint16_t type, Exiv2::PrintStructureOption option) {
return type == 700 && option == kpsXMP;
}
bool Image::isPrintICC(uint16_t type, Exiv2::PrintStructureOption option) {
return type == 0x8773 && option == kpsIccProfile;
}
bool Image::isBigEndianPlatform() {
union {
uint32_t i;
char c[4];
} e = {0x01000000};
return e.c[0] != 0;
}
bool Image::isLittleEndianPlatform() {
return !isBigEndianPlatform();
}
uint64_t Image::byteSwap(uint64_t value, bool bSwap) {
uint64_t result = 0;
auto source_value = reinterpret_cast<byte*>(&value);
auto destination_value = reinterpret_cast<byte*>(&result);
for (int i = 0; i < 8; i++)
destination_value[i] = source_value[8 - i - 1];
return bSwap ? result : value;
}
uint32_t Image::byteSwap(uint32_t value, bool bSwap) {
uint32_t result = 0;
result |= (value & 0x000000FFU) << 24;
result |= (value & 0x0000FF00U) << 8;
result |= (value & 0x00FF0000U) >> 8;
result |= (value & 0xFF000000U) >> 24;
return bSwap ? result : value;
}
uint16_t Image::byteSwap(uint16_t value, bool bSwap) {
uint16_t result = 0;
result |= (value & 0x00FFU) << 8;
result |= (value & 0xFF00U) >> 8;
return bSwap ? result : value;
}
uint16_t Image::byteSwap2(const DataBuf& buf, size_t offset, bool bSwap) {
uint16_t v = 0;
auto p = reinterpret_cast<char*>(&v);
p[0] = buf.read_uint8(offset);
p[1] = buf.read_uint8(offset + 1);
return Image::byteSwap(v, bSwap);
}
uint32_t Image::byteSwap4(const DataBuf& buf, size_t offset, bool bSwap) {
uint32_t v = 0;
auto p = reinterpret_cast<char*>(&v);
p[0] = buf.read_uint8(offset);
p[1] = buf.read_uint8(offset + 1);
p[2] = buf.read_uint8(offset + 2);
p[3] = buf.read_uint8(offset + 3);
return Image::byteSwap(v, bSwap);
}
/// \todo not used internally. At least we should test it
uint64_t Image::byteSwap8(const DataBuf& buf, size_t offset, bool bSwap) {
uint64_t v = 0;
auto p = reinterpret_cast<byte*>(&v);
for (int i = 0; i < 8; i++)
p[i] = buf.read_uint8(offset + i);
return Image::byteSwap(v, bSwap);
}
const char* Image::typeName(uint16_t tag) {
//! List of TIFF image tags
const char* result = nullptr;
switch (tag) {
case Exiv2::unsignedByte:
result = "BYTE";
break;
case Exiv2::asciiString:
result = "ASCII";
break;
case Exiv2::unsignedShort:
result = "SHORT";
break;
case Exiv2::unsignedLong:
result = "LONG";
break;
case Exiv2::unsignedRational:
result = "RATIONAL";
break;
case Exiv2::signedByte:
result = "SBYTE";
break;
case Exiv2::undefined:
result = "UNDEFINED";
break;
case Exiv2::signedShort:
result = "SSHORT";
break;
case Exiv2::signedLong:
result = "SLONG";
break;
case Exiv2::signedRational:
result = "SRATIONAL";
break;
case Exiv2::tiffFloat:
result = "FLOAT";
break;
case Exiv2::tiffDouble:
result = "DOUBLE";
break;
case Exiv2::tiffIfd:
result = "IFD";
break;
default:
result = "unknown";
break;
}
return result;
}
static bool typeValid(uint16_t type) {
return type >= 1 && type <= 13;
}
static std::set<size_t> visits; // #547
void Image::printIFDStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t start,
bool bSwap, char c, size_t depth) {
if (depth == 1)
visits.clear();
bool bFirst = true;
// buffer
const size_t dirSize = 32;
DataBuf dir(dirSize);
bool bPrint = option == kpsBasic || option == kpsRecursive;
do {
// Read top of directory
io.seekOrThrow(start, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
io.readOrThrow(dir.data(), 2, ErrorCode::kerCorruptedMetadata);
uint16_t dirLength = byteSwap2(dir, 0, bSwap);
// Prevent infinite loops. (GHSA-m479-7frc-gqqg)
enforce(dirLength > 0, ErrorCode::kerCorruptedMetadata);
if (dirLength > 500) // tooBig
throw Error(ErrorCode::kerTiffDirectoryTooLarge);
if (bFirst && bPrint) {
out << Internal::indent(depth) << Internal::stringFormat("STRUCTURE OF TIFF FILE (%c%c): ", c, c) << io.path()
<< std::endl;
}
// Read the dictionary
for (int i = 0; i < dirLength; i++) {
if (visits.find(io.tell()) != visits.end()) { // #547
throw Error(ErrorCode::kerCorruptedMetadata);
}
visits.insert(io.tell());
if (bFirst && bPrint) {
out << Internal::indent(depth) << " address | tag | "
<< " type | count | offset | value\n";
}
bFirst = false;
io.readOrThrow(dir.data(), 12, ErrorCode::kerCorruptedMetadata);
uint16_t tag = byteSwap2(dir, 0, bSwap);
uint16_t type = byteSwap2(dir, 2, bSwap);
uint32_t count = byteSwap4(dir, 4, bSwap);
uint32_t offset = byteSwap4(dir, 8, bSwap);
// Break for unknown tag types else we may segfault.
if (!typeValid(type)) {
EXV_ERROR << "invalid type in tiff structure" << type << std::endl;
throw Error(ErrorCode::kerInvalidTypeValue);
}
std::string sp; // output spacer
// prepare to print the value
uint32_t kount = [=] {
// haul in all the data
if (isPrintXMP(tag, option))
return count;
// ditto
if (isPrintICC(tag, option))
return count;
// restrict long arrays
if (isStringType(type)) {
if (count > 32u)
return 32u;
return count;
}
if (count > 5u)
return 5u;
return count;
}();
uint32_t pad = isStringType(type) ? 1 : 0;
size_t size = [=] {
if (isStringType(type))
return 1;
if (is2ByteType(type))
return 2;
if (is4ByteType(type))
return 4;
if (is8ByteType(type))
return 8;
return 1;
}();
// if ( offset > io.size() ) offset = 0; // Denial of service?
// #55 and #56 memory allocation crash test/data/POC8
const size_t allocate64 = size * count + pad + 20;
if (allocate64 > io.size()) {
throw Error(ErrorCode::kerInvalidMalloc);
}
// Overflow check
enforce(allocate64 <= std::numeric_limits<size_t>::max(), ErrorCode::kerCorruptedMetadata);
DataBuf buf(allocate64); // allocate a buffer
std::copy_n(dir.c_data(8), 4, buf.begin()); // copy dir[8:11] into buffer (short strings)
// We have already checked that this multiplication cannot overflow.
const size_t count_x_size = count * size;
const bool bOffsetIsPointer = count_x_size > 4;
if (bOffsetIsPointer) { // read into buffer
const size_t restore = io.tell(); // save
io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position
io.readOrThrow(buf.data(), count_x_size, ErrorCode::kerCorruptedMetadata); // read
io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // restore
}
if (bPrint) {
const size_t address = start + 2 + i * 12;
const std::string offsetString = bOffsetIsPointer ? Internal::stringFormat("%10u", offset) : "";
out << Internal::indent(depth)
<< Internal::stringFormat("%8zu | %#06x %-28s |%10s |%9u |%10s | ", address, tag, tagName(tag).c_str(),
typeName(type), count, offsetString.c_str());
if (isShortType(type)) {
for (size_t k = 0; k < kount; k++) {
out << sp << byteSwap2(buf, k * size, bSwap);
sp = " ";
}
} else if (isLongType(type)) {
for (size_t k = 0; k < kount; k++) {
out << sp << byteSwap4(buf, k * size, bSwap);
sp = " ";
}
} else if (isRationalType(type)) {
for (size_t k = 0; k < kount; k++) {
uint32_t a = byteSwap4(buf, k * size + 0, bSwap);
uint32_t b = byteSwap4(buf, k * size + 4, bSwap);
out << sp << a << "/" << b;
sp = " ";
}
} else if (isStringType(type)) {
out << sp << Internal::binaryToString(makeSlice(buf, 0, kount));
}
sp = kount == count ? "" : " ...";
out << sp << std::endl;
if (option == kpsRecursive && (tag == 0x8769 /* ExifTag */ || tag == 0x014a /*SubIFDs*/ || type == tiffIfd)) {
for (size_t k = 0; k < count; k++) {
const size_t restore = io.tell();
offset = byteSwap4(buf, k * size, bSwap);
printIFDStructure(io, out, option, offset, bSwap, c, depth + 1);
io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
}
} else if (option == kpsRecursive && tag == 0x83bb /* IPTCNAA */) {
if (count > 0) {
if (static_cast<size_t>(Safe::add(count, offset)) > io.size()) {
throw Error(ErrorCode::kerCorruptedMetadata);
}
const size_t restore = io.tell();
io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position
std::vector<byte> bytes(count); // allocate memory
// TODO: once we have C++11 use bytes.data()
io.readOrThrow(bytes.data(), count, ErrorCode::kerCorruptedMetadata);
io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata);
// TODO: once we have C++11 use bytes.data()
IptcData::printStructure(out, makeSliceUntil(bytes.data(), count), depth);
}
} else if (option == kpsRecursive && tag == 0x927c /* MakerNote */ && count > 10) {
const size_t restore = io.tell(); // save
uint32_t jump = 10;
byte bytes[20];
const auto chars = reinterpret_cast<const char*>(&bytes[0]);
io.seekOrThrow(offset, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position
io.readOrThrow(bytes, jump, ErrorCode::kerCorruptedMetadata); // read
bytes[jump] = 0;
bool bNikon = ::strcmp("Nikon", chars) == 0;
bool bSony = ::strcmp("SONY DSC ", chars) == 0;
if (bNikon) {
// tag is an embedded tiff
const long byteslen = count - jump;
DataBuf bytes(byteslen); // allocate a buffer
io.readOrThrow(bytes.data(), byteslen, ErrorCode::kerCorruptedMetadata); // read
MemIo memIo(bytes.c_data(), byteslen); // create a file
printTiffStructure(memIo, out, option, depth + 1);
} else {
// tag is an IFD
uint32_t punt = bSony ? 12 : 0;
io.seekOrThrow(0, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // position
printIFDStructure(io, out, option, offset + punt, bSwap, c, depth + 1);
}
io.seekOrThrow(restore, BasicIo::beg, ErrorCode::kerCorruptedMetadata); // restore
}
}
if (isPrintXMP(tag, option)) {
buf.write_uint8(count, 0);
out << buf.c_str();
}
if (isPrintICC(tag, option)) {
out.write(buf.c_str(), count);
}
}
if (start) {
io.readOrThrow(dir.data(), 4, ErrorCode::kerCorruptedMetadata);
start = byteSwap4(dir, 0, bSwap);
}
} while (start);
if (bPrint) {
out << Internal::indent(depth) << "END " << io.path() << std::endl;
}
out.flush();
}
void Image::printTiffStructure(BasicIo& io, std::ostream& out, Exiv2::PrintStructureOption option, size_t depth,
size_t offset /*=0*/) {
if (option == kpsBasic || option == kpsXMP || option == kpsRecursive || option == kpsIccProfile) {
// buffer
const size_t dirSize = 32;
DataBuf dir(dirSize);
// read header (we already know for certain that we have a Tiff file)
io.readOrThrow(dir.data(), 8, ErrorCode::kerCorruptedMetadata);
auto c = dir.read_uint8(0);
bool bSwap = (c == 'M' && isLittleEndianPlatform()) || (c == 'I' && isBigEndianPlatform());
size_t start = byteSwap4(dir, 4, bSwap);
printIFDStructure(io, out, option, start + offset, bSwap, c, depth);
}
}
void Image::clearMetadata() {
clearExifData();
clearIptcData();
clearXmpPacket();
clearXmpData();
clearComment();
clearIccProfile();
}
ExifData& Image::exifData() {
return exifData_;
}
IptcData& Image::iptcData() {
return iptcData_;
}
XmpData& Image::xmpData() {
return xmpData_;
}
std::string& Image::xmpPacket() {
// Serialize the current XMP
if (!xmpData_.empty() && !writeXmpFromPacket()) {
XmpParser::encode(xmpPacket_, xmpData_, XmpParser::useCompactFormat | XmpParser::omitAllFormatting);
}
return xmpPacket_;
}
/// \todo not used internally. At least we should test it
void Image::setMetadata(const Image& image) {
if (checkMode(mdExif) & amWrite) {
setExifData(image.exifData());
}
if (checkMode(mdIptc) & amWrite) {
setIptcData(image.iptcData());
}
if (checkMode(mdIccProfile) & amWrite) {
setIccProfile(DataBuf(image.iccProfile()));
}
if (checkMode(mdXmp) & amWrite) {
setXmpPacket(image.xmpPacket());
setXmpData(image.xmpData());
}
if (checkMode(mdComment) & amWrite) {
setComment(image.comment());
}
}
void Image::clearExifData() {
exifData_.clear();
}
void Image::setExifData(const ExifData& exifData) {
exifData_ = exifData;
}
void Image::clearIptcData() {
iptcData_.clear();
}
void Image::setIptcData(const IptcData& iptcData) {
iptcData_ = iptcData;
}
void Image::clearXmpPacket() {
xmpPacket_.clear();
writeXmpFromPacket(true);
}
void Image::setXmpPacket(const std::string& xmpPacket) {
if (XmpParser::decode(xmpData_, xmpPacket)) {
throw Error(ErrorCode::kerInvalidXMP);
}
xmpPacket_ = xmpPacket;
}
void Image::clearXmpData() {
xmpData_.clear();
writeXmpFromPacket(false);
}
void Image::setXmpData(const XmpData& xmpData) {
xmpData_ = xmpData;
writeXmpFromPacket(false);
}
#ifdef EXV_HAVE_XMP_TOOLKIT
void Image::writeXmpFromPacket(bool flag) {
writeXmpFromPacket_ = flag;
}
#else
void Image::writeXmpFromPacket(bool) {
}
#endif
void Image::clearComment() {
comment_.erase();
}
void Image::setComment(const std::string& comment) {
comment_ = comment;
}
void Image::setIccProfile(Exiv2::DataBuf&& iccProfile, bool bTestValid) {
if (bTestValid) {
if (iccProfile.size() < sizeof(long)) {
throw Error(ErrorCode::kerInvalidIccProfile);
}
const size_t size = iccProfile.read_uint32(0, bigEndian);
if (size != iccProfile.size()) {
throw Error(ErrorCode::kerInvalidIccProfile);
}
}
iccProfile_ = std::move(iccProfile);
}
void Image::clearIccProfile() {
iccProfile_.reset();
}
void Image::setByteOrder(ByteOrder byteOrder) {
byteOrder_ = byteOrder;
}
ByteOrder Image::byteOrder() const {
return byteOrder_;
}
uint32_t Image::pixelWidth() const {
return pixelWidth_;
}
uint32_t Image::pixelHeight() const {
return pixelHeight_;
}
const ExifData& Image::exifData() const {
return exifData_;
}
const IptcData& Image::iptcData() const {
return iptcData_;
}
const XmpData& Image::xmpData() const {
return xmpData_;
}
std::string Image::comment() const {
return comment_;
}
const std::string& Image::xmpPacket() const {
return xmpPacket_;
}
BasicIo& Image::io() const {
return *io_;
}
bool Image::writeXmpFromPacket() const {
return writeXmpFromPacket_;
}
const NativePreviewList& Image::nativePreviews() const {
return nativePreviews_;
}
bool Image::good() const {
if (io_->open() != 0)
return false;
IoCloser closer(*io_);
return ImageFactory::checkType(imageType_, *io_, false);
}
/// \todo not used internally. At least we should test it
bool Image::supportsMetadata(MetadataId metadataId) const {
return (supportedMetadata_ & metadataId) != 0;
}
AccessMode Image::checkMode(MetadataId metadataId) const {
return ImageFactory::checkMode(imageType_, metadataId);
}
const std::string& Image::tagName(uint16_t tag) {
if (init_) {
int idx;
const TagInfo* ti;
for (ti = Internal::mnTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::iopTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::gpsTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::ifdTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::exifTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::mpfTagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
for (ti = Internal::Nikon1MakerNote::tagList(), idx = 0; ti[idx].tag_ != 0xffff; ++idx)
tags_[ti[idx].tag_] = ti[idx].name_;
}
init_ = false;
return tags_[tag];
}
AccessMode ImageFactory::checkMode(ImageType type, MetadataId metadataId) {
auto r = std::find(registry.begin(), registry.end(), type);
if (r == registry.end())
throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
AccessMode am = amNone;
switch (metadataId) {
case mdNone:
break;
case mdExif:
am = r->exifSupport_;
break;
case mdIptc:
am = r->iptcSupport_;
break;
case mdXmp:
am = r->xmpSupport_;
break;
case mdComment:
am = r->commentSupport_;
break;
case mdIccProfile:
break;
// no default: let the compiler complain
}
return am;
}
bool ImageFactory::checkType(ImageType type, BasicIo& io, bool advance) {
auto r = std::find(registry.begin(), registry.end(), type);
if (r != registry.end())
return r->isThisType_(io, advance);
return false;
}
ImageType ImageFactory::getType(const std::string& path) {
FileIo fileIo(path);
return getType(fileIo);
}
ImageType ImageFactory::getType(const byte* data, size_t size) {
MemIo memIo(data, size);
return getType(memIo);
}
ImageType ImageFactory::getType(BasicIo& io) {
if (io.open() != 0)
return ImageType::none;
IoCloser closer(io);
for (const auto& r : registry) {
if (r.isThisType_(io, false)) {
return r.imageType_;
}
}
return ImageType::none;
}
BasicIo::UniquePtr ImageFactory::createIo(const std::string& path, bool useCurl) {
Protocol fProt = fileProtocol(path);
#ifdef EXV_USE_CURL
if (useCurl && (fProt == pHttp || fProt == pHttps || fProt == pFtp)) {
return std::make_unique<CurlIo>(path); // may throw
}
#endif
if (fProt == pHttp)
return std::make_unique<HttpIo>(path); // may throw
if (fProt == pFileUri)
return std::make_unique<FileIo>(pathOfFileUrl(path));
if (fProt == pStdin || fProt == pDataUri)
return std::make_unique<XPathIo>(path); // may throw
return std::make_unique<FileIo>(path);
(void)(useCurl);
} // ImageFactory::createIo
Image::UniquePtr ImageFactory::open(const std::string& path, bool useCurl) {
auto image = open(ImageFactory::createIo(path, useCurl)); // may throw
if (!image)
throw Error(ErrorCode::kerFileContainsUnknownImageType, path);
return image;
}
Image::UniquePtr ImageFactory::open(const byte* data, size_t size) {
auto image = open(std::make_unique<MemIo>(data, size)); // may throw
if (!image)
throw Error(ErrorCode::kerMemoryContainsUnknownImageType);
return image;
}
Image::UniquePtr ImageFactory::open(BasicIo::UniquePtr io) {
if (io->open() != 0) {
throw Error(ErrorCode::kerDataSourceOpenFailed, io->path(), strError());
}
for (const auto& r : registry) {
if (r.isThisType_(*io, false)) {
return r.newInstance_(std::move(io), false);
}
}
return nullptr;
}
Image::UniquePtr ImageFactory::create(ImageType type, const std::string& path) {
auto fileIo = std::make_unique<FileIo>(path);
// Create or overwrite the file, then close it
if (fileIo->open("w+b") != 0) {
throw Error(ErrorCode::kerFileOpenFailed, path, "w+b", strError());
}
fileIo->close();
BasicIo::UniquePtr io(std::move(fileIo));
auto image = create(type, std::move(io));
if (!image)
throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
return image;
}
Image::UniquePtr ImageFactory::create(ImageType type) {
auto image = create(type, std::make_unique<MemIo>());
if (!image)
throw Error(ErrorCode::kerUnsupportedImageType, static_cast<int>(type));
return image;
}
Image::UniquePtr ImageFactory::create(ImageType type, BasicIo::UniquePtr io) {
// BasicIo instance does not need to be open
if (type == ImageType::none)
return {};
auto r = std::find(registry.begin(), registry.end(), type);
if (r == registry.end())
return {};
return r->newInstance_(std::move(io), true);
}
// *****************************************************************************
// template, inline and free functions
void append(Blob& blob, const byte* buf, size_t len) {
if (len != 0) {
Blob::size_type size = blob.size();
if (blob.capacity() - size < len) {
blob.reserve(size + 65536);
}
blob.resize(size + len);
std::memcpy(&blob[size], buf, len);
}
} // append
} // namespace Exiv2