public function load(PelDataWindow $d, $offset)
{
$thumb_offset = 0;
$thumb_length = 0;
Pel::debug('Constructing IFD at offset %d from %d bytes...', $offset, $d->getSize());
/* Read the number of entries */
$n = $d->getShort($offset);
Pel::debug('Loading %d entries...', $n);
$offset += 2;
/* Check if we have enough data. */
if ($offset + 12 * $n > $d->getSize()) {
$n = floor(($offset - $d->getSize()) / 12);
Pel::maybeThrow(new PelIfdException('Adjusted to: %d.', $n));
}
for ($i = 0; $i < $n; $i++) {
// TODO: increment window start instead of using offsets.
$tag = $d->getShort($offset + 12 * $i);
Pel::debug('Loading entry with tag 0x%04X: %s (%d of %d)...', $tag, PelTag::getName($this->type, $tag), $i + 1, $n);
switch ($tag) {
case PelTag::EXIF_IFD_POINTER:
case PelTag::GPS_INFO_IFD_POINTER:
case PelTag::INTEROPERABILITY_IFD_POINTER:
$o = $d->getLong($offset + 12 * $i + 8);
Pel::debug('Found sub IFD at offset %d', $o);
/* Map tag to IFD type. */
if ($tag == PelTag::EXIF_IFD_POINTER) {
$type = PelIfd::EXIF;
} elseif ($tag == PelTag::GPS_INFO_IFD_POINTER) {
$type = PelIfd::GPS;
} elseif ($tag == PelTag::INTEROPERABILITY_IFD_POINTER) {
$type = PelIfd::INTEROPERABILITY;
}
$this->sub[$type] = new PelIfd($type);
$this->sub[$type]->load($d, $o);
break;
case PelTag::JPEG_INTERCHANGE_FORMAT:
$thumb_offset = $d->getLong($offset + 12 * $i + 8);
$this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
break;
case PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH:
$thumb_length = $d->getLong($offset + 12 * $i + 8);
$this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
break;
default:
$format = $d->getShort($offset + 12 * $i + 2);
$components = $d->getLong($offset + 12 * $i + 4);
/*
* The data size. If bigger than 4 bytes, the actual data is
* not in the entry but somewhere else, with the offset stored
* in the entry.
*/
$s = PelFormat::getSize($format) * $components;
if ($s > 0) {
$doff = $offset + 12 * $i + 8;
if ($s > 4) {
$doff = $d->getLong($doff);
}
$data = $d->getClone($doff, $s);
} else {
$data = new PelDataWindow();
}
try {
$entry = $this->newEntryFromData($tag, $format, $components, $data);
$this->addEntry($entry);
} catch (PelException $e) {
/*
* Throw the exception when running in strict mode, store
* otherwise.
*/
Pel::maybeThrow($e);
}
/* The format of the thumbnail is stored in this tag. */
// TODO: handle TIFF thumbnail.
// if ($tag == PelTag::COMPRESSION) {
// $this->thumb_format = $data->getShort();
// }
break;
}
}
/* Offset to next IFD */
$o = $d->getLong($offset + 12 * $n);
Pel::debug('Current offset is %d, link at %d points to %d.', $offset, $offset + 12 * $n, $o);
if ($o > 0) {
/* Sanity check: we need 6 bytes */
if ($o > $d->getSize() - 6) {
Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: ' . '%d > %d!', $o, $d->getSize() - 6));
} else {
if ($this->type == PelIfd::IFD1) {
// IFD1 shouldn't link further...
Pel::maybeThrow(new PelIfdException('IFD1 links to another IFD!'));
}
$this->next = new PelIfd(PelIfd::IFD1);
$this->next->load($d, $o);
}
} else {
Pel::debug('Last IFD.');
}
}