/** * Load TIFF data. * * The data given will be parsed and an internal tree representation * will be built. If the data cannot be parsed correctly, a {@link * PelInvalidDataException} is thrown, explaining the problem. * * @param * d * PelDataWindow the data from which the object will be * constructed. This should be valid TIFF data, coming either * directly from a TIFF image or from the Exif data in a JPEG image. */ public function load(PelDataWindow $d) { Pel::debug('Parsing %d bytes of TIFF data...', $d->getSize()); /* * There must be at least 8 bytes available: 2 bytes for the byte * order, 2 bytes for the TIFF header, and 4 bytes for the offset * to the first IFD. */ if ($d->getSize() < 8) { throw new PelInvalidDataException('Expected at least 8 bytes of TIFF ' . 'data, found just %d bytes.', $d->getSize()); } /* Byte order */ if ($d->strcmp(0, 'II')) { Pel::debug('Found Intel byte order'); $d->setByteOrder(PelConvert::LITTLE_ENDIAN); } elseif ($d->strcmp(0, 'MM')) { Pel::debug('Found Motorola byte order'); $d->setByteOrder(PelConvert::BIG_ENDIAN); } else { throw new PelInvalidDataException('Unknown byte order found in TIFF ' . 'data: 0x%2X%2X', $d->getByte(0), $d->getByte(1)); } /* Verify the TIFF header */ if ($d->getShort(2) != self::TIFF_HEADER) { throw new PelInvalidDataException('Missing TIFF magic value.'); } /* IFD 0 offset */ $offset = $d->getLong(4); Pel::debug('First IFD at offset %d.', $offset); if ($offset > 0) { /* * Parse the first IFD, this will automatically parse the * following IFDs and any sub IFDs. */ $this->ifd = new PelIfd(PelIfd::IFD0); $this->ifd->load($d, $offset); } }
/** * Make a new entry from a bunch of bytes. * * This method will create the proper subclass of {@link PelEntry} * corresponding to the {@link PelTag} and {@link PelFormat} given. * The entry will be initialized with the data given. * * Please note that the data you pass to this method should come * from an image, that is, it should be raw bytes. If instead you * want to create an entry for holding, say, an short integer, then * create a {@link PelEntryShort} object directly and load the data * into it. * * A {@link PelUnexpectedFormatException} is thrown if a mismatch is * discovered between the tag and format, and likewise a {@link * PelWrongComponentCountException} is thrown if the number of * components does not match the requirements of the tag. The * requirements for a given tag (if any) can be found in the * documentation for {@link PelTag}. * * @param PelTag the tag of the entry. * * @param PelFormat the format of the entry. * * @param int the components in the entry. * * @param PelDataWindow the data which will be used to construct the * entry. * * @return PelEntry a newly created entry, holding the data given. */ function newEntryFromData($tag, $format, $components, PelDataWindow $data) { /* First handle tags for which we have a specific PelEntryXXX * class. */ switch ($this->type) { case self::IFD0: case self::IFD1: case self::EXIF: case self::INTEROPERABILITY: switch ($tag) { case PelTag::DATE_TIME: case PelTag::DATE_TIME_ORIGINAL: case PelTag::DATE_TIME_DIGITIZED: if ($format != PelFormat::ASCII) { throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::ASCII); } if ($components != 20) { throw new PelWrongComponentCountException($this->type, $tag, $components, 20); } // TODO: handle timezones. return new PelEntryTime($tag, $data->getBytes(0, -1), PelEntryTime::EXIF_STRING); case PelTag::COPYRIGHT: if ($format != PelFormat::ASCII) { throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::ASCII); } $v = explode("", trim($data->getBytes(), ' ')); return new PelEntryCopyright($v[0], $v[1]); case PelTag::EXIF_VERSION: case PelTag::FLASH_PIX_VERSION: case PelTag::INTEROPERABILITY_VERSION: if ($format != PelFormat::UNDEFINED) { throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::UNDEFINED); } return new PelEntryVersion($tag, $data->getBytes() / 100); case PelTag::USER_COMMENT: if ($format != PelFormat::UNDEFINED) { throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::UNDEFINED); } if ($data->getSize() < 8) { return new PelEntryUserComment(); } else { return new PelEntryUserComment($data->getBytes(8), rtrim($data->getBytes(0, 8))); } case PelTag::XP_TITLE: case PelTag::XP_COMMENT: case PelTag::XP_AUTHOR: case PelTag::XP_KEYWORDS: case PelTag::XP_SUBJECT: if ($format != PelFormat::BYTE) { throw new PelUnexpectedFormatException($this->type, $tag, $format, PelFormat::BYTE); } $v = ''; for ($i = 0; $i < $components; $i++) { $b = $data->getByte($i); /* Convert the byte to a character if it is non-null --- * information about the character encoding of these entries * would be very nice to have! So far my tests have shown * that characters in the Latin-1 character set are stored in * a single byte followed by a NULL byte. */ if ($b != 0) { $v .= chr($b); } } return new PelEntryWindowsString($tag, $v); } case self::GPS: default: /* Then handle the basic formats. */ switch ($format) { case PelFormat::BYTE: $v = new PelEntryByte($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getByte($i)); } return $v; case PelFormat::SBYTE: $v = new PelEntrySByte($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getSByte($i)); } return $v; case PelFormat::ASCII: return new PelEntryAscii($tag, $data->getBytes(0, -1)); case PelFormat::SHORT: $v = new PelEntryShort($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getShort($i * 2)); } return $v; case PelFormat::SSHORT: $v = new PelEntrySShort($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getSShort($i * 2)); } return $v; case PelFormat::LONG: $v = new PelEntryLong($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getLong($i * 4)); } return $v; case PelFormat::SLONG: $v = new PelEntrySLong($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getSLong($i * 4)); } return $v; case PelFormat::RATIONAL: $v = new PelEntryRational($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getRational($i * 8)); } return $v; case PelFormat::SRATIONAL: $v = new PelEntrySRational($tag); for ($i = 0; $i < $components; $i++) { $v->addNumber($data->getSRational($i * 8)); } return $v; case PelFormat::UNDEFINED: return new PelEntryUndefined($tag, $data->getBytes()); default: throw new PelException('Unsupported format: %s', PelFormat::getName($format)); } } }
function testReadBigIntegers() { $window = new PelDataWindow("‰«Íï", PelConvert::BIG_ENDIAN); $this->assertEqual($window->getSize(), 4); $this->assertEqual($window->getBytes(), "‰«Íï"); $this->assertEqual($window->getByte(0), 0x89); $this->assertEqual($window->getByte(1), 0xab); $this->assertEqual($window->getByte(2), 0xcd); $this->assertEqual($window->getByte(3), 0xef); $this->assertEqual($window->getShort(0), 0x89ab); $this->assertEqual($window->getShort(1), 0xabcd); $this->assertEqual($window->getShort(2), 0xcdef); $this->assertEqual($window->getLong(0), 0x89abcdef); $window->setByteOrder(PelConvert::LITTLE_ENDIAN); $this->assertEqual($window->getSize(), 4); $this->assertEqual($window->getBytes(), "‰«Íï"); $this->assertEqual($window->getByte(0), 0x89); $this->assertEqual($window->getByte(1), 0xab); $this->assertEqual($window->getByte(2), 0xcd); $this->assertEqual($window->getByte(3), 0xef); $this->assertEqual($window->getShort(0), 0xab89); $this->assertEqual($window->getShort(1), 0xcdab); $this->assertEqual($window->getShort(2), 0xefcd); $this->assertEqual($window->getLong(0), 0xefcdab89); }