예제 #1
0
//
// Main parsing function for an EXIF segment.
//
// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0".
// PARAM: 'len' length of buffer
//
int easyexif::EXIFInfo::parseFromEXIFSegment(const unsigned char *buf,
                                             unsigned len) {
  bool alignIntel = true;  // byte alignment (defined in EXIF header)
  unsigned offs = 0;       // current offset into buffer
  if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF;

  if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF;
  offs += 6;

  // Now parsing the TIFF header. The first two bytes are either "II" or
  // "MM" for Intel or Motorola byte alignment. Sanity check by parsing
  // the unsigned short that follows, making sure it equals 0x2a. The
  // last 4 bytes are an offset into the first IFD, which are added to
  // the global offset counter. For this block, we expect the following
  // minimum size:
  //  2 bytes: 'II' or 'MM'
  //  2 bytes: 0x002a
  //  4 bytes: offset to first IDF
  // -----------------------------
  //  8 bytes
  if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT;
  unsigned tiff_header_start = offs;
  if (buf[offs] == 'I' && buf[offs + 1] == 'I')
    alignIntel = true;
  else {
    if (buf[offs] == 'M' && buf[offs + 1] == 'M')
      alignIntel = false;
    else
      return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
  }
  this->ByteAlign = alignIntel;
  offs += 2;
  if (0x2a != parse_value<uint16_t>(buf + offs, alignIntel))
    return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned first_ifd_offset = parse_value<uint32_t>(buf + offs, alignIntel);
  offs += first_ifd_offset - 4;
  if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT;

  // Now parsing the first Image File Directory (IFD0, for the main image).
  // An IFD consists of a variable number of 12-byte directory entries. The
  // first two bytes of the IFD section contain the number of directory
  // entries in the section. The last 4 bytes of the IFD contain an offset
  // to the next IFD, which means this IFD must contain exactly 6 + 12 * num
  // bytes of data.
  if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT;
  int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
  if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned exif_sub_ifd_offset = len;
  unsigned gps_sub_ifd_offset = len;
  while (--num_entries >= 0) {
    IFEntry result =
        parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
    offs += 12;
    readTag(result, this);
    switch(result.tag())
    {
      case 0x8825:
        // GPS IFS offset
        gps_sub_ifd_offset = tiff_header_start + result.data();
        break;

      case 0x8769:
        // EXIF SubIFD offset
        exif_sub_ifd_offset = tiff_header_start + result.data();
        break;
    }
  }

  // Jump to the EXIF SubIFD if it exists and parse all the information
  // there. Note that it's possible that the EXIF SubIFD doesn't exist.
  // The EXIF SubIFD contains most of the interesting information that a
  // typical user might want.
  if (exif_sub_ifd_offset + 4 <= len) {
    offs = exif_sub_ifd_offset;
    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_entries >= 0) {
      IFEntry result =
          parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
      readTag(result, this);
      offs += 12;
    }
  }

  // Jump to the GPS SubIFD if it exists and parse all the information
  // there. Note that it's possible that the GPS SubIFD doesn't exist.
  if (gps_sub_ifd_offset + 4 <= len) {
    offs = gps_sub_ifd_offset;
    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_entries >= 0) {
      unsigned short tag, format;
      unsigned length, data;
      parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);
      switch (tag) {
        case 1:
          // GPS north or south
          this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
          if (this->GeoLocation.LatComponents.direction == 0) {
            this->GeoLocation.LatComponents.direction = '?';
          }
          if ('S' == this->GeoLocation.LatComponents.direction) {
            this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
          }
          break;

        case 2:
          // GPS latitude
          if (format == 5 && length == 3) {
            this->GeoLocation.LatComponents.degrees = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LatComponents.minutes = parse_value<Rational>(
                buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LatComponents.seconds = parse_value<Rational>(
                buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Latitude =
                this->GeoLocation.LatComponents.degrees +
                this->GeoLocation.LatComponents.minutes / 60 +
                this->GeoLocation.LatComponents.seconds / 3600;
            if ('S' == this->GeoLocation.LatComponents.direction) {
              this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
            }
          }
          break;

        case 3:
          // GPS east or west
          this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
          if (this->GeoLocation.LonComponents.direction == 0) {
            this->GeoLocation.LonComponents.direction = '?';
          }
          if ('W' == this->GeoLocation.LonComponents.direction) {
            this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          }
          break;

        case 4:
          // GPS longitude
          if (format == 5 && length == 3) {
            this->GeoLocation.LonComponents.degrees = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LonComponents.minutes = parse_value<Rational>(
                buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LonComponents.seconds = parse_value<Rational>(
                buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Longitude =
                this->GeoLocation.LonComponents.degrees +
                this->GeoLocation.LonComponents.minutes / 60 +
                this->GeoLocation.LonComponents.seconds / 3600;
            if ('W' == this->GeoLocation.LonComponents.direction)
              this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          }
          break;

        case 5:
          // GPS altitude reference (below or above sea level)
          this->GeoLocation.AltitudeRef = *(buf + offs + 8);
          if (1 == this->GeoLocation.AltitudeRef) {
            this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
          }
          break;

        case 6:
          // GPS altitude
          if (format == 5) {
            this->GeoLocation.Altitude = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            if (1 == this->GeoLocation.AltitudeRef) {
              this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
            }
          }
          break;

        case 11:
          // GPS degree of precision (DOP)
          if (format == 5) {
            this->GeoLocation.DOP = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
          }
          break;
      }
      offs += 12;
    }
  }

  return PARSE_EXIF_SUCCESS;
}
예제 #2
0
파일: exif.cpp 프로젝트: Ironholds/exif
//
// Main parsing function for an EXIF segment.
//
// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0".
// PARAM: 'len' length of buffer
//
int easyexif::EXIFInfo::parseFromEXIFSegment(const unsigned char *buf, unsigned len) {
  bool alignIntel = true;     // byte alignment (defined in EXIF header)
  unsigned offs   = 0;        // current offset into buffer
  if (!buf || len < 6)
    return PARSE_EXIF_ERROR_NO_EXIF;

  if (!std::equal(buf, buf+6, "Exif\0\0"))
    return PARSE_EXIF_ERROR_NO_EXIF;
  offs += 6;
  
  // Now parsing the TIFF header. The first two bytes are either "II" or
  // "MM" for Intel or Motorola byte alignment. Sanity check by parsing
  // the unsigned short that follows, making sure it equals 0x2a. The
  // last 4 bytes are an offset into the first IFD, which are added to 
  // the global offset counter. For this block, we expect the following
  // minimum size:
  //  2 bytes: 'II' or 'MM'
  //  2 bytes: 0x002a
  //  4 bytes: offset to first IDF
  // -----------------------------
  //  8 bytes
  if (offs + 8 > len)
    return PARSE_EXIF_ERROR_CORRUPT;
  unsigned tiff_header_start = offs;
  if (buf[offs] == 'I' && buf[offs+1] == 'I')
    alignIntel = true;
  else {
    if(buf[offs] == 'M' && buf[offs+1] == 'M')
      alignIntel = false;
    else 
      return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
  }
  this->ByteAlign = alignIntel;
  offs += 2;
  if (0x2a != parse_value<uint16_t>(buf+offs, alignIntel))
    return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned first_ifd_offset = parse_value<uint32_t>(buf + offs, alignIntel);
  offs += first_ifd_offset - 4;
  if (offs >= len)
    return PARSE_EXIF_ERROR_CORRUPT;

  // Now parsing the first Image File Directory (IFD0, for the main image).
  // An IFD consists of a variable number of 12-byte directory entries. The
  // first two bytes of the IFD section contain the number of directory
  // entries in the section. The last 4 bytes of the IFD contain an offset
  // to the next IFD, which means this IFD must contain exactly 6 + 12 * num
  // bytes of data.
  if (offs + 2 > len)
    return PARSE_EXIF_ERROR_CORRUPT;
  int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
  if (offs + 6 + 12 * num_entries > len)
    return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned exif_sub_ifd_offset = len;
  unsigned gps_sub_ifd_offset  = len;
  while (--num_entries >= 0) {
    IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
    offs += 12;
    switch(result.tag()) {
      case 0x102:
        // Bits per sample
        if (result.format() == 3)
          this->BitsPerSample = result.val_short().front();
        break;

      case 0x10E:
        // Image description
        if (result.format() == 2)
          this->ImageDescription = result.val_string();
        break;

      case 0x10F:
        // Digicam make
        if (result.format() == 2)
          this->Make = result.val_string();
        break;

      case 0x110:
        // Digicam model
        if (result.format() == 2)
          this->Model = result.val_string();
        break;

      case 0x112:
        // Orientation of image
        if (result.format() == 3)
          this->Orientation = result.val_short().front();
        break;

      case 0x131:
        // Software used for image
        if (result.format() == 2)
          this->Software = result.val_string();
        break;

      case 0x132:
        // EXIF/TIFF date/time of image modification
        if (result.format() == 2)
          this->DateTime = result.val_string();
        break;

      case 0x8298:
        // Copyright information
        if (result.format() == 2)
          this->Copyright = result.val_string();
        break;

      case 0x8825:
        // GPS IFS offset
        gps_sub_ifd_offset = tiff_header_start + result.data();
        break;

      case 0x8769:
        // EXIF SubIFD offset
        exif_sub_ifd_offset = tiff_header_start + result.data();
        break;
    }
  }

  // Jump to the EXIF SubIFD if it exists and parse all the information
  // there. Note that it's possible that the EXIF SubIFD doesn't exist.
  // The EXIF SubIFD contains most of the interesting information that a
  // typical user might want.
  if (exif_sub_ifd_offset + 4 <= len) {
    offs = exif_sub_ifd_offset;
    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_entries > len)
      return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_entries >= 0) {
      IFEntry result = parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
      switch(result.tag()) {
        case 0x829a:
          // Exposure time in seconds
          if (result.format() == 5)
            this->ExposureTime = result.val_rational().front();
          break;

        case 0x829d:
          // FNumber
          if (result.format() == 5)
            this->FNumber = result.val_rational().front();
          break;

        case 0x8827:
          // ISO Speed Rating
          if (result.format() == 3)
            this->ISOSpeedRatings = result.val_short().front();
          break;

        case 0x9003:
          // Original date and time
          if (result.format() == 2)
            this->DateTimeOriginal = result.val_string();
          break;

        case 0x9004:
          // Digitization date and time
          if (result.format() == 2)
            this->DateTimeDigitized = result.val_string();
          break;

        case 0x9201:
          // Shutter speed value
          if (result.format() == 5)
            this->ShutterSpeedValue = result.val_rational().front();
          break;

        case 0x9204:
          // Exposure bias value 
          if (result.format() == 5)
            this->ExposureBiasValue = result.val_rational().front();
          break;

        case 0x9206:
          // Subject distance
          if (result.format() == 5)
            this->SubjectDistance = result.val_rational().front();
          break;

        case 0x9209:
          // Flash used
          if (result.format() == 3)
            this->Flash = result.data() ? 1 : 0;
          break;

        case 0x920a:
          // Focal length
          if (result.format() == 5)
            this->FocalLength = result.val_rational().front();
          break;

        case 0x9207:
          // Metering mode
          if (result.format() == 3)
            this->MeteringMode = result.val_short().front();
          break;

        case 0x9291:
          // Subsecond original time
          if (result.format() == 2)
            this->SubSecTimeOriginal = result.val_string();
          break;

        case 0xa002:
          // EXIF Image width
          if (result.format() == 4)
            this->ImageWidth = result.val_long().front();
          if (result.format() == 3)
            this->ImageWidth = result.val_short().front();
          break;

        case 0xa003:
          // EXIF Image height
          if (result.format() == 4)
            this->ImageHeight = result.val_long().front();
          if (result.format() == 3)
            this->ImageHeight = result.val_short().front();
          break;

        case 0xa405:
          // Focal length in 35mm film
          if (result.format() == 3)
            this->FocalLengthIn35mm = result.val_short().front();
          break;
        case 0xa432:
          if (result.format() == 5) {
            this->LensInfo.FocalLengthMin = result.val_rational()[0];
            this->LensInfo.FocalLengthMax = result.val_rational()[1];
            this->LensInfo.FStopMin = result.val_rational()[2];
            this->LensInfo.FStopMax = result.val_rational()[3];
          }
          break;
        case 0xa433:
          if (result.format() == 2) {
            this->LensInfo.Make = result.val_string();
          }
          break;
        case 0xa434:
          if (result.format() == 2) {
            this->LensInfo.Model = result.val_string();
          }
          break;
      }
      offs += 12;
    }
  }

  // Jump to the GPS SubIFD if it exists and parse all the information
  // there. Note that it's possible that the GPS SubIFD doesn't exist.
  if (gps_sub_ifd_offset + 4 <= len) {
    offs = gps_sub_ifd_offset;
    int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_entries > len)
      return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_entries >= 0) {
      unsigned short tag, format;
      unsigned length, data;
      parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);
      switch(tag) {
        case 1:
          // GPS north or south
          this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
          if ('S' == this->GeoLocation.LatComponents.direction)
            this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
          break;

        case 2:
          // GPS latitude
          if (format == 5 && length == 3) {
            this->GeoLocation.LatComponents.degrees = 
              parse_value<Rational>(buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LatComponents.minutes = 
              parse_value<Rational>(buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LatComponents.seconds = 
              parse_value<Rational>(buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Latitude = 
              this->GeoLocation.LatComponents.degrees +
              this->GeoLocation.LatComponents.minutes / 60 +
              this->GeoLocation.LatComponents.seconds / 3600;
            if ('S' == this->GeoLocation.LatComponents.direction)
              this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
          }
          break;

        case 3:
          // GPS east or west
          this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
          if ('W' == this->GeoLocation.LonComponents.direction)
            this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          break;

        case 4:
          // GPS longitude
          if (format == 5 && length == 3) {
            this->GeoLocation.LonComponents.degrees = 
              parse_value<Rational>(buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LonComponents.minutes = 
              parse_value<Rational>(buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LonComponents.seconds = 
              parse_value<Rational>(buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Longitude = 
              this->GeoLocation.LonComponents.degrees +
              this->GeoLocation.LonComponents.minutes / 60 +
              this->GeoLocation.LonComponents.seconds / 3600;
            if ('W' == this->GeoLocation.LonComponents.direction)
              this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          }
          break;

        case 5:
          // GPS altitude reference (below or above sea level)
          this->GeoLocation.AltitudeRef = *(buf + offs + 8);
          if (1 == this->GeoLocation.AltitudeRef)
            this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
          break;

        case 6:
          // GPS altitude reference
          if (format == 5) {
            this->GeoLocation.Altitude = 
              parse_value<Rational>(buf + data + tiff_header_start, alignIntel);
            if (1 == this->GeoLocation.AltitudeRef)
              this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
          }
          break;
      }
      offs += 12;
    }
  }

  return PARSE_EXIF_SUCCESS;
}
예제 #3
0
void readTag(IFEntry & result, easyexif::EXIFInfo * exif)
{
  switch (result.tag()) {
    case 0x102:
      // Bits per sample
      if (result.format() == 3)
        exif->BitsPerSample = result.val_short().front();
      break;

    case 0x10E:
      // Image description
      if (result.format() == 2) exif->ImageDescription = result.val_string();
      break;

    case 0x10F:
      // Digicam make
      if (result.format() == 2) exif->Make = result.val_string();
      break;

    case 0x110:
      // Digicam model
      if (result.format() == 2) exif->Model = result.val_string();
      break;

    case 0x112:
      // Orientation of image
      if (result.format() == 3)
        exif->Orientation = result.val_short().front();
      break;

    case 0x131:
      // Software used for image
      if (result.format() == 2) exif->Software = result.val_string();
      break;

    case 0x132:
      // EXIF/TIFF date/time of image modification
      if (result.format() == 2) exif->DateTime = result.val_string();
      break;

    case 0x8298:
      // Copyright information
      if (result.format() == 2) exif->Copyright = result.val_string();
      break;

    case 0x829a:
      // Exposure time in seconds
      if (result.format() == 5)
        exif->ExposureTime = result.val_rational().front();
      break;

    case 0x829d:
      // FNumber
      if (result.format() == 5)
        exif->FNumber = result.val_rational().front();
      break;

    case 0x8827:
      // ISO Speed Rating
      if (result.format() == 3)
        exif->ISOSpeedRatings = result.val_short().front();
      break;

    case 0x9003:
      // Original date and time
      if (result.format() == 2)
        exif->DateTimeOriginal = result.val_string();
      break;

    case 0x9004:
      // Digitization date and time
      if (result.format() == 2)
        exif->DateTimeDigitized = result.val_string();
      break;

    case 0x9201:
      // Shutter speed value
      if (result.format() == 5)
        exif->ShutterSpeedValue = result.val_rational().front();
      break;

    case 0x9204:
      // Exposure bias value
      if (result.format() == 5)
        exif->ExposureBiasValue = result.val_rational().front();
      break;

    case 0x9206:
      // Subject distance
      if (result.format() == 5)
        exif->SubjectDistance = result.val_rational().front();
      break;

    case 0x9209:
      // Flash used
      if (result.format() == 3) exif->Flash = result.data() ? 1 : 0;
      break;

    case 0x920a:
      // Focal length
      if (result.format() == 5)
        exif->FocalLength = result.val_rational().front();
      break;

    case 0x9207:
      // Metering mode
      if (result.format() == 3)
        exif->MeteringMode = result.val_short().front();
      break;

    case 0x9291:
      // Subsecond original time
      if (result.format() == 2)
        exif->SubSecTimeOriginal = result.val_string();
      break;

    case 0xa002:
      // EXIF Image width
      if (result.format() == 4)
        exif->ImageWidth = result.val_long().front();
      if (result.format() == 3)
        exif->ImageWidth = result.val_short().front();
      break;

    case 0xa003:
      // EXIF Image height
      if (result.format() == 4)
        exif->ImageHeight = result.val_long().front();
      if (result.format() == 3)
        exif->ImageHeight = result.val_short().front();
      break;

    case 0xa20e:
      // EXIF Focal plane X-resolution
      if (result.format() == 5) {
        exif->LensInfo.FocalPlaneXResolution = result.val_rational()[0];
      }
      break;

    case 0xa20f:
      // EXIF Focal plane Y-resolution
      if (result.format() == 5) {
        exif->LensInfo.FocalPlaneYResolution = result.val_rational()[0];
      }
      break;

    case 0xa405:
      // Focal length in 35mm film
      if (result.format() == 3)
        exif->FocalLengthIn35mm = result.val_short().front();
      break;

    case 0xa420:
      if (result.format() == 2)
        exif->ImageUniqueID = result.val_string();
       break;

    case 0xa432:
      // Focal length and FStop.
      if (result.format() == 5) {
        exif->LensInfo.FocalLengthMin = result.val_rational()[0];
        exif->LensInfo.FocalLengthMax = result.val_rational()[1];
        exif->LensInfo.FStopMin = result.val_rational()[2];
        exif->LensInfo.FStopMax = result.val_rational()[3];
      }
      break;

    case 0xa433:
      // Lens make.
      if (result.format() == 2) {
        exif->LensInfo.Make = result.val_string();
      }
      break;

    case 0xa434:
      // Lens model.
      if (result.format() == 2) {
        exif->LensInfo.Model = result.val_string();
      }
      break;
  }
}