#689: Provide support for JPEG previews stored in the XMP metadata
This commit is contained in:
+121
-1
@@ -36,6 +36,7 @@ EXIV2_RCSID("@(#) $Id$")
|
||||
# include "exv_conf.h"
|
||||
#endif
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#include "preview.hpp"
|
||||
@@ -66,6 +67,11 @@ namespace {
|
||||
return l < r;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Decode a Base64 string.
|
||||
*/
|
||||
std::string decodeBase64(const std::string &src);
|
||||
|
||||
/*!
|
||||
Base class for image loaders. Provides virtual methods for reading properties
|
||||
and DataBuf.
|
||||
@@ -236,6 +242,29 @@ namespace {
|
||||
//! Function to create new LoaderTiff
|
||||
Loader::AutoPtr createLoaderTiff(PreviewId id, const Image &image, int parIdx);
|
||||
|
||||
//! Loader for JPEG previews stored in the XMP metadata
|
||||
class LoaderXmpJpeg : public Loader {
|
||||
public:
|
||||
//! Constructor
|
||||
LoaderXmpJpeg(PreviewId id, const Image &image, int parIdx);
|
||||
|
||||
//! Get properties of a preview image with given params
|
||||
virtual PreviewProperties getProperties() const;
|
||||
|
||||
//! Get a buffer that contains the preview image
|
||||
virtual DataBuf getData() const;
|
||||
|
||||
//! Read preview image dimensions
|
||||
virtual bool readDimensions();
|
||||
|
||||
protected:
|
||||
//! Preview image data
|
||||
std::string preview_;
|
||||
};
|
||||
|
||||
//! Function to create new LoaderXmpJpeg
|
||||
Loader::AutoPtr createLoaderXmpJpeg(PreviewId id, const Image &image, int parIdx);
|
||||
|
||||
// *****************************************************************************
|
||||
// class member definitions
|
||||
|
||||
@@ -265,7 +294,8 @@ namespace {
|
||||
{ 0, createLoaderExifJpeg, 5 },
|
||||
{ 0, createLoaderExifJpeg, 6 },
|
||||
{ "image/x-canon-cr2", createLoaderExifJpeg, 7 },
|
||||
{ 0, createLoaderExifJpeg, 8 }
|
||||
{ 0, createLoaderExifJpeg, 8 },
|
||||
{ 0, createLoaderXmpJpeg, 0 }
|
||||
};
|
||||
|
||||
const LoaderExifJpeg::Param LoaderExifJpeg::param_[] = {
|
||||
@@ -663,6 +693,96 @@ namespace {
|
||||
return DataBuf(mio.mmap(), mio.size());
|
||||
}
|
||||
|
||||
LoaderXmpJpeg::LoaderXmpJpeg(PreviewId id, const Image &image, int parIdx)
|
||||
: Loader(id, image)
|
||||
{
|
||||
(void)parIdx;
|
||||
|
||||
const Exiv2::XmpData &xmpData = image_.xmpData();
|
||||
XmpData::const_iterator imageDatum = xmpData.findKey(XmpKey("Xmp.xmp.Thumbnails[1]/xmpGImg:image"));
|
||||
if (imageDatum == xmpData.end()) return;
|
||||
XmpData::const_iterator formatDatum = xmpData.findKey(XmpKey("Xmp.xmp.Thumbnails[1]/xmpGImg:format"));
|
||||
if (formatDatum == xmpData.end()) return;
|
||||
XmpData::const_iterator widthDatum = xmpData.findKey(XmpKey("Xmp.xmp.Thumbnails[1]/xmpGImg:width"));
|
||||
if (widthDatum == xmpData.end()) return;
|
||||
XmpData::const_iterator heightDatum = xmpData.findKey(XmpKey("Xmp.xmp.Thumbnails[1]/xmpGImg:height"));
|
||||
if (heightDatum == xmpData.end()) return;
|
||||
|
||||
if (formatDatum->toString() != "JPEG") return;
|
||||
|
||||
width_ = widthDatum->toLong();
|
||||
height_ = heightDatum->toLong();
|
||||
preview_ = decodeBase64(imageDatum->toString());
|
||||
size_ = preview_.size();
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
Loader::AutoPtr createLoaderXmpJpeg(PreviewId id, const Image &image, int parIdx)
|
||||
{
|
||||
return Loader::AutoPtr(new LoaderXmpJpeg(id, image, parIdx));
|
||||
}
|
||||
|
||||
PreviewProperties LoaderXmpJpeg::getProperties() const
|
||||
{
|
||||
PreviewProperties prop = Loader::getProperties();
|
||||
prop.mimeType_ = "image/jpeg";
|
||||
prop.extension_ = ".jpg";
|
||||
#ifdef EXV_UNICODE_PATH
|
||||
prop.wextension_ = EXV_WIDEN(".jpg");
|
||||
#endif
|
||||
return prop;
|
||||
}
|
||||
|
||||
DataBuf LoaderXmpJpeg::getData() const
|
||||
{
|
||||
if (!valid()) return DataBuf();
|
||||
return DataBuf(reinterpret_cast<const Exiv2::byte*>(preview_.data()), preview_.size());
|
||||
}
|
||||
|
||||
bool LoaderXmpJpeg::readDimensions()
|
||||
{
|
||||
return valid();
|
||||
}
|
||||
|
||||
static const char encodeBase64Table[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
std::string decodeBase64(const std::string& src)
|
||||
{
|
||||
const unsigned int srcSize = src.size();
|
||||
|
||||
// create decoding table
|
||||
unsigned int invalid = 64;
|
||||
unsigned int decodeBase64Table[256];
|
||||
for (unsigned int i = 0; i < 256; i++) decodeBase64Table[i] = invalid;
|
||||
for (unsigned int i = 0; i < 64; i++) decodeBase64Table[(unsigned char)encodeBase64Table[i]] = i;
|
||||
|
||||
// calculate dest size
|
||||
unsigned int validSrcSize = 0;
|
||||
for (unsigned int srcPos = 0; srcPos < srcSize; srcPos++) {
|
||||
if (decodeBase64Table[(unsigned char)src[srcPos]] != invalid) validSrcSize++;
|
||||
}
|
||||
if (validSrcSize > UINT_MAX / 3) return std::string(); // avoid integer overflow
|
||||
const unsigned int destSize = (validSrcSize * 3) / 4;
|
||||
|
||||
// allocate dest buffer
|
||||
std::string dest(destSize, 0);
|
||||
|
||||
// decode
|
||||
for (unsigned int srcPos = 0, destPos = 0; destPos < destSize;) {
|
||||
unsigned int buffer = 0;
|
||||
for (int bufferPos = 3; bufferPos >= 0 && srcPos < srcSize; srcPos++) {
|
||||
unsigned int srcValue = decodeBase64Table[(unsigned char)src[srcPos]];
|
||||
if (srcValue == invalid) continue;
|
||||
buffer |= srcValue << (bufferPos * 6);
|
||||
bufferPos--;
|
||||
}
|
||||
for (int bufferPos = 2; bufferPos >= 0 && destPos < destSize; bufferPos--, destPos++) {
|
||||
dest[destPos] = buffer >> (bufferPos * 8);
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
@@ -529,6 +529,7 @@ Xmp.xmpMM.History[8]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-10-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-10-lev2.eps
|
||||
@@ -735,6 +736,7 @@ Xmp.xmpMM.History[10]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-8-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-8-lev2.eps
|
||||
@@ -915,6 +917,7 @@ Xmp.xmpMM.History[9]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-9-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-9-lev2.eps
|
||||
@@ -1063,6 +1066,7 @@ Xmp.xmpMM.History[7]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs-lev2.eps
|
||||
@@ -1205,6 +1209,7 @@ Xmp.xmpMM.History[6]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs2-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs2-lev2.eps
|
||||
@@ -1341,6 +1346,7 @@ Xmp.xmpMM.History[5]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs3-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs3-lev2.eps
|
||||
@@ -1471,6 +1477,7 @@ Xmp.xmpMM.History[4]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs4-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs4-lev2.eps
|
||||
@@ -1567,6 +1574,7 @@ Xmp.dc.format XmpText 22 application/postscri
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs5-lev2.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs5-lev2.eps
|
||||
@@ -1685,6 +1693,7 @@ Xmp.xmpMM.History[2]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs5-lev3-nodocthumb.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs5-lev3-nodocthumb.eps
|
||||
@@ -1801,6 +1810,7 @@ Xmp.xmpMM.History[1]/stEvt:changed XmpText 1 /
|
||||
Exit code: 253
|
||||
|
||||
Command: exiv2 -pp eps-flat_oodraw_ai-cs5-lev3.eps
|
||||
Preview 1: image/jpeg, 256x208 pixels, 3166 bytes
|
||||
Exit code: 0
|
||||
|
||||
Command: exiv2 -f -eX eps-flat_oodraw_ai-cs5-lev3.eps
|
||||
|
||||
Reference in New Issue
Block a user