mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
Fix 41
This commit is contained in:
@ -257,6 +257,7 @@ bool CxImageJPG::CxExifInfo::process_EXIF(uint8_t * CharBuf, uint32_t length)
|
||||
|
||||
uint8_t * LastExifRefd = CharBuf;
|
||||
|
||||
#if 0
|
||||
/* First directory starts 16 bytes in. Offsets start at 8 bytes in. */
|
||||
if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
|
||||
return false;
|
||||
@ -266,6 +267,18 @@ bool CxImageJPG::CxExifInfo::process_EXIF(uint8_t * CharBuf, uint32_t length)
|
||||
if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
CSafeReader reader(CharBuf, length);
|
||||
/* First directory starts 16 bytes in. Offsets start at 8 bytes in. */
|
||||
if (!ProcessExifDir2(reader.Offset(14), reader.Offset(6), length-6, m_exifinfo, &LastExifRefd))
|
||||
return false;
|
||||
|
||||
/* <Richard Collins> give a chance for a second directory */
|
||||
if (FirstOffset > 8) {
|
||||
if (!ProcessExifDir2(reader.Offset(14+FirstOffset-8), reader.Offset(6), length-6, m_exifinfo, &LastExifRefd))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This is how far the interesting (non thumbnail) part of the exif went. */
|
||||
// int32_t ExifSettingsLength = LastExifRefd - CharBuf;
|
||||
@ -703,6 +716,322 @@ bool CxImageJPG::CxExifInfo::ProcessExifDir(uint8_t * DirStart, uint8_t * Offset
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CxImageJPG::CxExifInfo::ProcessExifDir2(CSafeReader& DirStart, CSafeReader& OffsetBase, unsigned ExifLength,
|
||||
EXIFINFO * const m_exifinfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel)
|
||||
{
|
||||
int32_t de;
|
||||
int32_t a;
|
||||
int32_t NumDirEntries;
|
||||
unsigned ThumbnailOffset = 0;
|
||||
unsigned ThumbnailSize = 0;
|
||||
|
||||
if (NestingLevel > 4){
|
||||
strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)");
|
||||
return false;
|
||||
}
|
||||
|
||||
NumDirEntries = DirStart.Check(2) ? Get16u(DirStart.GetData(0)) : 0;
|
||||
|
||||
if ((DirStart.GetData(2+NumDirEntries*12+2)) > (OffsetBase.GetData(ExifLength))){
|
||||
strcpy(m_szLastError,"Illegally sized directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (de=0;de<NumDirEntries;de++){
|
||||
int32_t Tag, Format, Components;
|
||||
CSafeReader ValuePtr;
|
||||
/* This actually can point to a variety of things; it must be
|
||||
cast to other types when used. But we use it as a byte-by-byte
|
||||
cursor, so we declare it as a pointer to a generic byte here.
|
||||
*/
|
||||
int32_t ByteCount;
|
||||
CSafeReader DirEntry = DirStart.Offset(2+12*de);
|
||||
|
||||
Tag = DirEntry.Check(2) ? Get16u(DirEntry.GetData(0)) : 0;
|
||||
Format = DirEntry.Check(2, 2) ? Get16u(DirEntry.GetData(2)) : 0;
|
||||
Components = DirEntry.Check(4, 4) ? Get32u(DirEntry.GetData(4)) : 0;
|
||||
|
||||
if ((Format-1) >= NUM_FORMATS) {
|
||||
/* (-1) catches illegal zero case as unsigned underflows to positive large */
|
||||
strcpy(m_szLastError,"Illegal format code in EXIF dir");
|
||||
return false;
|
||||
}
|
||||
|
||||
ByteCount = Components * BytesPerFormat[Format];
|
||||
|
||||
if (ByteCount > 4){
|
||||
unsigned OffsetVal;
|
||||
OffsetVal = DirEntry.Check(8, 4) ? Get32u(DirEntry.GetData(8)) : 0;
|
||||
/* If its bigger than 4 bytes, the dir entry contains an offset.*/
|
||||
if (OffsetVal+ByteCount > ExifLength){
|
||||
/* Bogus pointer offset and / or bytecount value */
|
||||
strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
|
||||
return false;
|
||||
}
|
||||
ValuePtr = OffsetBase.Offset(OffsetVal);
|
||||
}else{
|
||||
/* 4 bytes or less and value is in the dir entry itself */
|
||||
ValuePtr = DirEntry.Offset(8);
|
||||
}
|
||||
|
||||
if (*LastExifRefdP < ValuePtr.GetData(ByteCount)){
|
||||
/* Keep track of last byte in the exif header that was
|
||||
actually referenced. That way, we know where the
|
||||
discardable thumbnail data begins.
|
||||
*/
|
||||
*LastExifRefdP = ValuePtr.GetData(ByteCount);
|
||||
}
|
||||
|
||||
/* Extract useful components of tag */
|
||||
switch(Tag){
|
||||
|
||||
case TAG_MAKE:
|
||||
if (ValuePtr.Check(31)) strncpy(m_exifinfo->CameraMake, (char*)ValuePtr.GetData(0), 31);
|
||||
break;
|
||||
|
||||
case TAG_MODEL:
|
||||
if (ValuePtr.Check(39)) strncpy(m_exifinfo->CameraModel, (char*)ValuePtr.GetData(0), 39);
|
||||
break;
|
||||
|
||||
case TAG_EXIF_VERSION:
|
||||
if (ValuePtr.Check(4)) strncpy(m_exifinfo->Version,(char*)ValuePtr.GetData(0), 4);
|
||||
break;
|
||||
|
||||
case TAG_DATETIME_ORIGINAL:
|
||||
if (ValuePtr.Check(19)) strncpy(m_exifinfo->DateTime, (char*)ValuePtr.GetData(0), 19);
|
||||
break;
|
||||
|
||||
case TAG_USERCOMMENT:
|
||||
// Olympus has this padded with trailing spaces. Remove these first.
|
||||
if (ValuePtr.Check(ByteCount))
|
||||
{
|
||||
for (a=ByteCount;;){
|
||||
a--;
|
||||
if (*((char*)ValuePtr.GetData(a)) == ' '){
|
||||
*((char*)ValuePtr.GetData(a)) = '\0';
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
if (a == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the comment */
|
||||
if (ValuePtr.Check(5) && memcmp(ValuePtr.GetData(0), "ASCII",5) == 0){
|
||||
for (a=5;a<10;a++){
|
||||
char c;
|
||||
c = *((char*)ValuePtr.GetData(a));
|
||||
if (c != '\0' && c != ' '){
|
||||
if (ValuePtr.Check(a, 199))
|
||||
strncpy(m_exifinfo->Comments, (char*)ValuePtr.GetData(a), 199);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
if (ValuePtr.Check(199))
|
||||
strncpy(m_exifinfo->Comments, (char*)ValuePtr.GetData(0), 199);
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_FNUMBER:
|
||||
/* Simplest way of expressing aperture, so I trust it the most.
|
||||
(overwrite previously computd value if there is one)
|
||||
*/
|
||||
m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_APERTURE:
|
||||
case TAG_MAXAPERTURE:
|
||||
/* More relevant info always comes earlier, so only
|
||||
use this field if we don't have appropriate aperture
|
||||
information yet.
|
||||
*/
|
||||
if (m_exifinfo->ApertureFNumber == 0){
|
||||
m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat2(ValuePtr, Format)*log(2.0f)*0.5);
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_BRIGHTNESS:
|
||||
m_exifinfo->Brightness = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_FOCALLENGTH:
|
||||
/* Nice digital cameras actually save the focal length
|
||||
as a function of how farthey are zoomed in.
|
||||
*/
|
||||
|
||||
m_exifinfo->FocalLength = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_SUBJECT_DISTANCE:
|
||||
/* Inidcates the distacne the autofocus camera is focused to.
|
||||
Tends to be less accurate as distance increases.
|
||||
*/
|
||||
m_exifinfo->Distance = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_EXPOSURETIME:
|
||||
/* Simplest way of expressing exposure time, so I
|
||||
trust it most. (overwrite previously computd value
|
||||
if there is one)
|
||||
*/
|
||||
m_exifinfo->ExposureTime =
|
||||
(float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_SHUTTERSPEED:
|
||||
/* More complicated way of expressing exposure time,
|
||||
so only use this value if we don't already have it
|
||||
from somewhere else.
|
||||
*/
|
||||
if (m_exifinfo->ExposureTime == 0){
|
||||
m_exifinfo->ExposureTime = (float)
|
||||
(1/exp(ConvertAnyFormat2(ValuePtr, Format)*log(2.0f)));
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_FLASH:
|
||||
if ((int32_t)ConvertAnyFormat2(ValuePtr, Format) & 7){
|
||||
m_exifinfo->FlashUsed = 1;
|
||||
}else{
|
||||
m_exifinfo->FlashUsed = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_ORIENTATION:
|
||||
m_exifinfo->Orientation = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){
|
||||
strcpy(m_szLastError,"Undefined rotation value");
|
||||
m_exifinfo->Orientation = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_EXIF_IMAGELENGTH:
|
||||
case TAG_EXIF_IMAGEWIDTH:
|
||||
/* Use largest of height and width to deal with images
|
||||
that have been rotated to portrait format.
|
||||
*/
|
||||
a = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
if (ExifImageWidth < a) ExifImageWidth = a;
|
||||
break;
|
||||
|
||||
case TAG_FOCALPLANEXRES:
|
||||
m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_FOCALPLANEYRES:
|
||||
m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_RESOLUTIONUNIT:
|
||||
switch((int32_t)ConvertAnyFormat2(ValuePtr, Format)){
|
||||
case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */
|
||||
case 2: m_exifinfo->ResolutionUnit = 1.0f; break;
|
||||
case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/
|
||||
case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/
|
||||
case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/
|
||||
}
|
||||
break;
|
||||
|
||||
case TAG_FOCALPLANEUNITS:
|
||||
switch((int32_t)ConvertAnyFormat2(ValuePtr, Format)){
|
||||
case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */
|
||||
case 2: m_exifinfo->FocalplaneUnits = 1.0f; break;
|
||||
case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/
|
||||
case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/
|
||||
case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/
|
||||
}
|
||||
break;
|
||||
|
||||
// Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>
|
||||
|
||||
case TAG_EXPOSURE_BIAS:
|
||||
m_exifinfo->ExposureBias = (float) ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_WHITEBALANCE:
|
||||
m_exifinfo->Whitebalance = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_METERING_MODE:
|
||||
m_exifinfo->MeteringMode = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_EXPOSURE_PROGRAM:
|
||||
m_exifinfo->ExposureProgram = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_ISO_EQUIVALENT:
|
||||
m_exifinfo->ISOequivalent = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
|
||||
break;
|
||||
|
||||
case TAG_COMPRESSION_LEVEL:
|
||||
m_exifinfo->CompressionLevel = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_XRESOLUTION:
|
||||
m_exifinfo->Xresolution = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
case TAG_YRESOLUTION:
|
||||
m_exifinfo->Yresolution = (float)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_THUMBNAIL_OFFSET:
|
||||
ThumbnailOffset = (unsigned)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
case TAG_THUMBNAIL_LENGTH:
|
||||
ThumbnailSize = (unsigned)ConvertAnyFormat2(ValuePtr, Format);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
|
||||
unsigned Offset = ValuePtr.Check(4) ? Get32u(ValuePtr.GetData(0)) : 0;
|
||||
if (Offset>8){
|
||||
if (!OffsetBase.Check(Offset))
|
||||
{
|
||||
strcpy(m_szLastError,"Illegal subdirectory link");
|
||||
return false;
|
||||
}
|
||||
ProcessExifDir2(OffsetBase.Offset(Offset), OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
/* In addition to linking to subdirectories via exif tags,
|
||||
there's also a potential link to another directory at the end
|
||||
of each directory. This has got to be the result of a
|
||||
committee!
|
||||
*/
|
||||
unsigned Offset;
|
||||
Offset = DirStart.Check(2+12*NumDirEntries, 2) ? Get16u(DirStart.GetData(2+12*NumDirEntries)) : 0;
|
||||
if (Offset){
|
||||
if (!OffsetBase.Check(Offset))
|
||||
{
|
||||
strcpy(m_szLastError,"Illegal subdirectory link");
|
||||
return false;
|
||||
}
|
||||
ProcessExifDir2(OffsetBase.Offset(Offset), OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ThumbnailSize && ThumbnailOffset){
|
||||
if (ThumbnailSize + ThumbnailOffset <= ExifLength){
|
||||
/* The thumbnail pointer appears to be valid. Store it. */
|
||||
m_exifinfo->ThumbnailPointer = OffsetBase.GetData(ThumbnailOffset);
|
||||
m_exifinfo->ThumbnailSize = ThumbnailSize;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/*--------------------------------------------------------------------------
|
||||
Evaluate number, be it int32_t, rational, or float from directory.
|
||||
@ -743,6 +1072,58 @@ double CxImageJPG::CxExifInfo::ConvertAnyFormat(void * ValuePtr, int32_t Format)
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
double CxImageJPG::CxExifInfo::ConvertAnyFormat2(CSafeReader& reader, int32_t Format)
|
||||
{
|
||||
double Value;
|
||||
Value = 0;
|
||||
|
||||
switch(Format){
|
||||
case FMT_SBYTE:
|
||||
case FMT_BYTE: if (!reader.Check(1)) {return Value;} break;
|
||||
case FMT_USHORT:
|
||||
case FMT_SSHORT: if (!reader.Check(2)) {return Value;} break;
|
||||
case FMT_ULONG:
|
||||
case FMT_SLONG: if (!reader.Check(4)) {return Value;} break;
|
||||
case FMT_URATIONAL:
|
||||
case FMT_SRATIONAL: if (!reader.Check(8)) {return Value;} break;
|
||||
case FMT_SINGLE: if (!reader.Check(sizeof(float))) {return Value;} break;
|
||||
case FMT_DOUBLE: if (!reader.Check(sizeof(double))) {return Value;} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
void * ValuePtr = (void*)reader.GetData(0);
|
||||
switch(Format){
|
||||
case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
|
||||
case FMT_BYTE: Value = *(uint8_t *)ValuePtr; break;
|
||||
|
||||
case FMT_USHORT: Value = Get16u(ValuePtr); break;
|
||||
case FMT_ULONG: Value = Get32u(ValuePtr); break;
|
||||
|
||||
case FMT_URATIONAL:
|
||||
case FMT_SRATIONAL:
|
||||
{
|
||||
int32_t Num,Den;
|
||||
Num = Get32s(ValuePtr);
|
||||
Den = Get32s(4+(char *)ValuePtr);
|
||||
if (Den == 0){
|
||||
Value = 0;
|
||||
}else{
|
||||
Value = (double)Num/Den;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FMT_SSHORT: Value = (int16_t)Get16u(ValuePtr); break;
|
||||
case FMT_SLONG: Value = Get32s(ValuePtr); break;
|
||||
|
||||
/* Not sure if this is correct (never seen float used in Exif format)
|
||||
*/
|
||||
case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
|
||||
case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
|
||||
}
|
||||
return Value;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void CxImageJPG::CxExifInfo::process_COM (const uint8_t * Data, int32_t length)
|
||||
{
|
||||
|
||||
@ -101,6 +101,55 @@ typedef struct tag_Section_t{
|
||||
unsigned Size;
|
||||
} Section_t;
|
||||
|
||||
public:
|
||||
class CSafeReader
|
||||
{
|
||||
private:
|
||||
uint8_t* data;
|
||||
unsigned int len;
|
||||
|
||||
public:
|
||||
CSafeReader(uint8_t* _data = NULL, unsigned int _len = 0)
|
||||
{
|
||||
data = _data;
|
||||
len = _len;
|
||||
}
|
||||
CSafeReader(const CSafeReader& src)
|
||||
{
|
||||
data = src.data;
|
||||
len = src.len;
|
||||
}
|
||||
CSafeReader& operator=(const CSafeReader& src)
|
||||
{
|
||||
data = src.data;
|
||||
len = src.len;
|
||||
return *this;
|
||||
}
|
||||
CSafeReader Offset(unsigned int offset)
|
||||
{
|
||||
if (offset > len)
|
||||
offset = len;
|
||||
CSafeReader reader(data, len);
|
||||
reader.data += offset;
|
||||
reader.len -= offset;
|
||||
return reader;
|
||||
}
|
||||
bool Check(unsigned int size)
|
||||
{
|
||||
if (len >= size)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool Check(unsigned int offset, unsigned int size)
|
||||
{
|
||||
return Check(offset + size);
|
||||
}
|
||||
uint8_t* GetData(unsigned int offset)
|
||||
{
|
||||
return data + offset;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
EXIFINFO* m_exifinfo;
|
||||
char m_szLastError[256];
|
||||
@ -118,9 +167,12 @@ protected:
|
||||
int32_t Get32s(void * Long);
|
||||
uint32_t Get32u(void * Long);
|
||||
double ConvertAnyFormat(void * ValuePtr, int32_t Format);
|
||||
double ConvertAnyFormat2(CSafeReader& reader, int32_t Format);
|
||||
void* FindSection(int32_t SectionType);
|
||||
bool ProcessExifDir(uint8_t * DirStart, uint8_t * OffsetBase, unsigned ExifLength,
|
||||
EXIFINFO * const pInfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel=0);
|
||||
bool ProcessExifDir2(CSafeReader& DirStart, CSafeReader& OffsetBase, unsigned ExifLength,
|
||||
EXIFINFO * const pInfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel=0);
|
||||
int32_t ExifImageWidth;
|
||||
int32_t MotorolaOrder;
|
||||
Section_t Sections[MAX_SECTIONS];
|
||||
|
||||
Reference in New Issue
Block a user