b1d10e5f3d53d9614c7cae9618040ec64c4568f2
[yaffs-website] / vendor / lsolesen / pel / src / PelDataWindow.php
1 <?php
2
3 /**
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.
7  *
8  * Copyright (C) 2004, 2005, 2006, 2007 Martin Geisler.
9  *
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.
14  *
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.
19  *
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
24  */
25 namespace lsolesen\pel;
26
27 /**
28  * The window.
29  *
30  * @package PEL
31  */
32 class PelDataWindow
33 {
34
35     /**
36      * The data held by this window.
37      *
38      * The string can contain any kind of data, including binary data.
39      *
40      * @var string
41      */
42     private $data = '';
43
44     /**
45      * The byte order currently in use.
46      *
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}.
50      *
51      * @var PelByteOrder
52      * @see setByteOrder, getByteOrder
53      */
54     private $order;
55
56     /**
57      * The start of the current window.
58      *
59      * All offsets used for access into the data will count from this
60      * offset, effectively limiting access to a window starting at this
61      * byte.
62      *
63      * @var int
64      * @see setWindowStart
65      */
66     private $start = 0;
67
68     /**
69      * The size of the current window.
70      *
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
73      * variable.
74      *
75      * @var int
76      * @see setWindowSize
77      */
78     private $size = 0;
79
80     /**
81      * Construct a new data window with the data supplied.
82      *
83      * @param mixed $data
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.
88      *
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
94      *            setByteOrder()}.
95      */
96     public function __construct($data = '', $endianess = PelConvert::LITTLE_ENDIAN)
97     {
98         if (is_string($data)) {
99             $this->data = $data;
100         } elseif (is_resource($data) && get_resource_type($data) == 'gd') {
101             /*
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...
105              */
106             ob_start();
107             ImageJpeg($data, null, Pel::getJPEGQuality());
108             $this->data = ob_get_clean();
109         } else {
110             throw new PelInvalidArgumentException('Bad type for $data: %s', gettype($data));
111         }
112
113         $this->order = $endianess;
114         $this->size = strlen($this->data);
115     }
116
117     /**
118      * Get the size of the data window.
119      *
120      * @return integer the number of bytes covered by the window. The
121      *         allowed offsets go from 0 up to this number minus one.
122      *
123      * @see getBytes()
124      */
125     public function getSize()
126     {
127         return $this->size;
128     }
129
130     /**
131      * Change the byte order of the data.
132      *
133      * @param integer $order
134      *            the new byte order. This must be either
135      *            {@link PelConvert::LITTLE_ENDIAN} or {@link
136      *            PelConvert::BIG_ENDIAN}.
137      */
138     public function setByteOrder($order)
139     {
140         $this->order = $order;
141     }
142
143     /**
144      * Get the currently used byte order.
145      *
146      * @return integer this will be either {@link
147      *         PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}.
148      */
149     public function getByteOrder()
150     {
151         return $this->order;
152     }
153
154     /**
155      * Move the start of the window forward.
156      *
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.
161      */
162     public function setWindowStart($start)
163     {
164         if ($start < 0 || $start > $this->size) {
165             throw new PelDataWindowWindowException(
166                 'Window [%d, %d] does ' . 'not fit in window [0, %d]',
167                 $start,
168                 $this->size,
169                 $this->size);
170         }
171         $this->start += $start;
172         $this->size -= $start;
173     }
174
175     /**
176      * Adjust the size of the window.
177      * The size can only be made smaller.
178      *
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.
182      */
183     public function setWindowSize($size)
184     {
185         if ($size < 0) {
186             $size += $this->size;
187         }
188         if ($size < 0 || $size > $this->size) {
189             throw new PelDataWindowWindowException(
190                 'Window [0, %d] ' . 'does not fit in window [0, %d]',
191                 $size,
192                 $this->size);
193         }
194         $this->size = $size;
195     }
196
197     /**
198      * Make a new data window with the same data as the this window.
199      *
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.
204      *
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.
209      *
210      * @return PelDataWindow a new window that operates on the same data
211      *         as this window, but (optionally) with a smaller window size.
212      */
213     public function getClone($start = null, $size = null)
214     {
215         $c = clone $this;
216
217         if (is_int($start)) {
218             $c->setWindowStart($start);
219         }
220         if (is_int($size)) {
221             $c->setWindowSize($size);
222         }
223         return $c;
224     }
225
226     /**
227      * Validate an offset against the current window.
228      *
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.
233      *
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
237      */
238     private function validateOffset($offset)
239     {
240         if ($offset < 0 || $offset >= $this->size) {
241             throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]', $offset, 0, $this->size - 1);
242         }
243     }
244
245     /**
246      * Return some or all bytes visible in the window.
247      *
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.
251      *
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.
257      *
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.
261      *
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
265      */
266     public function getBytes($start = null, $size = null)
267     {
268         if (is_int($start)) {
269             if ($start < 0) {
270                 $start += $this->size;
271             }
272
273             $this->validateOffset($start);
274         } else {
275             $start = 0;
276         }
277
278         if (is_int($size)) {
279             if ($size <= 0) {
280                 $size += $this->size - $start;
281             }
282
283             $this->validateOffset($start + $size);
284         } else {
285             $size = $this->size - $start;
286         }
287
288         return substr($this->data, $this->start + $start, $size);
289     }
290
291     /**
292      * Return an unsigned byte from the data.
293      *
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
299      *            thrown.
300      *
301      * @return integer the unsigned byte found at offset.
302      * @throws PelDataWindowOffsetException
303      */
304     public function getByte($offset = 0)
305     {
306         /*
307          * Validate the offset --- this throws an exception if offset is
308          * out of range.
309          */
310         $this->validateOffset($offset);
311
312         /* Translate the offset into an offset into the data. */
313         $offset += $this->start;
314
315         /* Return an unsigned byte. */
316         return PelConvert::bytesToByte($this->data, $offset);
317     }
318
319     /**
320      * Return a signed byte from the data.
321      *
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
327      *            thrown.
328      *
329      * @return integer the signed byte found at offset.
330      * @throws PelDataWindowOffsetException
331      */
332     public function getSByte($offset = 0)
333     {
334         /*
335          * Validate the offset --- this throws an exception if offset is
336          * out of range.
337          */
338         $this->validateOffset($offset);
339
340         /* Translate the offset into an offset into the data. */
341         $offset += $this->start;
342
343         /* Return a signed byte. */
344         return PelConvert::bytesToSByte($this->data, $offset);
345     }
346
347     /**
348      * Return an unsigned short read from the data.
349      *
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}
355      *            being thrown.
356      *
357      * @return integer the unsigned short found at offset.
358      * @throws PelDataWindowOffsetException
359      */
360     public function getShort($offset = 0)
361     {
362         /*
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.
365          */
366         $this->validateOffset($offset);
367         $this->validateOffset($offset + 1);
368
369         /* Translate the offset into an offset into the data. */
370         $offset += $this->start;
371
372         /* Return an unsigned short. */
373         return PelConvert::bytesToShort($this->data, $offset, $this->order);
374     }
375
376     /**
377      * Return a signed short read from the data.
378      *
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}
384      *            being thrown.
385      *
386      * @return integer the signed short found at offset.
387      * @throws PelDataWindowOffsetException
388      */
389     public function getSShort($offset = 0)
390     {
391         /*
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.
394          */
395         $this->validateOffset($offset);
396         $this->validateOffset($offset + 1);
397
398         /* Translate the offset into an offset into the data. */
399         $offset += $this->start;
400
401         /* Return a signed short. */
402         return PelConvert::bytesToSShort($this->data, $offset, $this->order);
403     }
404
405     /**
406      * Return an unsigned long read from the data.
407      *
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}
413      *            being thrown.
414      *
415      * @return integer the unsigned long found at offset.
416      * @throws PelDataWindowOffsetException
417      */
418     public function getLong($offset = 0)
419     {
420         /*
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.
423          */
424         $this->validateOffset($offset);
425         $this->validateOffset($offset + 3);
426
427         /* Translate the offset into an offset into the data. */
428         $offset += $this->start;
429
430         /* Return an unsigned long. */
431         return PelConvert::bytesToLong($this->data, $offset, $this->order);
432     }
433
434     /**
435      * Return a signed long read from the data.
436      *
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}
442      *            being thrown.
443      *
444      * @return integer the signed long found at offset.
445      * @throws PelDataWindowOffsetException
446      */
447     public function getSLong($offset = 0)
448     {
449         /*
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.
452          */
453         $this->validateOffset($offset);
454         $this->validateOffset($offset + 3);
455
456         /* Translate the offset into an offset into the data. */
457         $offset += $this->start;
458
459         /* Return a signed long. */
460         return PelConvert::bytesToSLong($this->data, $offset, $this->order);
461     }
462
463     /**
464      * Return an unsigned rational read from the data.
465      *
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.
472      *
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
477      */
478     public function getRational($offset = 0)
479     {
480         return array(
481             $this->getLong($offset),
482             $this->getLong($offset + 4)
483         );
484     }
485
486     /**
487      * Return a signed rational read from the data.
488      *
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.
495      *
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
500      */
501     public function getSRational($offset = 0)
502     {
503         return array(
504             $this->getSLong($offset),
505             $this->getSLong($offset + 4)
506         );
507     }
508
509     /**
510      * String comparison on substrings.
511      *
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.
518      *
519      * @param string $str
520      *            the string to compare with.
521      *
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
526      */
527     public function strcmp($offset, $str)
528     {
529         /*
530          * Validate the offset of the final character we might have to
531          * check.
532          */
533         $s = strlen($str);
534         $this->validateOffset($offset);
535         $this->validateOffset($offset + $s - 1);
536
537         /* Translate the offset into an offset into the data. */
538         $offset += $this->start;
539
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}) {
543                 return false;
544             }
545         }
546
547         /* All characters matches each other, return true. */
548         return true;
549     }
550
551     /**
552      * Return a string representation of the data window.
553      *
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.
557      */
558     public function __toString()
559     {
560         return Pel::fmt(
561             'DataWindow: %d bytes in [%d, %d] of %d bytes',
562             $this->size,
563             $this->start,
564             $this->start + $this->size,
565             strlen($this->data));
566     }
567 }