/** * Load and parse Exif data. * * This will populate the object with Exif data, contained as a * {@link PelTiff} object. This TIFF object can be accessed with * the {@link getTiff()} method. * * @param PelDataWindow $d */ public function load(PelDataWindow $d) { Pel::debug('Parsing %d bytes of Exif data...', $d->getSize()); /* There must be at least 6 bytes for the Exif header. */ if ($d->getSize() < 6) { throw new PelInvalidDataException('Expected at least 6 bytes of Exif ' . 'data, found just %d bytes.', $d->getSize()); } /* Verify the Exif header */ if ($d->strcmp(0, self::EXIF_HEADER)) { $d->setWindowStart(strlen(self::EXIF_HEADER)); } else { throw new PelInvalidDataException('Exif header not found.'); } /* The rest of the data is TIFF data. */ $this->tiff = new PelTiff(); $this->tiff->load($d); }
/** * Turn this directory into bytes. * * This directory will be turned into a byte string, with the * specified byte order. The offsets will be calculated from the * offset given. * * @param int the offset of the first byte of this directory. * * @param PelByteOrder the byte order that should be used when * turning integers into bytes. This should be one of {@link * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. */ function getBytes($offset, $order) { $bytes = ''; $extra_bytes = ''; Pel::debug('Bytes from IDF will start at offset %d within Exif data', $offset); $n = count($this->entries) + count($this->sub); if ($this->thumb_data != null) { /* We need two extra entries for the thumbnail offset and * length. */ $n += 2; } $bytes .= PelConvert::shortToBytes($n, $order); /* Initialize offset of extra data. This included the bytes * preceding this IFD, the bytes needed for the count of entries, * the entries themselves (and sub entries), the extra data in the * entries, and the IFD link. */ $end = $offset + 2 + 12 * $n + 4; foreach ($this->entries as $tag => $entry) { /* Each entry is 12 bytes long. */ $bytes .= PelConvert::shortToBytes($entry->getTag(), $order); $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order); $bytes .= PelConvert::longToBytes($entry->getComponents(), $order); /* * Size? If bigger than 4 bytes, the actual data is not in * the entry but somewhere else. */ $data = $entry->getBytes($order); $s = strlen($data); if ($s > 4) { Pel::debug('Data size %d too big, storing at offset %d instead.', $s, $end); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $data; $end += $s; } else { Pel::debug('Data size %d fits.', $s); /* Copy data directly, pad with NULL bytes as necessary to * fill out the four bytes available.*/ $bytes .= $data . str_repeat(chr(0), 4 - $s); } } if ($this->thumb_data != null) { Pel::debug('Appending %d bytes of thumbnail data at %d', $this->thumb_data->getSize(), $end); // TODO: make PelEntry a class that can be constructed with // arguments corresponding to the newt four lines. $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(), $order); $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT, $order); $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); $bytes .= PelConvert::longToBytes(1, $order); $bytes .= PelConvert::longToBytes($end, $order); $extra_bytes .= $this->thumb_data->getBytes(); $end += $this->thumb_data->getSize(); } /* Find bytes from sub IFDs. */ $sub_bytes = ''; foreach ($this->sub as $type => $sub) { if ($type == PelIfd::EXIF) { $tag = PelTag::EXIF_IFD_POINTER; } elseif ($type == PelIfd::GPS) { $tag = PelTag::GPS_INFO_IFD_POINTER; } elseif ($type == PelIfd::INTEROPERABILITY) { $tag = PelTag::INTEROPERABILITY_IFD_POINTER; } /* Make an aditional entry with the pointer. */ $bytes .= PelConvert::shortToBytes($tag, $order); /* Next the format, which is always unsigned long. */ $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); /* There is only one component. */ $bytes .= PelConvert::longToBytes(1, $order); $data = $sub->getBytes($end, $order); $s = strlen($data); $sub_bytes .= $data; $bytes .= PelConvert::longToBytes($end, $order); $end += $s; } /* Make link to next IFD, if any*/ if ($this->isLastIFD()) { $link = 0; } else { $link = $end; } Pel::debug('Link to next IFD: %d', $link); $bytes .= PelConvert::longtoBytes($link, $order); $bytes .= $extra_bytes . $sub_bytes; if (!$this->isLastIfd()) { $bytes .= $this->next->getBytes($end, $order); } return $bytes; }
/** * Check if data is valid TIFF data. * * This will read just enough data from the data window to determine * if the data could be a valid TIFF data. This means that the * check is more like a heuristic than a rigorous check. * * @param PelDataWindow $d * the bytes that will be examined. * * @return boolean true if the data looks like valid TIFF data, * false otherwise. * * @see PelJpeg::isValid() */ public static function isValid(PelDataWindow $d) { /* First check that we have enough data. */ if ($d->getSize() < 8) { return false; } /* Byte order */ if ($d->strcmp(0, 'II')) { $d->setByteOrder(PelConvert::LITTLE_ENDIAN); } elseif ($d->strcmp(0, 'MM')) { Pel::debug('Found Motorola byte order'); $d->setByteOrder(PelConvert::BIG_ENDIAN); } else { return false; } /* Verify the TIFF header */ return $d->getShort(2) == self::TIFF_HEADER; }
/** * Load and parse data. * * This will load the comment from the data window passed. */ function load(PelDataWindow $d) { $this->comment = $d->getBytes(); }
/** * Test data to see if it could be a valid JPEG image. * * The function will only look at the first few bytes of the data, * and try to determine if it could be a valid JPEG image based on * those bytes. This means that the check is more like a heuristic * than a rigorous check. * * @param PelDataWindow the bytes that will be checked. * * @return boolean true if the bytes look like the beginning of a * JPEG image, false otherwise. * * @see PelTiff::isValid() */ static function isValid(PelDataWindow $d) { /* JPEG data is stored in big-endian format. */ $d->setByteOrder(PelConvert::BIG_ENDIAN); for ($i = 0; $i < 7; $i++) { if ($d->getByte($i) != 0xff) { break; } } return $d->getByte($i) == PelJpegMarker::SOI; }
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); }