525 lines
16 KiB
PHP
525 lines
16 KiB
PHP
|
<?php
|
||
|
|
||
|
/* PEL: PHP Exif Library. A library with support for reading and
|
||
|
* writing all Exif headers in JPEG and TIFF images using PHP.
|
||
|
*
|
||
|
* Copyright (C) 2004, 2005 Martin Geisler.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program in the file COPYING; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||
|
* Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
/* $Id: PelDataWindow.php 387 2005-10-05 11:02:52Z mgeisler $ */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* A container for bytes with a limited window of accessible bytes.
|
||
|
*
|
||
|
* @author Martin Geisler <mgeisler@users.sourceforge.net>
|
||
|
* @version $Revision: 387 $
|
||
|
* @date $Date: 2005-10-05 13:02:52 +0200 (Wed, 05 Oct 2005) $
|
||
|
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL)
|
||
|
* @package PEL
|
||
|
*/
|
||
|
|
||
|
/**#@+ Required class definitions. */
|
||
|
require_once('PelException.php');
|
||
|
require_once('PelConvert.php');
|
||
|
/**#@-*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* An exception thrown when an invalid offset is encountered.
|
||
|
*
|
||
|
* @package PEL
|
||
|
* @subpackage Exception
|
||
|
*/
|
||
|
class PelDataWindowOffsetException extends PelException {}
|
||
|
|
||
|
/**
|
||
|
* An exception thrown when an invalid window is encountered.
|
||
|
*
|
||
|
* @package PEL
|
||
|
* @subpackage Exception
|
||
|
*/
|
||
|
class PelDataWindowWindowException extends PelException {}
|
||
|
|
||
|
/**
|
||
|
* The window.
|
||
|
*
|
||
|
* @package PEL
|
||
|
*/
|
||
|
class PelDataWindow {
|
||
|
|
||
|
/**
|
||
|
* The data held by this window.
|
||
|
*
|
||
|
* The string can contain any kind of data, including binary data.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
private $data = '';
|
||
|
|
||
|
/**
|
||
|
* The byte order currently in use.
|
||
|
*
|
||
|
* This will be the byte order used when data is read using the for
|
||
|
* example the {@link getShort} function. It must be one of {@link
|
||
|
* PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
|
||
|
*
|
||
|
* @var PelByteOrder
|
||
|
* @see setByteOrder, getByteOrder
|
||
|
*/
|
||
|
private $order;
|
||
|
|
||
|
/**
|
||
|
* The start of the current window.
|
||
|
*
|
||
|
* All offsets used for access into the data will count from this
|
||
|
* offset, effectively limiting access to a window starting at this
|
||
|
* byte.
|
||
|
*
|
||
|
* @var int
|
||
|
* @see setWindowStart
|
||
|
*/
|
||
|
private $start = 0;
|
||
|
|
||
|
/**
|
||
|
* The size of the current window.
|
||
|
*
|
||
|
* All offsets used for access into the data will be limited by this
|
||
|
* variable. A valid offset must be strictly less than this
|
||
|
* variable.
|
||
|
*
|
||
|
* @var int
|
||
|
* @see setWindowSize
|
||
|
*/
|
||
|
private $size = 0;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Construct a new data window with the data supplied.
|
||
|
*
|
||
|
* @param string the data that this window will contain. The data
|
||
|
* will be copied into the new data window.
|
||
|
*
|
||
|
* @param boolean the initial byte order of the window. This must
|
||
|
* be either {@link PelConvert::LITTLE_ENDIAN} or {@link
|
||
|
* PelConvert::BIG_ENDIAN}. This will be used when integers are
|
||
|
* read from the data, and it can be changed later with {@link
|
||
|
* setByteOrder()}.
|
||
|
*/
|
||
|
function __construct($d = '', $e = PelConvert::LITTLE_ENDIAN) {
|
||
|
$this->data = $d;
|
||
|
$this->order = $e;
|
||
|
$this->size = strlen($d);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Get the size of the data window.
|
||
|
*
|
||
|
* @return int the number of bytes covered by the window. The
|
||
|
* allowed offsets go from 0 up to this number minus one.
|
||
|
*
|
||
|
* @see getBytes()
|
||
|
*/
|
||
|
function getSize() {
|
||
|
return $this->size;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Change the byte order of the data.
|
||
|
*
|
||
|
* @param PelByteOrder the new byte order. This must be either
|
||
|
* {@link PelConvert::LITTLE_ENDIAN} or {@link
|
||
|
* PelConvert::BIG_ENDIAN}.
|
||
|
*/
|
||
|
function setByteOrder($o) {
|
||
|
$this->order = $o;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Get the currently used byte order.
|
||
|
*
|
||
|
* @return PelByteOrder this will be either {@link
|
||
|
* PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}.
|
||
|
*/
|
||
|
function getByteOrder() {
|
||
|
return $this->order;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Move the start of the window forward.
|
||
|
*
|
||
|
* @param int the new start of the window. All new offsets will be
|
||
|
* calculated from this new start offset, and the size of the window
|
||
|
* will shrink to keep the end of the window in place.
|
||
|
*/
|
||
|
function setWindowStart($start) {
|
||
|
if ($start < 0 || $start > $this->size)
|
||
|
throw new PelDataWindowWindowException('Window [%d, %d] does ' .
|
||
|
'not fit in window [0, %d]',
|
||
|
$start, $this->size, $this->size);
|
||
|
|
||
|
$this->start += $start;
|
||
|
$this->size -= $start;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Adjust the size of the window.
|
||
|
*
|
||
|
* The size can only be made smaller.
|
||
|
*
|
||
|
* @param int the desired size of the window. If the argument is
|
||
|
* negative, the window will be shrunk by the argument.
|
||
|
*/
|
||
|
function setWindowSize($size) {
|
||
|
if ($size < 0)
|
||
|
$size += $this->size;
|
||
|
|
||
|
if ($size < 0 || $size > $this->size)
|
||
|
throw new PelDataWindowWindowException('Window [0, %d] ' .
|
||
|
'does not fit in window [0, %d]',
|
||
|
$size, $this->size);
|
||
|
$this->size = $size;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Make a new data window with the same data as the this window.
|
||
|
*
|
||
|
* @param mixed if an integer is supplied, then it will be the start
|
||
|
* of the window in the clone. If left unspecified, then the clone
|
||
|
* will inherit the start from this object.
|
||
|
*
|
||
|
* @param mixed if an integer is supplied, then it will be the size
|
||
|
* of the window in the clone. If left unspecified, then the clone
|
||
|
* will inherit the size from this object.
|
||
|
*
|
||
|
* @return PelDataWindow a new window that operates on the same data
|
||
|
* as this window, but (optionally) with a smaller window size.
|
||
|
*/
|
||
|
function getClone($start = false, $size = false) {
|
||
|
$c = clone $this;
|
||
|
|
||
|
if (is_int($start))
|
||
|
$c->setWindowStart($start);
|
||
|
|
||
|
if (is_int($size))
|
||
|
$c->setWindowSize($size);
|
||
|
|
||
|
return $c;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Validate an offset against the current window.
|
||
|
*
|
||
|
* @param int the offset to be validated. If the offset is negative
|
||
|
* or if it is greater than or equal to the current window size,
|
||
|
* then a {@link PelDataWindowOffsetException} is thrown.
|
||
|
*
|
||
|
* @return void if the offset is valid nothing is returned, if it is
|
||
|
* invalid a new {@link PelDataWindowOffsetException} is thrown.
|
||
|
*/
|
||
|
private function validateOffset($o) {
|
||
|
if ($o < 0 || $o >= $this->size)
|
||
|
throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]',
|
||
|
$o, 0, $this->size-1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return some or all bytes visible in the window.
|
||
|
*
|
||
|
* This method works just like the standard {@link substr()}
|
||
|
* function in PHP with the exception that it works within the
|
||
|
* window of accessible bytes and does strict range checking.
|
||
|
*
|
||
|
* @param int the offset to the first byte returned. If a negative
|
||
|
* number is given, then the counting will be from the end of the
|
||
|
* window. Invalid offsets will result in a {@link
|
||
|
* PelDataWindowOffsetException} being thrown.
|
||
|
*
|
||
|
* @param int the size of the sub-window. If a negative number is
|
||
|
* given, then that many bytes will be omitted from the result.
|
||
|
*
|
||
|
* @return string a subset of the bytes in the window. This will
|
||
|
* always return no more than {@link getSize()} bytes.
|
||
|
*/
|
||
|
function getBytes($start = false, $size = false) {
|
||
|
if (is_int($start)) {
|
||
|
if ($start < 0)
|
||
|
$start += $this->size;
|
||
|
|
||
|
$this->validateOffset($start);
|
||
|
} else {
|
||
|
$start = 0;
|
||
|
}
|
||
|
|
||
|
if (is_int($size)) {
|
||
|
if ($size <= 0)
|
||
|
$size += $this->size - $start;
|
||
|
|
||
|
$this->validateOffset($start+$size);
|
||
|
} else {
|
||
|
$size = $this->size - $start;
|
||
|
}
|
||
|
|
||
|
return substr($this->data, $this->start + $start, $size);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return an unsigned byte from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first byte in the current allowed window. The last
|
||
|
* valid offset is equal to {@link getSize()}-1. Invalid offsets
|
||
|
* will result in a {@link PelDataWindowOffsetException} being
|
||
|
* thrown.
|
||
|
*
|
||
|
* @return int the unsigned byte found at offset.
|
||
|
*/
|
||
|
function getByte($o = 0) {
|
||
|
/* Validate the offset --- this throws an exception if offset is
|
||
|
* out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return an unsigned byte. */
|
||
|
return PelConvert::bytesToByte($this->data, $o);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return a signed byte from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first byte in the current allowed window. The last
|
||
|
* valid offset is equal to {@link getSize()}-1. Invalid offsets
|
||
|
* will result in a {@link PelDataWindowOffsetException} being
|
||
|
* thrown.
|
||
|
*
|
||
|
* @return int the signed byte found at offset.
|
||
|
*/
|
||
|
function getSByte($o = 0) {
|
||
|
/* Validate the offset --- this throws an exception if offset is
|
||
|
* out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return a signed byte. */
|
||
|
return PelConvert::bytesToSByte($this->data, $o);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return an unsigned short read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first short available in the current allowed window.
|
||
|
* The last valid offset is equal to {@link getSize()}-2. Invalid
|
||
|
* offsets will result in a {@link PelDataWindowOffsetException}
|
||
|
* being thrown.
|
||
|
*
|
||
|
* @return int the unsigned short found at offset.
|
||
|
*/
|
||
|
function getShort($o = 0) {
|
||
|
/* Validate the offset+1 to see if we can safely get two bytes ---
|
||
|
* this throws an exception if offset is out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
$this->validateOffset($o+1);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return an unsigned short. */
|
||
|
return PelConvert::bytesToShort($this->data, $o, $this->order);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return a signed short read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first short available in the current allowed window.
|
||
|
* The last valid offset is equal to {@link getSize()}-2. Invalid
|
||
|
* offsets will result in a {@link PelDataWindowOffsetException}
|
||
|
* being thrown.
|
||
|
*
|
||
|
* @return int the signed short found at offset.
|
||
|
*/
|
||
|
function getSShort($o = 0) {
|
||
|
/* Validate the offset+1 to see if we can safely get two bytes ---
|
||
|
* this throws an exception if offset is out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
$this->validateOffset($o+1);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return a signed short. */
|
||
|
return PelConvert::bytesToSShort($this->data, $o, $this->order);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return an unsigned long read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first long available in the current allowed window.
|
||
|
* The last valid offset is equal to {@link getSize()}-4. Invalid
|
||
|
* offsets will result in a {@link PelDataWindowOffsetException}
|
||
|
* being thrown.
|
||
|
*
|
||
|
* @return int the unsigned long found at offset.
|
||
|
*/
|
||
|
function getLong($o = 0) {
|
||
|
/* Validate the offset+3 to see if we can safely get four bytes
|
||
|
* --- this throws an exception if offset is out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
$this->validateOffset($o+3);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return an unsigned long. */
|
||
|
return PelConvert::bytesToLong($this->data, $o, $this->order);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return a signed long read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first long available in the current allowed window.
|
||
|
* The last valid offset is equal to {@link getSize()}-4. Invalid
|
||
|
* offsets will result in a {@link PelDataWindowOffsetException}
|
||
|
* being thrown.
|
||
|
*
|
||
|
* @return int the signed long found at offset.
|
||
|
*/
|
||
|
function getSLong($o = 0) {
|
||
|
/* Validate the offset+3 to see if we can safely get four bytes
|
||
|
* --- this throws an exception if offset is out of range. */
|
||
|
$this->validateOffset($o);
|
||
|
$this->validateOffset($o+3);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Return a signed long. */
|
||
|
return PelConvert::bytesToSLong($this->data, $o, $this->order);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return an unsigned rational read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first rational available in the current allowed
|
||
|
* window. The last valid offset is equal to {@link getSize()}-8.
|
||
|
* Invalid offsets will result in a {@link
|
||
|
* PelDataWindowOffsetException} being thrown.
|
||
|
*
|
||
|
* @return array the unsigned rational found at offset. A rational
|
||
|
* number is represented as an array of two numbers: the enumerator
|
||
|
* and denominator. Both of these numbers will be unsigned longs.
|
||
|
*/
|
||
|
function getRational($o = 0) {
|
||
|
return array($this->getLong($o), $this->getLong($o+4));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return a signed rational read from the data.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will
|
||
|
* return the first rational available in the current allowed
|
||
|
* window. The last valid offset is equal to {@link getSize()}-8.
|
||
|
* Invalid offsets will result in a {@link
|
||
|
* PelDataWindowOffsetException} being thrown.
|
||
|
*
|
||
|
* @return array the signed rational found at offset. A rational
|
||
|
* number is represented as an array of two numbers: the enumerator
|
||
|
* and denominator. Both of these numbers will be signed longs.
|
||
|
*/
|
||
|
function getSRational($o = 0) {
|
||
|
return array($this->getSLong($o), $this->getSLong($o+4));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* String comparison on substrings.
|
||
|
*
|
||
|
* @param int the offset into the data. An offset of zero will make
|
||
|
* the comparison start with the very first byte available in the
|
||
|
* window. The last valid offset is equal to {@link getSize()}
|
||
|
* minus the length of the string. If the string is too long, then
|
||
|
* a {@link PelDataWindowOffsetException} will be thrown.
|
||
|
*
|
||
|
* @param string the string to compare with.
|
||
|
*
|
||
|
* @return boolean true if the string given matches the data in the
|
||
|
* window, at the specified offset, false otherwise. The comparison
|
||
|
* will stop as soon as a mismatch if found.
|
||
|
*/
|
||
|
function strcmp($o, $str) {
|
||
|
/* Validate the offset of the final character we might have to
|
||
|
* check. */
|
||
|
$s = strlen($str);
|
||
|
$this->validateOffset($o);
|
||
|
$this->validateOffset($o + $s - 1);
|
||
|
|
||
|
/* Translate the offset into an offset into the data. */
|
||
|
$o += $this->start;
|
||
|
|
||
|
/* Check each character, return as soon as the answer is known. */
|
||
|
for ($i = 0; $i < $s; $i++) {
|
||
|
if ($this->data{$o + $i} != $str{$i})
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/* All characters matches each other, return true. */
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return a string representation of the data window.
|
||
|
*
|
||
|
* @return string a description of the window with information about
|
||
|
* the number of bytes accessible, the total number of bytes, and
|
||
|
* the window start and stop.
|
||
|
*/
|
||
|
function __toString() {
|
||
|
return Pel::fmt('DataWindow: %d bytes in [%d, %d] of %d bytes',
|
||
|
$this->size,
|
||
|
$this->start, $this->start + $this->size,
|
||
|
strlen($this->data));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
?>
|