Beispiel #1
0
//--------------------------------------------------------------------------
// Process GPS info directory
//--------------------------------------------------------------------------
void CExifParse::ProcessGpsInfo(
                    const unsigned char* const DirStart,
                    int ByteCountUnused,
                    const unsigned char* const OffsetBase,
                    unsigned ExifLength)
{
  int NumDirEntries = Get16(DirStart, m_MotorolaOrder);

  for (int de=0;de<NumDirEntries;de++)
  {
    const unsigned char* DirEntry = DIR_ENTRY_ADDR(DirStart, de);

    unsigned Tag        = Get16(DirEntry, m_MotorolaOrder);
    unsigned Format     = Get16(DirEntry+2, m_MotorolaOrder);
    unsigned Components = (unsigned)Get32(DirEntry+4, m_MotorolaOrder);
    if (Format == 0 || Format > NUM_FORMATS)
    {
      ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
      continue;
    }

    unsigned ComponentSize = BytesPerFormat[Format - 1];
    unsigned ByteCount = Components * ComponentSize;

    const unsigned char* ValuePtr;

    if (ByteCount > 4)
    {
      unsigned OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
      // If its bigger than 4 bytes, the dir entry contains an offset.
      if (OffsetVal+ByteCount > ExifLength)
      {
        // Bogus pointer offset and / or bytecount value
        ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
        continue;
      }
      ValuePtr = OffsetBase+OffsetVal;
    }
    else
    {
      // 4 bytes or less and value is in the dir entry itself
      ValuePtr = DirEntry+8;
    }

    switch(Tag)
    {
      case TAG_GPS_LAT_REF:
        m_ExifInfo->GpsLat[0] = ValuePtr[0];
        m_ExifInfo->GpsLat[1] = 0;
      break;

      case TAG_GPS_LONG_REF:
        m_ExifInfo->GpsLong[0] = ValuePtr[0];
        m_ExifInfo->GpsLong[1] = 0;
      break;

      case TAG_GPS_LAT:
        GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLat);
      break;
      case TAG_GPS_LONG:
        GetLatLong(Format, ValuePtr, ComponentSize, m_ExifInfo->GpsLong);
      break;

      case TAG_GPS_ALT_REF:
        if (ValuePtr[0] != 0)
          m_ExifInfo->GpsAlt[0] = '-';
        m_ExifInfo->GpsAlt[1] = 0;
      break;

      case TAG_GPS_ALT:
        {
          char temp[18];
          sprintf(temp,"%dm", Get32(ValuePtr, m_MotorolaOrder));
          strcat(m_ExifInfo->GpsAlt, temp);
        }
      break;
    }
  }
}
Beispiel #2
0
/**
	Process Exif directory

	@param dib Input FIBITMAP
	@param tiffp Pointer to the TIFF header
	@param offset 0th IFD offset
	@param length Length of the datafile
	@param msb_order Endianess order of the datafile
	@return 
*/
static BOOL 
jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) {
	WORD de, nde;

	std::stack<WORD>			destack;	// directory entries stack
	std::stack<BYTE*>			ifdstack;	// IFD stack
	std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack

	// Keep a list of already visited IFD to avoid stack overflows 
	// when recursive/cyclic directory structures exist. 
	// This kind of recursive Exif file was encountered with Kodak images coming from 
	// KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
	std::map<DWORD, int> visitedIFD;

    #define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))

	// set the metadata model to Exif

	TagLib::MDMODEL md_model = TagLib::EXIF_MAIN;

	// set the pointer to the first IFD and follow it were it leads.

	BYTE *ifdp = (BYTE*)tiffp + offset;

	de = 0;

	do {
		// if there is anything on the stack then pop it off
		if(!destack.empty()) {
			ifdp		= ifdstack.top();	ifdstack.pop();
			de			= destack.top();	destack.pop();
			md_model	= modelstack.top();	modelstack.pop();
		}

		// remember that we've visited this directory so that we don't visit it again later
		DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
		if(visitedIFD.find(visited) != visitedIFD.end()) {
			continue;
		} else {
			visitedIFD[visited] = 1;	// processed
		}

		// determine how many entries there are in the current IFD
		nde = ReadUint16(msb_order, ifdp);

		for(; de < nde; de++) {
			char *pde = NULL;	// pointer to the directory entry
			char *pval = NULL;	// pointer to the tag value
			
			// create a tag
			FITAG *tag = FreeImage_CreateTag();
			if(!tag) return FALSE;

			// point to the directory entry
			pde = (char*) DIR_ENTRY_ADDR(ifdp, de);

			// get the tag ID
			FreeImage_SetTagID(tag, ReadUint16(msb_order, pde));
			// get the tag format
			WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
                // a problem occured : delete the tag (not free'd after)
			    FreeImage_DeleteTag(tag);
				// break out of the for loop
				break;
            }
			FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);

			// get number of components
			FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4));
			// get the size of the tag value in bytes
			FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth((WORD)FreeImage_GetTagType(tag)));

			if(FreeImage_GetTagLength(tag) <= 4) {
				// 4 bytes or less and value is in the dir entry itself
				pval = pde + 8;
			} else {
				// if its bigger than 4 bytes, the directory entry contains an offset
				// first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
				DWORD offset_value = ReadUint32(msb_order, pde + 8);
				if(offset_value > length) {
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				// now check if offset + tag length exceeds buffer
				if(offset_value > length - FreeImage_GetTagLength(tag)) {
					// a problem occured : delete the tag (not free'd after)
					FreeImage_DeleteTag(tag);
					// jump to next entry
					continue;
				}
				pval = (char*)(tiffp + offset_value);
			}

			// check for a IFD offset
			BOOL isIFDOffset = FALSE;
			switch(FreeImage_GetTagID(tag)) {
				case TAG_EXIF_OFFSET:
				case TAG_GPS_OFFSET:
				case TAG_INTEROP_OFFSET:
				case TAG_MAKER_NOTE:
					isIFDOffset = TRUE;
					break;
			}
			if(isIFDOffset)	{
				DWORD sub_offset = 0;
				TagLib::MDMODEL next_mdmodel = md_model;
				BYTE *next_ifd = ifdp;
				
				// get offset and metadata model
				if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
					processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)pval + sub_offset;
				} else {
					processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
					next_ifd = (BYTE*)tiffp + sub_offset;
				}

				if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) {
					// push our current directory state onto the stack
					ifdstack.push(ifdp);
					// bump to the next entry
					de++;
					destack.push(de);

					// push our current metadata model
					modelstack.push(md_model);

					// push new state onto of stack to cause a jump
					ifdstack.push(next_ifd);
					destack.push(0);

					// select a new metadata model
					modelstack.push(next_mdmodel);
					
					// delete the tag as it won't be stored nor deleted in the for() loop
					FreeImage_DeleteTag(tag);
					
					break; // break out of the for loop
				}
				else {
					// unsupported camera model, canon maker tag or or something unknown
					// process as a standard tag
					processExifTag(dib, tag, pval, msb_order, md_model);
				}			

			} else {
				// process as a standard tag
				processExifTag(dib, tag, pval, msb_order, md_model);
			}
			
			// delete the tag
			FreeImage_DeleteTag(tag);

        } // for(nde)

		// additional thumbnail data is skipped

    } while (!destack.empty()); 


	return TRUE;
}
Beispiel #3
0
//--------------------------------------------------------------------------
// Process one of the nested EXIF directories.
//--------------------------------------------------------------------------
void CExifParse::ProcessDir(const unsigned char* const DirStart,
                            const unsigned char* const OffsetBase,
                            const unsigned ExifLength,
                            int NestingLevel)
{
  if (NestingLevel > 4)
  {
    ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
    return;
  }

  char IndentString[25];
  memset(IndentString, ' ', 25);
  IndentString[NestingLevel * 4] = '\0';


  int NumDirEntries = Get16((const void*)DirStart, m_MotorolaOrder);

  const unsigned char* const DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
  if (DirEnd+4 > (OffsetBase+ExifLength))
  {
    if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength)
    {
      // Version 1.3 of jhead would truncate a bit too much.
      // This also caught later on as well.
    }
    else
    {
      ErrNonfatal("Illegally sized directory", 0,0);
      return;
    }
  }

  for (int de=0;de<NumDirEntries;de++)
  {
    int Tag, Format, Components;
    unsigned char* ValuePtr;
    int ByteCount;
    const unsigned char* const DirEntry = DIR_ENTRY_ADDR(DirStart, de);

    Tag = Get16(DirEntry, m_MotorolaOrder);
    Format = Get16(DirEntry+2, m_MotorolaOrder);
    Components = Get32(DirEntry+4, m_MotorolaOrder);

    if (Format <= 0 || Format > NUM_FORMATS)
    {
      ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
      continue;
    }

    if ((unsigned)Components > 0x10000)
    {
      ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
      continue;
    }

    ByteCount = Components * BytesPerFormat[Format - 1];

    if (ByteCount > 4)
    {
      unsigned OffsetVal;
      OffsetVal = (unsigned)Get32(DirEntry+8, m_MotorolaOrder);
      // If its bigger than 4 bytes, the dir entry contains an offset.
      if (OffsetVal+ByteCount > ExifLength)
      {
        // Bogus pointer offset and / or bytecount value
        ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
        continue;
      }
      ValuePtr = (unsigned char*)(const_cast<unsigned char*>(OffsetBase)+OffsetVal);

      if (OffsetVal > m_LargestExifOffset)
      {
        m_LargestExifOffset = OffsetVal;
      }

    }
    else {
      // 4 bytes or less and value is in the dir entry itself
      ValuePtr = (unsigned char*)(const_cast<unsigned char*>(DirEntry)+8);
    }


    // Extract useful components of tag
    switch(Tag)
    {
      case TAG_DESCRIPTION:
      {
        int length = max(ByteCount, 0);
        length = min(length, MAX_COMMENT);
        strncpy(m_ExifInfo->Description, (char *)ValuePtr, length);
        m_ExifInfo->Description[length] = '\0';
        break;
      }
      case TAG_MAKE:
      {
        int space = sizeof(m_ExifInfo->CameraMake);
        if (space > 0)
        {
          strncpy(m_ExifInfo->CameraMake, (char *)ValuePtr, space - 1);
          m_ExifInfo->CameraMake[space - 1] = '\0';
        }
        break;
      }
      case TAG_MODEL:
      {
        int space = sizeof(m_ExifInfo->CameraModel);
        if (space > 0)
        {
          strncpy(m_ExifInfo->CameraModel, (char *)ValuePtr, space - 1);
          m_ExifInfo->CameraModel[space - 1] = '\0';
        }
        break;
      }
//      case TAG_SOFTWARE:          strncpy(m_ExifInfo->Software, ValuePtr, 5);    break;
      case TAG_FOCALPLANEXRES:    m_FocalPlaneXRes  = ConvertAnyFormat(ValuePtr, Format);               break;
      case TAG_THUMBNAIL_OFFSET:  m_ExifInfo->ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);     break;
      case TAG_THUMBNAIL_LENGTH:  m_ExifInfo->ThumbnailSize   = (unsigned)ConvertAnyFormat(ValuePtr, Format);     break;

      case TAG_MAKER_NOTE:
        continue;
      break;

      case TAG_DATETIME_ORIGINAL:
      {

        int space = sizeof(m_ExifInfo->DateTime);
        if (space > 0)
        {
          strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
          m_ExifInfo->DateTime[space - 1] = '\0';
          // If we get a DATETIME_ORIGINAL, we use that one.
          m_DateFound = true;
        }
        break;
      }
      case TAG_DATETIME_DIGITIZED:
      case TAG_DATETIME:
      {
        if (!m_DateFound)
        {
          // If we don't already have a DATETIME_ORIGINAL, use whatever
          // time fields we may have.
          int space = sizeof(m_ExifInfo->DateTime);
          if (space > 0)
          {
            strncpy(m_ExifInfo->DateTime, (char *)ValuePtr, space - 1);
            m_ExifInfo->DateTime[space - 1] = '\0';
          }
        }
        break;
      }
      case TAG_USERCOMMENT:
      {
        // The UserComment allows comments without the charset limitations of ImageDescription.
        // Therefore the UserComment field is prefixed by a CharacterCode field (8 Byte):
        //  - ASCII:         'ASCII\0\0\0'
        //  - Unicode:       'UNICODE\0'
        //  - JIS X208-1990: 'JIS\0\0\0\0\0'
        //  - Unknown:       '\0\0\0\0\0\0\0\0' (application specific)

        m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNKNOWN;

        const int EXIF_COMMENT_CHARSET_LENGTH = 8;
        if (ByteCount >= EXIF_COMMENT_CHARSET_LENGTH)
        {
          // As some implementations use spaces instead of \0 for the padding,
          // we're not so strict and check only the prefix.
          if (memcmp(ValuePtr, "ASCII", 5) == 0)
            m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_ASCII;
          else if (memcmp(ValuePtr, "UNICODE", 7) == 0)
            m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
          else if (memcmp(ValuePtr, "JIS", 3) == 0)
            m_ExifInfo->CommentsCharset = EXIF_COMMENT_CHARSET_JIS;

          int length = ByteCount - EXIF_COMMENT_CHARSET_LENGTH;
          length = min(length, MAX_COMMENT);
          memcpy(m_ExifInfo->Comments, ValuePtr + EXIF_COMMENT_CHARSET_LENGTH, length);
          m_ExifInfo->Comments[length] = '\0';
//          FixComment(comment);                          // Ensure comment is printable
        }
      }
      break;

      case TAG_XP_COMMENT:
      {
        // The XP user comment field is always unicode (UCS-2) encoded
        m_ExifInfo->XPCommentsCharset = EXIF_COMMENT_CHARSET_UNICODE;
        size_t length = min(ByteCount, MAX_COMMENT);
        memcpy(m_ExifInfo->XPComment, ValuePtr, length);
        m_ExifInfo->XPComment[length] = '\0';
      }
      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)ConvertAnyFormat(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(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
        }
      break;

      case TAG_FOCALLENGTH:
        // Nice digital cameras actually save the focal length as a function
        // of how far they are zoomed in.
        m_ExifInfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_SUBJECT_DISTANCE:
        // Inidcates the distacne the autofocus camera is focused to.
        // Tends to be less accurate as distance increases.
        {
          float distance = (float)ConvertAnyFormat(ValuePtr, Format);
          m_ExifInfo->Distance = distance;
        }
      break;

      case TAG_EXPOSURETIME:
        {
        // Simplest way of expressing exposure time, so I trust it most.
        // (overwrite previously computd value if there is one)
        float expTime = (float)ConvertAnyFormat(ValuePtr, Format);
        if (expTime)
          m_ExifInfo->ExposureTime = expTime;
        }
      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(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
        }
      break;

      case TAG_FLASH:
        m_ExifInfo->FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_ORIENTATION:
        m_ExifInfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
        if (m_ExifInfo->Orientation < 0 || m_ExifInfo->Orientation > 8)
        {
          ErrNonfatal("Undefined rotation value %d", m_ExifInfo->Orientation, 0);
          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.
        {
          int a = (int)ConvertAnyFormat(ValuePtr, Format);
          if (m_ExifImageWidth < a) m_ExifImageWidth = a;
        }
      break;

      case TAG_FOCALPLANEUNITS:
        switch((int)ConvertAnyFormat(ValuePtr, Format))
        {
          // According to the information I was using, 2 means meters.
          // But looking at the Cannon powershot's files, inches is the only
          // sensible value.
          case 1: m_FocalPlaneUnits = 25.4; break;  // inch
          case 2: m_FocalPlaneUnits = 25.4; break;
          case 3: m_FocalPlaneUnits = 10;   break;  // centimeter
          case 4: m_FocalPlaneUnits = 1;    break;  // millimeter
          case 5: m_FocalPlaneUnits = .001; break;  // micrometer
        }
      break;

      case TAG_EXPOSURE_BIAS:
        m_ExifInfo->ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_WHITEBALANCE:
        m_ExifInfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_LIGHT_SOURCE:
        //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both
        m_ExifInfo->LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_METERING_MODE:
        m_ExifInfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_EXPOSURE_PROGRAM:
        m_ExifInfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_EXPOSURE_INDEX:
        if (m_ExifInfo->ISOequivalent == 0)
        {
          // Exposure index and ISO equivalent are often used interchangeably,
          // so we will do the same.
          // http://photography.about.com/library/glossary/bldef_ei.htm
          m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
        }
      break;

      case TAG_ISO_EQUIVALENT:
        m_ExifInfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
        if (m_ExifInfo->ISOequivalent < 50)
          m_ExifInfo->ISOequivalent *= 200;          // Fixes strange encoding on some older digicams.
      break;

      case TAG_EXPOSURE_MODE:
        m_ExifInfo->ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_DIGITALZOOMRATIO:
        m_ExifInfo->DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
      break;

      case TAG_EXIF_OFFSET:
      case TAG_INTEROP_OFFSET:
      {
        const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
        if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
        {
          ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
        }
        else
        {
          ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
        }
        continue;
      }
      break;

      case TAG_GPSINFO:
      {
        const unsigned char* const SubdirStart = OffsetBase + (unsigned)Get32(ValuePtr, m_MotorolaOrder);
        if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength)
        {
          ErrNonfatal("Illegal GPS directory link",0,0);
        }
        else
        {
          ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
        }
        continue;
      }
      break;

      case TAG_FOCALLENGTH_35MM:
        // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
        // if its present, use it to compute equivalent focal length instead of
        // computing it from sensor geometry and actual focal length.
        m_ExifInfo->FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
      break;
    }
  }


  // 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;

  if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength)
  {
    Offset = (unsigned)Get32(DirStart+2+12*NumDirEntries, m_MotorolaOrder);
    if (Offset)
    {
      const unsigned char* const SubdirStart = OffsetBase + Offset;
      if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase)
      {
        if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20)
        {
          // Jhead 1.3 or earlier would crop the whole directory!
          // As Jhead produces this form of format incorrectness,
          // I'll just let it pass silently
        }
        else
        {
          ErrNonfatal("Illegal subdirectory link",0,0);
        }
      }
      else
      {
        if (SubdirStart <= OffsetBase+ExifLength)
        {
          ProcessDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
        }
      }
      if (Offset > m_LargestExifOffset)
      {
        m_LargestExifOffset = Offset;
      }
    }
  }
  else
  {
    // The exif header ends before the last next directory pointer.
  }

  if (m_ExifInfo->ThumbnailOffset)
  {
    m_ExifInfo->ThumbnailAtEnd = false;

    if (m_ExifInfo->ThumbnailOffset <= ExifLength)
    {
      if (m_ExifInfo->ThumbnailSize > ExifLength - m_ExifInfo->ThumbnailOffset)
      {
        // If thumbnail extends past exif header, only save the part that
        // actually exists.  Canon's EOS viewer utility will do this - the
        // thumbnail extracts ok with this hack.
        m_ExifInfo->ThumbnailSize = ExifLength - m_ExifInfo->ThumbnailOffset;
      }
    }
  }
}
//--------------------------------------------------------------------------
// Process GPS info directory
//--------------------------------------------------------------------------
void ProcessGpsInfo(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength)
{
    int de;
    unsigned a;
    int NumDirEntries;

    NumDirEntries = Get16u(DirStart);
    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))

    if (ShowTags){
        xprintf("(dir has %d entries)\n",NumDirEntries);
    }

    ImageInfo.GpsInfoPresent = TRUE;
    strcpy(ImageInfo.GpsLat, "? ?");
    strcpy(ImageInfo.GpsLong, "? ?");
    ImageInfo.GpsAlt[0] = 0; 

    for (de=0;de<NumDirEntries;de++){
        unsigned Tag, Format, Components;
        unsigned char * ValuePtr;
        int ComponentSize;
        unsigned ByteCount;
        unsigned char * DirEntry;
        DirEntry = DIR_ENTRY_ADDR(DirStart, de);

        if (DirEntry+12 > OffsetBase+ExifLength){
            ErrNonfatal("GPS info directory goes past end of exif",0,0);
            return;
        }

        Tag = Get16u(DirEntry);
        Format = Get16u(DirEntry+2);
        Components = Get32u(DirEntry+4);

        if ((Format-1) >= NUM_FORMATS) {
            // (-1) catches illegal zero case as unsigned underflows to positive large.
            ErrNonfatal("Illegal number format %d for Exif gps tag %04x", Format, Tag);
            continue;
        }

        ComponentSize = BytesPerFormat[Format];
        ByteCount = Components * ComponentSize;

        if (ByteCount > 4){
            unsigned OffsetVal;
            OffsetVal = Get32u(DirEntry+8);
            // If its bigger than 4 bytes, the dir entry contains an offset.
            if (OffsetVal+ByteCount > ExifLength){
                // Bogus pointer offset and / or bytecount value
                ErrNonfatal("Illegal value pointer for Exif gps tag %04x", Tag,0);
                continue;
            }
            ValuePtr = OffsetBase+OffsetVal;
        }else{
            // 4 bytes or less and value is in the dir entry itself
            ValuePtr = DirEntry+8;
        }

        switch(Tag){
            char FmtString[21];
            char TempString[50];
            double Values[3];

            case TAG_GPS_LAT_REF:
                ImageInfo.GpsLat[0] = ValuePtr[0];
                break;

            case TAG_GPS_LONG_REF:
                ImageInfo.GpsLong[0] = ValuePtr[0];
                break;

            case TAG_GPS_LAT:
            case TAG_GPS_LONG:
                if (Format != FMT_URATIONAL){
                    ErrNonfatal("Inappropriate format (%d) for Exif GPS coordinates!", Format, 0);
                }
                strcpy(FmtString, "%0.0fd %0.0fm %0.0fs");
                for (a=0;a<3;a++){
                    int den, digits;

                    den = Get32s(ValuePtr+4+a*ComponentSize);
                    digits = 0;
                    while (den > 1 && digits <= 6){
                        den = den / 10;
                        digits += 1;
                    }
                    if (digits > 6) digits = 6;
                    FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0));
                    FmtString[3+a*7] = (char)('0'+digits);

                    Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
                }

                sprintf(TempString, FmtString, Values[0], Values[1], Values[2]);

                if (Tag == TAG_GPS_LAT){
                    strncpy(ImageInfo.GpsLat+2, TempString, 29);
                }else{
                    strncpy(ImageInfo.GpsLong+2, TempString, 29);
                }
                break;

            case TAG_GPS_ALT_REF:
                ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' ');
                break;

            case TAG_GPS_ALT:
                sprintf(ImageInfo.GpsAlt + 1, "%.2fm", 
                    ConvertAnyFormat(ValuePtr, Format));
                break;
        }

        if (ShowTags){
            // Show tag value.
            if (Tag < MAX_GPS_TAG){
                xprintf("        GPS%s =", GpsTags[Tag]);
            }else{
                // Show unknown tag
                xprintf("        Illegal GPS tag %04x=", Tag);
            }

            switch(Format){
                case FMT_UNDEFINED:
                    // Undefined is typically an ascii string.

                case FMT_STRING:
                    // String arrays printed without function call (different from int arrays)
                    {
                        xprintf("\"");
                        for (a=0;a<ByteCount;a++){
                            int ZeroSkipped = 0;
                            if (ValuePtr[a] >= 32){
                                if (ZeroSkipped){
                                    xprintf("?");
                                    ZeroSkipped = 0;
                                }
                                putchar(ValuePtr[a]);
                            }else{
                                if (ValuePtr[a] == 0){
                                    ZeroSkipped = 1;
                                }
                            }
                        }
                        xprintf("\"\n");
                    }
                    break;

                default:
                    // Handle arrays of numbers later (will there ever be?)
                    for (a=0;;){
                        PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount);
                        if (++a >= Components) break;
                        xprintf(", ");
                    }
                    xprintf("\n");
            }
        }
    }
}
//--------------------------------------------------------------------------
// Process exif format directory, as used by Cannon maker note
//--------------------------------------------------------------------------
void ProcessCanonMakerNoteDir(unsigned char * DirStart, unsigned char * OffsetBase, 
        unsigned ExifLength)
{
    int de;
    int a;
    int NumDirEntries;

    NumDirEntries = Get16u(DirStart);
    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))

    {
        unsigned char * DirEnd;
        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
        if (DirEnd > (OffsetBase+ExifLength)){
            ErrNonfatal("Illegally sized directory",0,0);
            return;
        }

        if (DumpExifMap){
            printf("Map: %05td-%05td: Directory (makernote)\n",DirStart-OffsetBase, DirEnd-OffsetBase);
        }
    }

    if (ShowTags){
        printf("(dir has %d entries)\n",NumDirEntries);
    }

    for (de=0;de<NumDirEntries;de++){
        int Tag, Format, Components;
        unsigned char * ValuePtr;
        int ByteCount;
        unsigned char * DirEntry;
        DirEntry = DIR_ENTRY_ADDR(DirStart, de);

        Tag = Get16u(DirEntry);
        Format = Get16u(DirEntry+2);
        Components = Get32u(DirEntry+4);

        if ((Format-1) >= NUM_FORMATS) {
            // (-1) catches illegal zero case as unsigned underflows to positive large.
            ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
            continue;
        }

        if ((unsigned)Components > 0x10000){
            ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
            continue;
        }

        ByteCount = Components * BytesPerFormat[Format];

        if (ByteCount > 4){
            unsigned OffsetVal;
            OffsetVal = Get32u(DirEntry+8);
            // If its bigger than 4 bytes, the dir entry contains an offset.
            if (OffsetVal+ByteCount > ExifLength){
                // Bogus pointer offset and / or bytecount value
                ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
                continue;
            }
            ValuePtr = OffsetBase+OffsetVal;

            if (DumpExifMap){
                printf("Map: %05d-%05d:   Data for makernote tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag);
            }
        }else{
            // 4 bytes or less and value is in the dir entry itself
            ValuePtr = DirEntry+8;
        }

        if (ShowTags){
            // Show tag name
            printf("            Canon maker tag %04x Value = ", Tag);
        }

        // Show tag value.
        switch(Format){

            case FMT_UNDEFINED:
                // Undefined is typically an ascii string.

            case FMT_STRING:
                // String arrays printed without function call (different from int arrays)
                if (ShowTags){
                    printf("\"");
                    for (a=0;a<ByteCount;a++){
                        int ZeroSkipped = 0;
                        if (ValuePtr[a] >= 32){
                            if (ZeroSkipped){
                                printf("?");
                                ZeroSkipped = 0;
                            }
                            putchar(ValuePtr[a]);
                        }else{
                            if (ValuePtr[a] == 0){
                                ZeroSkipped = 1;
                            }
                        }
                    }
                    printf("\"\n");
                }
                break;

            default:
                if (ShowTags){
                    PrintFormatNumber(ValuePtr, Format, ByteCount);
                    printf("\n");
                }
        }
        if (Tag == 1 && Components > 16){
            int IsoCode = Get16u(ValuePtr + 16*sizeof(unsigned short));
            if (IsoCode >= 16 && IsoCode <= 24){
                ImageInfo.ISOequivalent = 50 << (IsoCode-16);
            } 
        }

        if (Tag == 4 && Format == FMT_USHORT){
            if (Components > 7){
                int WhiteBalance = Get16u(ValuePtr + 7*sizeof(unsigned short));
                switch(WhiteBalance){
                    // 0=Auto, 6=Custom
                    case 1: ImageInfo.LightSource = 1; break; // Sunny
                    case 2: ImageInfo.LightSource = 1; break; // Cloudy
                    case 3: ImageInfo.LightSource = 3; break; // Thungsten
                    case 4: ImageInfo.LightSource = 2; break; // Fourescent
                    case 5: ImageInfo.LightSource = 4; break; // Flash
                }
            }
            if (Components > 19 && ImageInfo.Distance <= 0) {
                // Inidcates the distance the autofocus camera is focused to.
                // Tends to be less accurate as distance increases.
                int temp_dist = Get16u(ValuePtr + 19*sizeof(unsigned short));
printf("temp dist=%d\n",temp_dist);		
                if (temp_dist != 65535){
                    ImageInfo.Distance = (float)temp_dist/100;
                }else{
                    ImageInfo.Distance = -1 /* infinity */;
                }
            }
        }
    }
}
Beispiel #6
0
/**
	Process Exif directory

	@param dib Input FIBITMAP
	@param tiffp Pointer to the TIFF header
	@param offset 0th IFD offset
	@param length Length of the datafile
	@param msb_order Endianess order of the datafile
	@return
*/
static BOOL
jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order) {
    WORD de, nde;

    std::stack<WORD>			destack;	// directory entries stack
    std::stack<const BYTE*>		ifdstack;	// IFD stack
    std::stack<TagLib::MDMODEL>	modelstack; // metadata model stack

    // Keep a list of already visited IFD to avoid stack overflows
    // when recursive/cyclic directory structures exist.
    // This kind of recursive Exif file was encountered with Kodak images coming from
    // KODAK PROFESSIONAL DCS Photo Desk JPEG Export v3.2 W
    std::map<DWORD, int> visitedIFD;

    /*
    "An Image File Directory (IFD) consists of a 2-byte count of the number of directory
    entries (i.e. the number of fields), followed by a sequence of 12-byte field
    entries, followed by a 4-byte offset of the next IFD (or 0 if none)."
    The "next IFD" (1st IFD) is the thumbnail.
    */
#define DIR_ENTRY_ADDR(_start, _entry) (_start + 2 + (12 * _entry))

    // set the metadata model to Exif

    TagLib::MDMODEL md_model = TagLib::EXIF_MAIN;

    // set the pointer to the first IFD (0th IFD) and follow it were it leads.

    const BYTE *ifd0th = (BYTE*)tiffp + offset;

    const BYTE *ifdp = ifd0th;

    de = 0;

    do {
        // if there is anything on the stack then pop it off
        if(!destack.empty()) {
            ifdp		= ifdstack.top();
            ifdstack.pop();
            de			= destack.top();
            destack.pop();
            md_model	= modelstack.top();
            modelstack.pop();
        }

        // remember that we've visited this directory and entry so that we don't visit it again later
        DWORD visited = (DWORD)( (((size_t)ifdp & 0xFFFF) << 16) | (size_t)de );
        if(visitedIFD.find(visited) != visitedIFD.end()) {
            continue;
        } else {
            visitedIFD[visited] = 1;	// processed
        }

        // determine how many entries there are in the current IFD
        nde = ReadUint16(msb_order, ifdp);

        for(; de < nde; de++) {
            char *pde = NULL;	// pointer to the directory entry
            char *pval = NULL;	// pointer to the tag value

            // create a tag
            FITAG *tag = FreeImage_CreateTag();
            if(!tag) return FALSE;

            // point to the directory entry
            pde = (char*) DIR_ENTRY_ADDR(ifdp, de);

            // get the tag ID
            FreeImage_SetTagID(tag, ReadUint16(msb_order, pde));
            // get the tag type
            WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2);
            if((tag_type - 1) >= EXIF_NUM_FORMATS) {
                // a problem occured : delete the tag (not free'd after)
                FreeImage_DeleteTag(tag);
                // break out of the for loop
                break;
            }
            FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type);

            // get number of components
            FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4));
            // check that tag length (size of the tag value in bytes) will fit in a DWORD
            unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag));
            if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) {
                FreeImage_DeleteTag(tag);
                // jump to next entry
                continue;
            }
            FreeImage_SetTagLength(tag, FreeImage_GetTagCount(tag) * tag_data_width);

            if(FreeImage_GetTagLength(tag) <= 4) {
                // 4 bytes or less and value is in the dir entry itself
                pval = pde + 8;
            } else {
                // if its bigger than 4 bytes, the directory entry contains an offset
                // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data
                DWORD offset_value = ReadUint32(msb_order, pde + 8);
                if(offset_value > length) {
                    // a problem occured : delete the tag (not free'd after)
                    FreeImage_DeleteTag(tag);
                    // jump to next entry
                    continue;
                }
                // now check that length does not exceed the buffer size
                if(FreeImage_GetTagLength(tag) > length - offset_value) {
                    // a problem occured : delete the tag (not free'd after)
                    FreeImage_DeleteTag(tag);
                    // jump to next entry
                    continue;
                }
                pval = (char*)(tiffp + offset_value);
            }

            // check for a IFD offset
            BOOL isIFDOffset = FALSE;
            switch(FreeImage_GetTagID(tag)) {
            case TAG_EXIF_OFFSET:
            case TAG_GPS_OFFSET:
            case TAG_INTEROP_OFFSET:
            case TAG_MAKER_NOTE:
                isIFDOffset = TRUE;
                break;
            }
            if(isIFDOffset)	{
                DWORD sub_offset = 0;
                TagLib::MDMODEL next_mdmodel = md_model;
                const BYTE *next_ifd = ifdp;

                // get offset and metadata model
                if (FreeImage_GetTagID(tag) == TAG_MAKER_NOTE) {
                    processMakerNote(dib, pval, msb_order, &sub_offset, &next_mdmodel);
                    next_ifd = (BYTE*)pval + sub_offset;
                } else {
                    processIFDOffset(tag, pval, msb_order, &sub_offset, &next_mdmodel);
                    next_ifd = (BYTE*)tiffp + sub_offset;
                }

                if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) {
                    // push our current directory state onto the stack
                    ifdstack.push(ifdp);
                    // bump to the next entry
                    de++;
                    destack.push(de);

                    // push our current metadata model
                    modelstack.push(md_model);

                    // push new state onto of stack to cause a jump
                    ifdstack.push(next_ifd);
                    destack.push(0);

                    // select a new metadata model
                    modelstack.push(next_mdmodel);

                    // delete the tag as it won't be stored nor deleted in the for() loop
                    FreeImage_DeleteTag(tag);

                    break; // break out of the for loop
                }
                else {
                    // unsupported camera model, canon maker tag or something unknown
                    // process as a standard tag
                    processExifTag(dib, tag, pval, msb_order, md_model);
                }

            } else {
                // process as a standard tag
                processExifTag(dib, tag, pval, msb_order, md_model);
            }

            // delete the tag
            FreeImage_DeleteTag(tag);

        } // for(nde)

        // additional thumbnail data is skipped

    } while (!destack.empty());

    //
    // --- handle thumbnail data ---
    //

    const WORD entriesCount0th = ReadUint16(msb_order, ifd0th);

    DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th));
    if((next_offset == 0) || (next_offset >= length)) {
        return TRUE; //< no thumbnail
    }

    const BYTE* const ifd1st = (BYTE*)tiffp + next_offset;
    const WORD entriesCount1st = ReadUint16(msb_order, ifd1st);

    unsigned thCompression = 0;
    unsigned thOffset = 0;
    unsigned thSize = 0;

    for(int e = 0; e < entriesCount1st; e++) {

        // point to the directory entry
        const BYTE* base = DIR_ENTRY_ADDR(ifd1st, e);

        // check for buffer overflow
        const size_t remaining = (size_t)base + 12 - (size_t)tiffp;
        if(remaining >= length) {
            // bad IFD1 directory, ignore it
            return FALSE;
        }

        // get the tag ID
        WORD tag = ReadUint16(msb_order, base);
        // get the tag type
        WORD type = ReadUint16(msb_order, base + sizeof(WORD));
        // get number of components
        DWORD count = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD));
        // get the tag value
        DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD));

        switch(tag) {
        case TAG_COMPRESSION:
            // Tiff Compression Tag (should be COMPRESSION_OJPEG (6), but is not always respected)
            thCompression = offset;
            break;
        case TAG_JPEG_INTERCHANGE_FORMAT:
            // Tiff JPEGInterchangeFormat Tag
            thOffset = offset;
            break;
        case TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
            // Tiff JPEGInterchangeFormatLength Tag
            thSize = offset;
            break;
        // ### X and Y Resolution ignored, orientation ignored
        case TAG_X_RESOLUTION:		// XResolution
        case TAG_Y_RESOLUTION:		// YResolution
        case TAG_RESOLUTION_UNIT:	// ResolutionUnit
        case TAG_ORIENTATION:		// Orientation
            break;
        default:
            break;
        }
    }

    if(/*thCompression != 6 ||*/ thOffset == 0 || thSize == 0) {
        return TRUE;
    }

    if(thOffset + thSize > length) {
        return TRUE;
    }

    // load the thumbnail

    const BYTE *thLocation = tiffp + thOffset;

    FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(thLocation), thSize);
    FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
    FreeImage_CloseMemory(hmem);

    // store the thumbnail
    FreeImage_SetThumbnail(dib, thumbnail);
    // then delete it
    FreeImage_Unload(thumbnail);

    return TRUE;
}
Beispiel #7
0
//--------------------------------------------------------------------------
// Process GPS info directory
//--------------------------------------------------------------------------
void ProcessGpsInfo(unsigned char * DirStart, int ByteCountUnused, unsigned char * OffsetBase, unsigned ExifLength)
{
    int de;
    unsigned a;
    int NumDirEntries;

    NumDirEntries = Get16u(DirStart);
    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))

    if (ShowTags){
        printf("(dir has %d entries)\n",NumDirEntries);
    }

    ImageInfo.GpsInfoPresent = TRUE;
    strcpy(ImageInfo.GpsLat, "? ?");
    strcpy(ImageInfo.GpsLong, "? ?");
    ImageInfo.GpsAlt[0] = 0;

    for (de=0;de<NumDirEntries;de++){
        unsigned Tag, Format, Components;
        unsigned char * ValuePtr;
        int ComponentSize;
        unsigned ByteCount;
        unsigned char * DirEntry;
        DirEntry = DIR_ENTRY_ADDR(DirStart, de);

        if (DirEntry+12 > OffsetBase+ExifLength){
            ErrNonfatal("GPS info directory goes past end of exif",0,0);
            return;
        }

        Tag = Get16u(DirEntry);
        Format = Get16u(DirEntry+2);
        Components = Get32u(DirEntry+4);

        if ((Format-1) >= NUM_FORMATS) {
            // (-1) catches illegal zero case as unsigned underflows to positive large.
            ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
            continue;
        }

        ComponentSize = BytesPerFormat[Format];
        ByteCount = Components * ComponentSize;

#ifdef SUPERDEBUG
    printf("GPS tag %x format %s #components %d componentsize %d bytecount %d", Tag, formatStr(Format), Components, ComponentSize,
            ByteCount);
#endif

        if (ByteCount > 4){
            unsigned OffsetVal;
            OffsetVal = Get32u(DirEntry+8);
            // If its bigger than 4 bytes, the dir entry contains an offset.
            if (OffsetVal+ByteCount > ExifLength){
                // Bogus pointer offset and / or bytecount value
                ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
                continue;
            }
            ValuePtr = OffsetBase+OffsetVal;
        }else{
            // 4 bytes or less and value is in the dir entry itself
            ValuePtr = DirEntry+8;
        }

        switch(Tag){
            char FmtString[21];
            char TempString[56];
            double Values[3];

            case TAG_GPS_LAT_REF:
                ImageInfo.GpsLat[0] = ValuePtr[0];
                ImageInfo.GpsLatRef[0] = ValuePtr[0];
                ImageInfo.GpsLatRef[1] = '\0';
                break;

            case TAG_GPS_LONG_REF:
                ImageInfo.GpsLong[0] = ValuePtr[0];
                ImageInfo.GpsLongRef[0] = ValuePtr[0];
                ImageInfo.GpsLongRef[1] = '\0';
                break;

            case TAG_GPS_LAT:
            case TAG_GPS_LONG:
                if (Format != FMT_URATIONAL){
                    ErrNonfatal("Inappropriate format (%d) for GPS coordinates!", Format, 0);
                }
                strcpy(FmtString, "%0.0fd %0.0fm %0.0fs");
                for (a=0;a<3;a++){
                    int den, digits;

                    den = Get32s(ValuePtr+4+a*ComponentSize);
                    digits = 0;
                    while (den > 1 && digits <= 6){
                        den = den / 10;
                        digits += 1;
                    }
                    if (digits > 6) digits = 6;
                    FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0));
                    FmtString[3+a*7] = (char)('0'+digits);

                    Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format);
                }

                sprintf(TempString, FmtString, Values[0], Values[1], Values[2]);

                if (Tag == TAG_GPS_LAT){
                    strncpy(ImageInfo.GpsLat+2, TempString, 29);
                }else{
                    strncpy(ImageInfo.GpsLong+2, TempString, 29);
                }

                sprintf(TempString, "%d/%d,%d/%d,%d/%d",
                    Get32s(ValuePtr), Get32s(4+(char*)ValuePtr),
                    Get32s(8+(char*)ValuePtr), Get32s(12+(char*)ValuePtr),
                    Get32s(16+(char*)ValuePtr), Get32s(20+(char*)ValuePtr));
                if (Tag == TAG_GPS_LAT){
                    strncpy(ImageInfo.GpsLatRaw, TempString, 31);
                }else{
                    strncpy(ImageInfo.GpsLongRaw, TempString, 31);
                }
                break;

            case TAG_GPS_ALT_REF:
                ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' ');
                ImageInfo.GpsAltRef = (char)ValuePtr[0];
                break;

            case TAG_GPS_ALT:
                sprintf(ImageInfo.GpsAlt + 1, "%.2fm", 
                    ConvertAnyFormat(ValuePtr, Format));
                ImageInfo.GpsAltRaw.num = Get32u(ValuePtr);
                ImageInfo.GpsAltRaw.denom = Get32u(4+(char *)ValuePtr);
                break;

            case TAG_GPS_TIMESTAMP:
                snprintf(ImageInfo.GpsTimeStamp,
                    sizeof(ImageInfo.GpsTimeStamp), "%d:%d:%d",
                    (int) ConvertAnyFormat(ValuePtr, Format),
                    (int) ConvertAnyFormat(ValuePtr + 8, Format),
                    (int) ConvertAnyFormat(ValuePtr + 16, Format)
                );
                break;

            case TAG_GPS_DATESTAMP:
                strncpy(ImageInfo.GpsDateStamp, (char*)ValuePtr, sizeof(ImageInfo.GpsDateStamp));
                break;

            case TAG_GPS_PROCESSING_METHOD:
                if (ByteCount > EXIF_ASCII_PREFIX_LEN &&
                    memcmp(ValuePtr, ExifAsciiPrefix, EXIF_ASCII_PREFIX_LEN) == 0) {
                    int length =
                        ByteCount < GPS_PROCESSING_METHOD_LEN + EXIF_ASCII_PREFIX_LEN ?
                        ByteCount - EXIF_ASCII_PREFIX_LEN : GPS_PROCESSING_METHOD_LEN;
                    memcpy(ImageInfo.GpsProcessingMethod,
                        (char*)(ValuePtr + EXIF_ASCII_PREFIX_LEN), length);
                    ImageInfo.GpsProcessingMethod[length] = 0;
                } else {
                    ALOGW("Unsupported encoding for GPSProcessingMethod");
                }
                break;
        }

        if (ShowTags){
            // Show tag value.
            if (Tag < MAX_GPS_TAG){
                printf("        %s =", GpsTags[Tag].Desc);
            }else{
                // Show unknown tag
                printf("        Illegal GPS tag %04x=", Tag);
            }

            switch(Format){
                case FMT_UNDEFINED:
                    // Undefined is typically an ascii string.

                case FMT_STRING:
                    // String arrays printed without function call (different from int arrays)
                    {
                        printf("\"");
                        for (a=0;a<ByteCount;a++){
                            int ZeroSkipped = 0;
                            if (ValuePtr[a] >= 32){
                                if (ZeroSkipped){
                                    printf("?");
                                    ZeroSkipped = 0;
                                }
                                putchar(ValuePtr[a]);
                            }else{
                                if (ValuePtr[a] == 0){
                                    ZeroSkipped = 1;
                                }
                            }
                        }
                        printf("\"\n");
                    }
                    break;

                default:
                    // Handle arrays of numbers later (will there ever be?)
                    for (a=0;;){
                        PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount);
                        if (++a >= Components) break;
                        printf(", ");
                    }
                    printf("\n");
            }
        }
    }
}
Beispiel #8
0
//--------------------------------------------------------------------------
// Process one of the nested EXIF directories.
//--------------------------------------------------------------------------
void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength, unsigned NestingLevel)
{
    int de;
    int a;
    int NumDirEntries;
    unsigned ThumbnailOffset = 0;
    unsigned ThumbnailSize = 0;

    if ( NestingLevel > 4)
        throw FatalError("Maximum directory nesting exceeded (corrupt exif header)");

    NumDirEntries = Get16u(DirStart);
#define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))

    {
        unsigned char * DirEnd;
        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
        if (DirEnd+4 > (OffsetBase+ExifLength)) {
            if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength) {
                // Version 1.3 of jhead would truncate a bit too much.
                // This also caught later on as well.
            } else {
                // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier
                // might trigger this.
                throw FatalError("Illegally sized directory");
            }
        }
        if (DirEnd < LastExifRefd) LastExifRefd = DirEnd;
    }

    for (de=0; de<NumDirEntries; de++) {
        int Tag, Format, Components;
        unsigned char * ValuePtr;
        unsigned ByteCount;
        char * DirEntry;
        DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de);

        Tag = Get16u(DirEntry);
        Format = Get16u(DirEntry+2);
        Components = Get32u(DirEntry+4);

        if ((Format-1) >= NUM_FORMATS) {
            // (-1) catches illegal zero case as unsigned underflows to positive large.
            throw FatalError("Illegal format code in EXIF dir");
        }

        if ((unsigned)Components > 0x10000) {
            throw FatalError("Illegal number of components for tag");
            continue;
        }

        ByteCount = Components * BytesPerFormat[Format];

        if (ByteCount > 4) {
            unsigned OffsetVal;
            OffsetVal = Get32u(DirEntry+8);
            // If its bigger than 4 bytes, the dir entry contains an offset.
            if (OffsetVal+ByteCount > ExifLength) {
                // Bogus pointer offset and / or bytecount value
                //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength);

                throw FatalError("Illegal pointer offset value in EXIF");
            }
            ValuePtr = OffsetBase+OffsetVal;
        } else {
            // 4 bytes or less and value is in the dir entry itself
            ValuePtr = (unsigned char *)DirEntry+8;
        }

        if (LastExifRefd < ValuePtr+ByteCount) {
            // Keep track of last byte in the exif header that was actually referenced.
            // That way, we know where the discardable thumbnail data begins.
            LastExifRefd = ValuePtr+ByteCount;
        }

        // Extract useful components of tag
        switch(Tag) {

        case TAG_MAKE:
            ExifData::CameraMake = QString::fromLatin1((const char*)ValuePtr, 31);
            break;

        case TAG_MODEL:
            ExifData::CameraModel = QString::fromLatin1((const char*)ValuePtr, 39);
            break;

        case TAG_ORIENTATION:
            Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_DATETIME_ORIGINAL:
            DateTime = QString::fromLatin1((const char*)ValuePtr, 19);
            break;

        case TAG_USERCOMMENT:
            // Olympus has this padded with trailing spaces.  Remove these first.
            for (a=ByteCount;;) {
                a--;
                if ((ValuePtr)[a] == ' ') {
                    (ValuePtr)[a] = '\0';
                } else {
                    break;
                }
                if (a == 0) break;
            }

            // Copy the comment
            if (memcmp(ValuePtr, "ASCII",5) == 0) {
                for (a=5; a<10; a++) {
                    int c;
                    c = (ValuePtr)[a];
                    if (c != '\0' && c != ' ') {
                        UserComment = QString::fromLatin1((const char*)(a+ValuePtr), 199);
                        break;
                    }
                }
            } else {
                UserComment = QString::fromLatin1((const char*)ValuePtr, 199);
            }
            break;

        case TAG_FNUMBER:
            // Simplest way of expressing aperture, so I trust it the most.
            // (overwrite previously computd value if there is one)
            ExifData::ApertureFNumber = (float)ConvertAnyFormat(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 (ExifData::ApertureFNumber == 0) {
                ExifData::ApertureFNumber
                    = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)*0.5);
            }
            break;

        case TAG_FOCALLENGTH:
            // Nice digital cameras actually save the focal length as a function
            // of how far they are zoomed in.
            ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_SUBJECT_DISTANCE:
            // Inidcates the distacne the autofocus camera is focused to.
            // Tends to be less accurate as distance increases.
            ExifData::Distance = (float)ConvertAnyFormat(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)
            ExifData::ExposureTime = (float)ConvertAnyFormat(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 (ExifData::ExposureTime == 0) {
                ExifData::ExposureTime
                    = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0)));
            }
            break;

        case TAG_FLASH:
            ExifData::FlashUsed = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_EXIF_IMAGELENGTH:
            ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_EXIF_IMAGEWIDTH:
            ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_FOCALPLANEXRES:
            FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_FOCALPLANEUNITS:
            switch((int)ConvertAnyFormat(ValuePtr, Format)) {
            case 1:
                FocalplaneUnits = 25.4;
                break; // inch
            case 2:
                // According to the information I was using, 2 means meters.
                // But looking at the Cannon powershot's files, inches is the only
                // sensible value.
                FocalplaneUnits = 25.4;
                break;

            case 3:
                FocalplaneUnits = 10;
                break;  // centimeter
            case 4:
                FocalplaneUnits = 1;
                break;  // milimeter
            case 5:
                FocalplaneUnits = .001;
                break;  // micrometer
            }
            break;

        // Remaining cases contributed by: Volker C. Schoech ([email protected])

        case TAG_EXPOSURE_BIAS:
            ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_WHITEBALANCE:
            ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_METERING_MODE:
            ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_EXPOSURE_PROGRAM:
            ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_ISO_EQUIVALENT:
            ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
            if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200;
            break;

        case TAG_COMPRESSION_LEVEL:
            ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_THUMBNAIL_OFFSET:
            ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
            break;

        case TAG_THUMBNAIL_LENGTH:
            ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
            break;

        }

        if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET) {
            unsigned char * SubdirStart;
            SubdirStart = OffsetBase + Get32u(ValuePtr);
            if (SubdirStart <= OffsetBase || SubdirStart >= OffsetBase+ExifLength) {
                throw FatalError("Illegal subdirectory link");
            }
            ProcessExifDir(SubdirStart, OffsetBase, ExifLength, 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 comitee!
        unsigned char * SubdirStart;
        unsigned Offset;

        if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength) {
            Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries));
            // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT.
            // Adding OffsetBase to it produces an overflow, so compare with ExifLength here.
            // See http://bugs.kde.org/show_bug.cgi?id=54542
            if (Offset && Offset < ExifLength) {
                SubdirStart = OffsetBase + Offset;
                if (SubdirStart > OffsetBase+ExifLength) {
                    if (SubdirStart < OffsetBase+ExifLength+20) {
                        // Jhead 1.3 or earlier would crop the whole directory!
                        // As Jhead produces this form of format incorrectness,
                        // I'll just let it pass silently
                        kdDebug(7034) << "Thumbnail removed with Jhead 1.3 or earlier\n";
                    } else {
                        throw FatalError("Illegal subdirectory link 2");
                    }
                } else {
                    if (SubdirStart <= OffsetBase+ExifLength) {
                        ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
                    }
                }
            }
        } else {
            // The exif header ends before the last next directory pointer.
        }
    }

    if (ThumbnailSize && ThumbnailOffset) {
        if (ThumbnailSize + ThumbnailOffset < ExifLength) {
            // The thumbnail pointer appears to be valid.  Store it.
            Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG");
        }
    }
}