Skip to content

Commit

Permalink
Add previewBuffer type as a workaround for Sony preview images.
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinbackhouse committed Nov 29, 2021
1 parent fde8ed0 commit 5eab1d1
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 12 deletions.
1 change: 1 addition & 0 deletions include/exiv2/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ namespace Exiv2 {
unsignedLongLong =16, //!< Exif LONG LONG type, 64-bit (8-byte) unsigned integer.
signedLongLong =17, //!< Exif LONG LONG type, 64-bit (8-byte) signed integer.
tiffIfd8 =18, //!< TIFF IFD type, 64-bit (8-byte) unsigned integer.
previewBuffer =0xffff, //!< Used when the metadata contains a reference to a preview image that is not stored with the metadata, but somewhere else in the file. (Sony does this. See https://github.com/Exiv2/exiv2/issues/2001)
string =0x10000, //!< IPTC string type.
date =0x10001, //!< IPTC date type.
time =0x10002, //!< IPTC time type.
Expand Down
87 changes: 87 additions & 0 deletions include/exiv2/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,93 @@ namespace Exiv2 {

}; // class TimeValue

/*!
@brief %Value for preview images
This class is used to handle Sony's preview images, which are not
stored in the metadata but instead refer to a location in the main
file. Since the actual preview is not available while we're parsing
the metadata, we create this placeholder value instead.
See: https://github.com/Exiv2/exiv2/issues/2001
*/
class EXIV2API PreviewBufferValue : public Value {
public:
//! Shortcut for a %PreviewBufferValue auto pointer.
typedef std::unique_ptr<PreviewBufferValue> UniquePtr;

//! @name Creators
//@{
//! Default constructor.
PreviewBufferValue();
//! Virtual destructor.
~PreviewBufferValue() override = default;
//@}

//! @name Manipulators
//@{
/*!
@brief Read the value from a character buffer.
@note The byte order is required by the interface but not used by this
method, so just use the default.
@param buf Pointer to the data buffer to read from
@param len Number of bytes in the data buffer
@param byteOrder Byte order. Not needed.
@return 0 if successful<BR>
1 in case of an unsupported time format
*/
int read(const byte* buf, long len, ByteOrder byteOrder = invalidByteOrder) override;
/*!
@brief Set the value to that of the string buf.
@param buf String containing the preview.
@return 0 if successful<BR>
1 in case of an unsupported time format
*/
int read(const std::string& buf) override;
//@}

//! @name Accessors
//@{
/*!
@brief Write value to a character data buffer.
The user must ensure that the buffer has enough memory. Otherwise
the call results in undefined behaviour.
@note The byte order is required by the interface but not used by this
method, so just use the default.
@param buf Data buffer to write to.
@param byteOrder Byte order. Not used.
@return Number of characters written.
*/
long copy(byte* buf, ByteOrder byteOrder = invalidByteOrder) const override;
long count() const override;
long size() const override;
std::ostream& write(std::ostream& os) const override;
//! Returns number of seconds in the day in UTC.
long toLong(long n = 0) const override;
//! Returns number of seconds in the day in UTC converted to float.
float toFloat(long n = 0) const override;
//! Returns number of seconds in the day in UTC converted to Rational.
Rational toRational(long n = 0) const override;
//@}

private:
//! @name Accessors
//@{
//! Internal virtual copy constructor.
PreviewBufferValue* clone_() const override;
//@}

size_t size_;
}; // class PreviewBufferValue

//! Template to determine the TypeId for a type T
template<typename T> TypeId getType();

Expand Down
17 changes: 11 additions & 6 deletions src/tiffvisitor_int.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,12 @@ namespace Exiv2 {
// #1143
if ( object->tag() == 0x2001 && std::string(groupName(object->group())) == "Sony1" ) {
isize=size;

// This a workaround for Sony preview images, which are stored in the body
// of the main file, not in the metadata. Since we don't have access to the
// rest of the file here, we just store a placeholder (of type PreviewBufferValue).
// See https://github.com/Exiv2/exiv2/issues/2001
typeId = previewBuffer;
} else {
#ifndef SUPPRESS_WARNINGS
EXV_ERROR << "Offset of directory " << groupName(object->group())
Expand Down Expand Up @@ -1595,17 +1601,16 @@ namespace Exiv2 {
}
Value::UniquePtr v = Value::create(typeId);
enforce(v.get() != nullptr, kerCorruptedMetadata);
if ( !isize ) {
if (typeId != previewBuffer) {
v->read(pData, size, byteOrder());
} else {
// Prevent large memory allocations: https://github.com/Exiv2/exiv2/issues/1881
enforce(isize <= 1024 * 1024, kerCorruptedMetadata);

// #1143 Write a "hollow" buffer for the preview image
// Sadly: we don't know the exact location of the image in the source (it's near offset)
// And neither TiffReader nor TiffEntryBase have access to the BasicIo object being processed
std::vector<byte> buffer(isize);
v->read(buffer.data() ,isize, byteOrder());
//
// typeId == previewBuffer, so v is a PreviewBufferValue, which just stores the size without
// attempting to read the bytes.
v->read(0, isize, byteOrder());
}

object->setValue(std::move(v));
Expand Down
1 change: 1 addition & 0 deletions src/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace {
{ Exiv2::tiffFloat, "Float", 4 },
{ Exiv2::tiffDouble, "Double", 8 },
{ Exiv2::tiffIfd, "Ifd", 4 },
{ Exiv2::previewBuffer, "PreviewBuffer", 1 },
{ Exiv2::string, "String", 1 },
{ Exiv2::date, "Date", 8 },
{ Exiv2::time, "Time", 11 },
Expand Down
62 changes: 62 additions & 0 deletions src/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ namespace Exiv2 {
case langAlt:
value = UniquePtr(new LangAltValue);
break;
case previewBuffer:
value = UniquePtr(new PreviewBufferValue);
break;
default:
value = UniquePtr(new DataValue(typeId));
break;
Expand Down Expand Up @@ -1205,4 +1208,63 @@ namespace Exiv2 {
return {toLong(n), 1};
}

PreviewBufferValue::PreviewBufferValue()
: Value(previewBuffer)
{
}

int PreviewBufferValue::read(const byte*, long len, ByteOrder /*byteOrder*/)
{
if (len < 0) {
return 1;
}
size_ = static_cast<size_t>(len);
return 0;
}

int PreviewBufferValue::read(const std::string& buf)
{
size_ = buf.size();
return 0;
}

long PreviewBufferValue::copy(byte* /*buf*/, ByteOrder /*byteOrder*/) const
{
return size_;
}

long PreviewBufferValue::count() const
{
return size();
}

long PreviewBufferValue::size() const
{
return static_cast<long>(size_);
}

PreviewBufferValue* PreviewBufferValue::clone_() const
{
return new PreviewBufferValue(*this);
}

std::ostream& PreviewBufferValue::write(std::ostream& os) const
{
return os;
}

long PreviewBufferValue::toLong(long /*n*/) const
{
return 0;
}

float PreviewBufferValue::toFloat(long n) const
{
return static_cast<float>(toLong(n));
}

Rational PreviewBufferValue::toRational(long n) const
{
return {toLong(n), 1};
}
} // namespace Exiv2
8 changes: 2 additions & 6 deletions tests/bugfixes/github/test_issue_1881.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,5 @@ class SonyPreviewImageLargeAllocation(metaclass=CaseMeta):
filename2 = path("$tmp_path/issue_1881_coverage.jpg")
commands = ["$exiv2 -q -d I rm $filename1", "$exiv2 -q -d I rm $filename2"]
stdout = ["",""]
stderr = [
"""Exiv2 exception in erase action for file $filename1:
$kerCorruptedMetadata
""",
""]
retval = [1,0]
stderr = ["",""]
retval = [0,0]

0 comments on commit 5eab1d1

Please sign in to comment.