Skip to content

Commit

Permalink
Split GPS parsing into its own function (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
asmaloney authored Dec 26, 2023
1 parent ac45961 commit 5196754
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 113 deletions.
239 changes: 126 additions & 113 deletions exif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -925,158 +925,171 @@ easyexif::ParseError easyexif::EXIFInfo::parseFromEXIFSegment(
// 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;
ParseError err = parseGPSFromEXIFSegment(
buf, len, alignIntel, gps_sub_ifd_offset, tiff_header_start);

int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
if (offs + 6 + 12 * num_sub_entries > len) {
return easyexif::ParseError::DataCorrupt;
if (err != ParseError::None) {
return err;
}
}

offs += 2;
return ParseError::None;
}

while (--num_sub_entries >= 0) {
unsigned short tag = 0;
unsigned short format = 0;
unsigned int length = 0;
unsigned int data = 0;

parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);

switch (tag) {
case 1:
// GPS north or south
if (offs + 8 > len) {
return easyexif::ParseError::DataCorrupt;
}
easyexif::ParseError easyexif::EXIFInfo::parseGPSFromEXIFSegment(
const unsigned char *buf, unsigned int len, bool alignIntel,
unsigned int startingOffset, unsigned int tiffHeaderStart) {
unsigned int offs = startingOffset;

GeoLocation.LatComponents.direction = *(buf + offs + 8);
int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
if (offs + 6 + 12 * num_sub_entries > len) {
return ParseError::DataCorrupt;
}

if (GeoLocation.LatComponents.direction == 0) {
GeoLocation.LatComponents.direction = '?';
}
offs += 2;

if ('S' == GeoLocation.LatComponents.direction) {
GeoLocation.Latitude = -GeoLocation.Latitude;
}
break;
while (--num_sub_entries >= 0) {
unsigned short tag = 0;
unsigned short format = 0;
unsigned int length = 0;
unsigned int data = 0;

case 2:
// GPS latitude
if ((format == UnsignedRational || format == SignedRational) &&
length == 3) {
if (data + tiff_header_start + 16 > len) {
return easyexif::ParseError::DataCorrupt;
}
parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);

GeoLocation.LatComponents.degrees = parse_value<Rational>(
buf + data + tiff_header_start, alignIntel);
switch (tag) {
case 1:
// GPS north or south
if (offs + 8 > len) {
return ParseError::DataCorrupt;
}

GeoLocation.LatComponents.minutes = parse_value<Rational>(
buf + data + tiff_header_start + 8, alignIntel);
GeoLocation.LatComponents.direction = *(buf + offs + 8);

GeoLocation.LatComponents.seconds = parse_value<Rational>(
buf + data + tiff_header_start + 16, alignIntel);
if (GeoLocation.LatComponents.direction == 0) {
GeoLocation.LatComponents.direction = '?';
}

GeoLocation.Latitude = GeoLocation.LatComponents.degrees +
GeoLocation.LatComponents.minutes / 60 +
GeoLocation.LatComponents.seconds / 3600;
if ('S' == GeoLocation.LatComponents.direction) {
GeoLocation.Latitude = -GeoLocation.Latitude;
}
break;

if ('S' == GeoLocation.LatComponents.direction) {
GeoLocation.Latitude = -GeoLocation.Latitude;
}
case 2:
// GPS latitude
if ((format == UnsignedRational || format == SignedRational) &&
length == 3) {
if (data + tiffHeaderStart + 16 > len) {
return ParseError::DataCorrupt;
}
break;

case 3:
// GPS east or west
if (offs + 8 > len) {
return easyexif::ParseError::DataCorrupt;
}
GeoLocation.LatComponents.degrees =
parse_value<Rational>(buf + data + tiffHeaderStart, alignIntel);

GeoLocation.LonComponents.direction = *(buf + offs + 8);
GeoLocation.LatComponents.minutes = parse_value<Rational>(
buf + data + tiffHeaderStart + 8, alignIntel);

if (GeoLocation.LonComponents.direction == 0) {
GeoLocation.LonComponents.direction = '?';
}
GeoLocation.LatComponents.seconds = parse_value<Rational>(
buf + data + tiffHeaderStart + 16, alignIntel);

if ('W' == GeoLocation.LonComponents.direction) {
GeoLocation.Longitude = -GeoLocation.Longitude;
}
break;
GeoLocation.Latitude = GeoLocation.LatComponents.degrees +
GeoLocation.LatComponents.minutes / 60 +
GeoLocation.LatComponents.seconds / 3600;

case 4:
// GPS longitude
if ((format == UnsignedRational || format == SignedRational) &&
length == 3) {
if (data + tiff_header_start + 16 > len) {
return easyexif::ParseError::DataCorrupt;
}
if ('S' == GeoLocation.LatComponents.direction) {
GeoLocation.Latitude = -GeoLocation.Latitude;
}
}
break;

GeoLocation.LonComponents.degrees = parse_value<Rational>(
buf + data + tiff_header_start, alignIntel);
case 3:
// GPS east or west
if (offs + 8 > len) {
return ParseError::DataCorrupt;
}

GeoLocation.LonComponents.minutes = parse_value<Rational>(
buf + data + tiff_header_start + 8, alignIntel);
GeoLocation.LonComponents.direction = *(buf + offs + 8);

GeoLocation.LonComponents.seconds = parse_value<Rational>(
buf + data + tiff_header_start + 16, alignIntel);
if (GeoLocation.LonComponents.direction == 0) {
GeoLocation.LonComponents.direction = '?';
}

GeoLocation.Longitude = GeoLocation.LonComponents.degrees +
GeoLocation.LonComponents.minutes / 60 +
GeoLocation.LonComponents.seconds / 3600;
if ('W' == GeoLocation.LonComponents.direction) {
GeoLocation.Longitude = -GeoLocation.Longitude;
}
break;

if ('W' == GeoLocation.LonComponents.direction)
GeoLocation.Longitude = -GeoLocation.Longitude;
case 4:
// GPS longitude
if ((format == UnsignedRational || format == SignedRational) &&
length == 3) {
if (data + tiffHeaderStart + 16 > len) {
return ParseError::DataCorrupt;
}
break;

case 5:
// GPS altitude reference (below or above sea level)
if (offs + 8 > len) {
return easyexif::ParseError::DataCorrupt;
}
GeoLocation.LonComponents.degrees =
parse_value<Rational>(buf + data + tiffHeaderStart, alignIntel);

GeoLocation.AltitudeRef = *(buf + offs + 8);
GeoLocation.LonComponents.minutes = parse_value<Rational>(
buf + data + tiffHeaderStart + 8, alignIntel);

if (1 == GeoLocation.AltitudeRef) {
GeoLocation.Altitude = -GeoLocation.Altitude;
}
break;
GeoLocation.LonComponents.seconds = parse_value<Rational>(
buf + data + tiffHeaderStart + 16, alignIntel);

case 6:
// GPS altitude
if (format == UnsignedRational || format == SignedRational) {
if (data + tiff_header_start > len) {
return easyexif::ParseError::DataCorrupt;
}
GeoLocation.Longitude = GeoLocation.LonComponents.degrees +
GeoLocation.LonComponents.minutes / 60 +
GeoLocation.LonComponents.seconds / 3600;

GeoLocation.Altitude = parse_value<Rational>(
buf + data + tiff_header_start, alignIntel);
if ('W' == GeoLocation.LonComponents.direction)
GeoLocation.Longitude = -GeoLocation.Longitude;
}
break;

case 5:
// GPS altitude reference (below or above sea level)
if (offs + 8 > len) {
return ParseError::DataCorrupt;
}

GeoLocation.AltitudeRef = *(buf + offs + 8);

if (1 == GeoLocation.AltitudeRef) {
GeoLocation.Altitude = -GeoLocation.Altitude;
}
if (1 == GeoLocation.AltitudeRef) {
GeoLocation.Altitude = -GeoLocation.Altitude;
}
break;

case 6:
// GPS altitude
if (format == UnsignedRational || format == SignedRational) {
if (data + tiffHeaderStart > len) {
return ParseError::DataCorrupt;
}
break;

case 11:
// GPS degree of precision (DOP)
if (format == UnsignedRational || format == SignedRational) {
if (data + tiff_header_start > len) {
return easyexif::ParseError::DataCorrupt;
}
GeoLocation.Altitude =
parse_value<Rational>(buf + data + tiffHeaderStart, alignIntel);

GeoLocation.DOP = parse_value<Rational>(
buf + data + tiff_header_start, alignIntel);
if (1 == GeoLocation.AltitudeRef) {
GeoLocation.Altitude = -GeoLocation.Altitude;
}
break;
}
}
break;

offs += 12;
case 11:
// GPS degree of precision (DOP)
if (format == UnsignedRational || format == SignedRational) {
if (data + tiffHeaderStart > len) {
return ParseError::DataCorrupt;
}

GeoLocation.DOP =
parse_value<Rational>(buf + data + tiffHeaderStart, alignIntel);
}
break;
}

offs += 12;
}

return easyexif::ParseError::None;
return ParseError::None;
}

void easyexif::EXIFInfo::clear() {
Expand Down
6 changes: 6 additions & 0 deletions exif.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ class EXIFInfo {
} LensInfo;

EXIFInfo() { clear(); }

private:
ParseError parseGPSFromEXIFSegment(const unsigned char *buf, unsigned int len,
bool alignIntel,
unsigned int startingOffset,
unsigned int tiffHeaderStart);
};

// Parse was successful
Expand Down

0 comments on commit 5196754

Please sign in to comment.