bd0602350ff18d4677993afcc0a3cffd2cbc3bd7
[yaffs-website] / lsolesen / pel / src / PelTiff.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 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  * Classes for dealing with TIFF data.
29  *
30  * @author Martin Geisler <mgeisler@users.sourceforge.net>
31  * @license http://www.gnu.org/licenses/gpl.html GNU General Public
32  *          License (GPL)
33  * @package PEL
34  */
35
36 /**
37  * Class for handling TIFF data.
38  *
39  * Exif data is actually an extension of the TIFF file format. TIFF
40  * images consist of a number of {@link PelIfd Image File Directories}
41  * (IFDs), each containing a number of {@link PelEntry entries}. The
42  * IFDs are linked to each other --- one can get hold of the first one
43  * with the {@link getIfd()} method.
44  *
45  * To parse a TIFF image for Exif data one would do:
46  *
47  * <code>
48  * $tiff = new PelTiff($data);
49  * $ifd0 = $tiff->getIfd();
50  * $exif = $ifd0->getSubIfd(PelIfd::EXIF);
51  * $ifd1 = $ifd0->getNextIfd();
52  * </code>
53  *
54  * Should one have some image data of an unknown type, then the {@link
55  * PelTiff::isValid()} function is handy: it will quickly test if the
56  * data could be valid TIFF data. The {@link PelJpeg::isValid()}
57  * function does the same for JPEG images.
58  *
59  * @author Martin Geisler <mgeisler@users.sourceforge.net>
60  * @package PEL
61  */
62 class PelTiff
63 {
64
65     /**
66      * TIFF header.
67      *
68      * This must follow after the two bytes indicating the byte order.
69      */
70     const TIFF_HEADER = 0x002A;
71
72     /**
73      * The first Image File Directory, if any.
74      *
75      * If set, then the type of the IFD must be {@link PelIfd::IFD0}.
76      *
77      * @var PelIfd
78      */
79     private $ifd = null;
80
81     /**
82      * Construct a new object for holding TIFF data.
83      *
84      * The new object will be empty (with no {@link PelIfd}) unless an
85      * argument is given from which it can initialize itself. This can
86      * either be the filename of a TIFF image or a {@link PelDataWindow}
87      * object.
88      *
89      * Use {@link setIfd()} to explicitly set the IFD.
90      *
91      * @param boolean|string|PelDataWindow $data;
92      */
93     public function __construct($data = false)
94     {
95         if ($data === false) {
96             return;
97         }
98         if (is_string($data)) {
99             Pel::debug('Initializing PelTiff object from %s', $data);
100             $this->loadFile($data);
101         } elseif ($data instanceof PelDataWindow) {
102             Pel::debug('Initializing PelTiff object from PelDataWindow.');
103             $this->load($data);
104         } else {
105             throw new PelInvalidArgumentException('Bad type for $data: %s', gettype($data));
106         }
107     }
108
109     /**
110      * Load TIFF data.
111      *
112      * The data given will be parsed and an internal tree representation
113      * will be built. If the data cannot be parsed correctly, a {@link
114      * PelInvalidDataException} is thrown, explaining the problem.
115      *
116      * @param
117      *            d
118      *            PelDataWindow the data from which the object will be
119      *            constructed. This should be valid TIFF data, coming either
120      *            directly from a TIFF image or from the Exif data in a JPEG image.
121      */
122     public function load(PelDataWindow $d)
123     {
124         Pel::debug('Parsing %d bytes of TIFF data...', $d->getSize());
125
126         /*
127          * There must be at least 8 bytes available: 2 bytes for the byte
128          * order, 2 bytes for the TIFF header, and 4 bytes for the offset
129          * to the first IFD.
130          */
131         if ($d->getSize() < 8) {
132             throw new PelInvalidDataException('Expected at least 8 bytes of TIFF ' . 'data, found just %d bytes.', $d->getSize());
133         }
134         /* Byte order */
135         if ($d->strcmp(0, 'II')) {
136             Pel::debug('Found Intel byte order');
137             $d->setByteOrder(PelConvert::LITTLE_ENDIAN);
138         } elseif ($d->strcmp(0, 'MM')) {
139             Pel::debug('Found Motorola byte order');
140             $d->setByteOrder(PelConvert::BIG_ENDIAN);
141         } else {
142             throw new PelInvalidDataException('Unknown byte order found in TIFF ' . 'data: 0x%2X%2X', $d->getByte(0), $d->getByte(1));
143         }
144
145         /* Verify the TIFF header */
146         if ($d->getShort(2) != self::TIFF_HEADER) {
147             throw new PelInvalidDataException('Missing TIFF magic value.');
148         }
149         /* IFD 0 offset */
150         $offset = $d->getLong(4);
151         Pel::debug('First IFD at offset %d.', $offset);
152
153         if ($offset > 0) {
154             /*
155              * Parse the first IFD, this will automatically parse the
156              * following IFDs and any sub IFDs.
157              */
158             $this->ifd = new PelIfd(PelIfd::IFD0);
159             $this->ifd->load($d, $offset);
160         }
161     }
162
163     /**
164      * Load data from a file into a TIFF object.
165      *
166      * @param string $filename
167      *            the filename. This must be a readable file.
168      */
169     public function loadFile($filename)
170     {
171         $this->load(new PelDataWindow(file_get_contents($filename)));
172     }
173
174     /**
175      * Set the first IFD.
176      *
177      * @param PelIfd $ifd
178      *            the new first IFD, which must be of type {@link
179      *            PelIfd::IFD0}.
180      */
181     public function setIfd(PelIfd $ifd)
182     {
183         if ($ifd->getType() != PelIfd::IFD0) {
184             throw new PelInvalidDataException('Invalid type of IFD: %d, expected %d.', $ifd->getType(), PelIfd::IFD0);
185         }
186         $this->ifd = $ifd;
187     }
188
189     /**
190      * Return the first IFD.
191      *
192      * @return PelIfd the first IFD contained in the TIFF data, if any.
193      *         If there is no IFD null will be returned.
194      */
195     public function getIfd()
196     {
197         return $this->ifd;
198     }
199
200     /**
201      * Turn this object into bytes.
202      *
203      * TIFF images can have {@link PelConvert::LITTLE_ENDIAN
204      * little-endian} or {@link PelConvert::BIG_ENDIAN big-endian} byte
205      * order, and so this method takes an argument specifying that.
206      *
207      * @param PelByteOrder $order
208      *            the desired byte order of the TIFF data.
209      *            This should be one of {@link PelConvert::LITTLE_ENDIAN} or {@link
210      *            PelConvert::BIG_ENDIAN}.
211      *
212      * @return string the bytes representing this object.
213      */
214     public function getBytes($order = PelConvert::LITTLE_ENDIAN)
215     {
216         if ($order == PelConvert::LITTLE_ENDIAN) {
217             $bytes = 'II';
218         } else {
219             $bytes = 'MM';
220         }
221
222         /* TIFF magic number --- fixed value. */
223         $bytes .= PelConvert::shortToBytes(self::TIFF_HEADER, $order);
224
225         if ($this->ifd !== null) {
226             /*
227              * IFD 0 offset. We will always start IDF 0 at an offset of 8
228              * bytes (2 bytes for byte order, another 2 bytes for the TIFF
229              * header, and 4 bytes for the IFD 0 offset make 8 bytes
230              * together).
231              */
232             $bytes .= PelConvert::longToBytes(8, $order);
233
234             /*
235              * The argument specifies the offset of this IFD. The IFD will
236              * use this to calculate offsets from the entries to their data,
237              * all those offsets are absolute offsets counted from the
238              * beginning of the data.
239              */
240             $bytes .= $this->ifd->getBytes(8, $order);
241         } else {
242             $bytes .= PelConvert::longToBytes(0, $order);
243         }
244
245         return $bytes;
246     }
247
248     /**
249      * Save the TIFF object as a TIFF image in a file.
250      *
251      * @param
252      *            string the filename to save in. An existing file with the
253      *            same name will be overwritten!
254      *
255      * @return integer|FALSE The number of bytes that were written to the
256      *         file, or FALSE on failure.
257      */
258     public function saveFile($filename)
259     {
260         return file_put_contents($filename, $this->getBytes());
261     }
262
263     /**
264      * Return a string representation of this object.
265      *
266      * @return string a string describing this object. This is mostly useful
267      *         for debugging.
268      */
269     public function __toString()
270     {
271         $str = Pel::fmt("Dumping TIFF data...\n");
272         if ($this->ifd !== null) {
273             $str .= $this->ifd->__toString();
274         }
275
276         return $str;
277     }
278
279     /**
280      * Check if data is valid TIFF data.
281      *
282      * This will read just enough data from the data window to determine
283      * if the data could be a valid TIFF data. This means that the
284      * check is more like a heuristic than a rigorous check.
285      *
286      * @param PelDataWindow $d
287      *            the bytes that will be examined.
288      *
289      * @return boolean true if the data looks like valid TIFF data,
290      *         false otherwise.
291      *
292      * @see PelJpeg::isValid()
293      */
294     public static function isValid(PelDataWindow $d)
295     {
296         /* First check that we have enough data. */
297         if ($d->getSize() < 8) {
298             return false;
299         }
300
301         /* Byte order */
302         if ($d->strcmp(0, 'II')) {
303             $d->setByteOrder(PelConvert::LITTLE_ENDIAN);
304         } elseif ($d->strcmp(0, 'MM')) {
305             Pel::debug('Found Motorola byte order');
306             $d->setByteOrder(PelConvert::BIG_ENDIAN);
307         } else {
308             return false;
309         }
310
311         /* Verify the TIFF header */
312         return $d->getShort(2) == self::TIFF_HEADER;
313     }
314 }