4 * PEL: PHP Exif Library.
5 * A library with support for reading and
6 * writing all Exif headers in JPEG and TIFF images using PHP.
8 * Copyright (C) 2004, 2005, 2006, 2007 Martin Geisler.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program in the file COPYING; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301 USA
25 namespace lsolesen\pel;
36 * The data held by this window.
38 * The string can contain any kind of data, including binary data.
45 * The byte order currently in use.
47 * This will be the byte order used when data is read using the for
48 * example the {@link getShort} function. It must be one of {@link
49 * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
52 * @see setByteOrder, getByteOrder
57 * The start of the current window.
59 * All offsets used for access into the data will count from this
60 * offset, effectively limiting access to a window starting at this
69 * The size of the current window.
71 * All offsets used for access into the data will be limited by this
72 * variable. A valid offset must be strictly less than this
81 * Construct a new data window with the data supplied.
84 * the data that this window will contain. This can
85 * either be given as a string (interpreted litteraly as a sequence
86 * of bytes) or a PHP image resource handle. The data will be copied
87 * into the new data window.
89 * @param boolean $endianess
90 * the initial byte order of the window. This must
91 * be either {@link PelConvert::LITTLE_ENDIAN} or {@link
92 * PelConvert::BIG_ENDIAN}. This will be used when integers are
93 * read from the data, and it can be changed later with {@link
96 public function __construct($data = '', $endianess = PelConvert::LITTLE_ENDIAN)
98 if (is_string($data)) {
100 } elseif (is_resource($data) && get_resource_type($data) == 'gd') {
102 * The ImageJpeg() function insists on printing the bytes
103 * instead of returning them in a more civil way as a string, so
104 * we have to buffer the output...
107 ImageJpeg($data, null, Pel::getJPEGQuality());
108 $this->data = ob_get_clean();
110 throw new PelInvalidArgumentException('Bad type for $data: %s', gettype($data));
113 $this->order = $endianess;
114 $this->size = strlen($this->data);
118 * Get the size of the data window.
120 * @return integer the number of bytes covered by the window. The
121 * allowed offsets go from 0 up to this number minus one.
125 public function getSize()
131 * Change the byte order of the data.
133 * @param integer $order
134 * the new byte order. This must be either
135 * {@link PelConvert::LITTLE_ENDIAN} or {@link
136 * PelConvert::BIG_ENDIAN}.
138 public function setByteOrder($order)
140 $this->order = $order;
144 * Get the currently used byte order.
146 * @return integer this will be either {@link
147 * PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}.
149 public function getByteOrder()
155 * Move the start of the window forward.
157 * @param integer $start
158 * the new start of the window. All new offsets will be
159 * calculated from this new start offset, and the size of the window
160 * will shrink to keep the end of the window in place.
162 public function setWindowStart($start)
164 if ($start < 0 || $start > $this->size) {
165 throw new PelDataWindowWindowException(
166 'Window [%d, %d] does ' . 'not fit in window [0, %d]',
171 $this->start += $start;
172 $this->size -= $start;
176 * Adjust the size of the window.
177 * The size can only be made smaller.
179 * @param integer $size
180 * the desired size of the window. If the argument is
181 * negative, the window will be shrunk by the argument.
183 public function setWindowSize($size)
186 $size += $this->size;
188 if ($size < 0 || $size > $this->size) {
189 throw new PelDataWindowWindowException(
190 'Window [0, %d] ' . 'does not fit in window [0, %d]',
198 * Make a new data window with the same data as the this window.
200 * @param integer|NULL $start
201 * if an integer is supplied, then it will be the start
202 * of the window in the clone. If left unspecified, then the clone
203 * will inherit the start from this object.
205 * @param integer|NULL $size
206 * if an integer is supplied, then it will be the size
207 * of the window in the clone. If left unspecified, then the clone
208 * will inherit the size from this object.
210 * @return PelDataWindow a new window that operates on the same data
211 * as this window, but (optionally) with a smaller window size.
213 public function getClone($start = null, $size = null)
217 if (is_int($start)) {
218 $c->setWindowStart($start);
221 $c->setWindowSize($size);
227 * Validate an offset against the current window.
229 * @param integer $offset
230 * the offset to be validated. If the offset is negative
231 * or if it is greater than or equal to the current window size,
232 * then a {@link PelDataWindowOffsetException} is thrown.
234 * @return void if the offset is valid nothing is returned, if it is
235 * invalid a new {@link PelDataWindowOffsetException} is thrown.
236 * @throws PelDataWindowOffsetException
238 private function validateOffset($offset)
240 if ($offset < 0 || $offset >= $this->size) {
241 throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]', $offset, 0, $this->size - 1);
246 * Return some or all bytes visible in the window.
248 * This method works just like the standard {@link substr()}
249 * function in PHP with the exception that it works within the
250 * window of accessible bytes and does strict range checking.
252 * @param integer|NULL $start
253 * the offset to the first byte returned. If a negative
254 * number is given, then the counting will be from the end of the
255 * window. Invalid offsets will result in a {@link
256 * PelDataWindowOffsetException} being thrown.
258 * @param integer|NUL $size
259 * the size of the sub-window. If a negative number is
260 * given, then that many bytes will be omitted from the result.
262 * @return string a subset of the bytes in the window. This will
263 * always return no more than {@link getSize()} bytes.
264 * @throws PelDataWindowOffsetException
266 public function getBytes($start = null, $size = null)
268 if (is_int($start)) {
270 $start += $this->size;
273 $this->validateOffset($start);
280 $size += $this->size - $start;
283 $this->validateOffset($start + $size);
285 $size = $this->size - $start;
288 return substr($this->data, $this->start + $start, $size);
292 * Return an unsigned byte from the data.
294 * @param integer $offset
295 * the offset into the data. An offset of zero will
296 * return the first byte in the current allowed window. The last
297 * valid offset is equal to {@link getSize()}-1. Invalid offsets
298 * will result in a {@link PelDataWindowOffsetException} being
301 * @return integer the unsigned byte found at offset.
302 * @throws PelDataWindowOffsetException
304 public function getByte($offset = 0)
307 * Validate the offset --- this throws an exception if offset is
310 $this->validateOffset($offset);
312 /* Translate the offset into an offset into the data. */
313 $offset += $this->start;
315 /* Return an unsigned byte. */
316 return PelConvert::bytesToByte($this->data, $offset);
320 * Return a signed byte from the data.
322 * @param integer $offset
323 * the offset into the data. An offset of zero will
324 * return the first byte in the current allowed window. The last
325 * valid offset is equal to {@link getSize()}-1. Invalid offsets
326 * will result in a {@link PelDataWindowOffsetException} being
329 * @return integer the signed byte found at offset.
330 * @throws PelDataWindowOffsetException
332 public function getSByte($offset = 0)
335 * Validate the offset --- this throws an exception if offset is
338 $this->validateOffset($offset);
340 /* Translate the offset into an offset into the data. */
341 $offset += $this->start;
343 /* Return a signed byte. */
344 return PelConvert::bytesToSByte($this->data, $offset);
348 * Return an unsigned short read from the data.
350 * @param integer $offset
351 * the offset into the data. An offset of zero will
352 * return the first short available in the current allowed window.
353 * The last valid offset is equal to {@link getSize()}-2. Invalid
354 * offsets will result in a {@link PelDataWindowOffsetException}
357 * @return integer the unsigned short found at offset.
358 * @throws PelDataWindowOffsetException
360 public function getShort($offset = 0)
363 * Validate the offset+1 to see if we can safely get two bytes ---
364 * this throws an exception if offset is out of range.
366 $this->validateOffset($offset);
367 $this->validateOffset($offset + 1);
369 /* Translate the offset into an offset into the data. */
370 $offset += $this->start;
372 /* Return an unsigned short. */
373 return PelConvert::bytesToShort($this->data, $offset, $this->order);
377 * Return a signed short read from the data.
379 * @param integer $offset
380 * the offset into the data. An offset of zero will
381 * return the first short available in the current allowed window.
382 * The last valid offset is equal to {@link getSize()}-2. Invalid
383 * offsets will result in a {@link PelDataWindowOffsetException}
386 * @return integer the signed short found at offset.
387 * @throws PelDataWindowOffsetException
389 public function getSShort($offset = 0)
392 * Validate the offset+1 to see if we can safely get two bytes ---
393 * this throws an exception if offset is out of range.
395 $this->validateOffset($offset);
396 $this->validateOffset($offset + 1);
398 /* Translate the offset into an offset into the data. */
399 $offset += $this->start;
401 /* Return a signed short. */
402 return PelConvert::bytesToSShort($this->data, $offset, $this->order);
406 * Return an unsigned long read from the data.
408 * @param integer $offset
409 * the offset into the data. An offset of zero will
410 * return the first long available in the current allowed window.
411 * The last valid offset is equal to {@link getSize()}-4. Invalid
412 * offsets will result in a {@link PelDataWindowOffsetException}
415 * @return integer the unsigned long found at offset.
416 * @throws PelDataWindowOffsetException
418 public function getLong($offset = 0)
421 * Validate the offset+3 to see if we can safely get four bytes
422 * --- this throws an exception if offset is out of range.
424 $this->validateOffset($offset);
425 $this->validateOffset($offset + 3);
427 /* Translate the offset into an offset into the data. */
428 $offset += $this->start;
430 /* Return an unsigned long. */
431 return PelConvert::bytesToLong($this->data, $offset, $this->order);
435 * Return a signed long read from the data.
437 * @param integer $offset
438 * the offset into the data. An offset of zero will
439 * return the first long available in the current allowed window.
440 * The last valid offset is equal to {@link getSize()}-4. Invalid
441 * offsets will result in a {@link PelDataWindowOffsetException}
444 * @return integer the signed long found at offset.
445 * @throws PelDataWindowOffsetException
447 public function getSLong($offset = 0)
450 * Validate the offset+3 to see if we can safely get four bytes
451 * --- this throws an exception if offset is out of range.
453 $this->validateOffset($offset);
454 $this->validateOffset($offset + 3);
456 /* Translate the offset into an offset into the data. */
457 $offset += $this->start;
459 /* Return a signed long. */
460 return PelConvert::bytesToSLong($this->data, $offset, $this->order);
464 * Return an unsigned rational read from the data.
466 * @param integer $offset
467 * the offset into the data. An offset of zero will
468 * return the first rational available in the current allowed
469 * window. The last valid offset is equal to {@link getSize()}-8.
470 * Invalid offsets will result in a {@link
471 * PelDataWindowOffsetException} being thrown.
473 * @return array the unsigned rational found at offset. A rational
474 * number is represented as an array of two numbers: the enumerator
475 * and denominator. Both of these numbers will be unsigned longs.
476 * @throws PelDataWindowOffsetException
478 public function getRational($offset = 0)
481 $this->getLong($offset),
482 $this->getLong($offset + 4)
487 * Return a signed rational read from the data.
489 * @param integer $offset
490 * the offset into the data. An offset of zero will
491 * return the first rational available in the current allowed
492 * window. The last valid offset is equal to {@link getSize()}-8.
493 * Invalid offsets will result in a {@link
494 * PelDataWindowOffsetException} being thrown.
496 * @return array the signed rational found at offset. A rational
497 * number is represented as an array of two numbers: the enumerator
498 * and denominator. Both of these numbers will be signed longs.
499 * @throws PelDataWindowOffsetException
501 public function getSRational($offset = 0)
504 $this->getSLong($offset),
505 $this->getSLong($offset + 4)
510 * String comparison on substrings.
512 * @param integer $offset
513 * the offset into the data. An offset of zero will make
514 * the comparison start with the very first byte available in the
515 * window. The last valid offset is equal to {@link getSize()}
516 * minus the length of the string. If the string is too long, then
517 * a {@link PelDataWindowOffsetException} will be thrown.
520 * the string to compare with.
522 * @return boolean true if the string given matches the data in the
523 * window, at the specified offset, false otherwise. The comparison
524 * will stop as soon as a mismatch if found.
525 * @throws PelDataWindowOffsetException
527 public function strcmp($offset, $str)
530 * Validate the offset of the final character we might have to
534 $this->validateOffset($offset);
535 $this->validateOffset($offset + $s - 1);
537 /* Translate the offset into an offset into the data. */
538 $offset += $this->start;
540 /* Check each character, return as soon as the answer is known. */
541 for ($i = 0; $i < $s; $i ++) {
542 if ($this->data{$offset + $i} != $str{$i}) {
547 /* All characters matches each other, return true. */
552 * Return a string representation of the data window.
554 * @return string a description of the window with information about
555 * the number of bytes accessible, the total number of bytes, and
556 * the window start and stop.
558 public function __toString()
561 'DataWindow: %d bytes in [%d, %d] of %d bytes',
564 $this->start + $this->size,
565 strlen($this->data));