source: extensions/edit_gmaps/admin/include/EXIF.php @ 9412

Revision 9412, 119.2 KB checked in by cljosse, 9 years ago (diff)
Line 
1<?php
2
3/******************************************************************************
4*
5* Filename:     EXIF.php
6*
7* Description:  Provides functions for reading and writing EXIF Information
8*               to/from an APP1 segment of a JPEG file
9*               Unfortunately, because EXIF data may be distributed anywhere
10*               throughout an image file, rather than just being in one block,
11*               it is impossible to pass just a string containing only the EXIF
12*               information. Hence it is neccessary to be able to seek to
13*               any point in the file. This causes the HTTP and FTP wrappers
14*               not to work - i.e. the EXIF functions will only work with local
15*               files.
16*               To work on an internet file, copy it locally to start with:
17*
18*               $newfilename = tempnam ( $dir, "tmpexif" );
19*               copy ( "http://whatever.com", $newfilename );
20*
21*
22* Author:      Evan Hunter
23*
24* Date:         30/7/2004
25*
26* Project:      PHP JPEG Metadata Toolkit
27*
28* Revision:     1.11
29*
30* Changes:      1.00 -> 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file
31*               1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information
32*                              embedded within the EXIF data
33*                              added checks for http and ftp wrappers, as these are not supported
34*                              changed interpret_IFD to allow thumbnail links to work when
35*                              toolkit is portable across directories
36*
37*
38* URL:          http://electronics.ozhiker.com
39*
40* Copyright:    Copyright " . $auteur . " 2004
41*
42* License:      This file is part of the PHP JPEG Metadata Toolkit.
43*
44*               The PHP JPEG Metadata Toolkit is free software; you can
45*               redistribute it and/or modify it under the terms of the
46*               GNU General Public License as published by the Free Software
47*               Foundation; either version 2 of the License, or (at your
48*               option) any later version.
49*
50*               The PHP JPEG Metadata Toolkit is distributed in the hope
51*               that it will be useful, but WITHOUT ANY WARRANTY; without
52*               even the implied warranty of MERCHANTABILITY or FITNESS
53*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
54*               for more details.
55*
56*               You should have received a copy of the GNU General Public
57*               License along with the PHP JPEG Metadata Toolkit; if not,
58*               write to the Free Software Foundation, Inc., 59 Temple
59*               Place, Suite 330, Boston, MA  02111-1307  USA
60*
61*               If you require a different license for commercial or other
62*               purposes, please contact the author: evan@ozhiker.com
63*
64******************************************************************************/
65
66
67// TODO : Thoroughly test the functions for writing EXIF segments
68// TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
69// TODO : Implement EXIF decoding of Device Setting Description field
70// TODO : Implement EXIF decoding of SpatialFrequencyResponse field
71// TODO : Implement EXIF decoding of OECF field
72// TODO : Implement EXIF decoding of SubjectArea field
73// TODO : Add a put_EXIF_TIFF function
74
75/******************************************************************************
76*
77* Initialisation
78*
79******************************************************************************/
80
81
82if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) )     $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
83if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) )  $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE;
84if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE;
85
86
87include_once INCLUDE_PATH.'EXIF_Tags.php';
88include_once INCLUDE_PATH.'EXIF_Makernote.php';
89include_once INCLUDE_PATH.'PIM.php';
90include_once INCLUDE_PATH.'Unicode.php';
91include_once INCLUDE_PATH.'JPEG.php';
92include_once INCLUDE_PATH.'IPTC.php';
93//include_once INCLUDE_PATH.'Photoshop_IRB.php';       // Change: as of version 1.11  - Required for TIFF with embedded IRB
94include_once INCLUDE_PATH.'XMP.php';                 // Change: as of version 1.11  - Required for TIFF with embedded XMP
95include_once INCLUDE_PATH.'pjmt_utils.php';          // Change: as of version 1.11  - Required for directory portability
96
97
98
99
100
101
102
103
104/******************************************************************************
105*
106* Function:     get_EXIF_JPEG
107*
108* Description:  Retrieves information from a Exchangeable Image File Format (EXIF)
109*               APP1 segment and returns it in an array.
110*
111* Parameters:   filename - the filename of the JPEG image to process
112*
113* Returns:      OutputArray - Array of EXIF records
114*               FALSE - If an error occured in decoding
115*
116******************************************************************************/
117
118function get_EXIF_JPEG( $filename )
119{
120        // Change: Added as of version 1.11
121        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
122        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
123        {
124                // A HTTP or FTP wrapper is being used - show a warning and abort
125                echo "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified<br>";
126                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
127                echo "\$newfilename = tempnam ( \$dir, \"tmpexif\" );<br>\n";
128                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
129                return FALSE;
130        }
131
132        // get the JPEG headers
133        $jpeg_header_data = get_jpeg_header_data( $filename );
134
135
136        // Flag that an EXIF segment has not been found yet
137        $EXIF_Location = -1;
138
139        //Cycle through the header segments
140        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
141        {
142                // If we find an APP1 header,
143                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
144                {
145                        // And if it has the EXIF label,
146                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
147                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) )          // For some reason, some files have a faulty EXIF name which has a 0xFF in it
148                        {
149                                // Save the location of the EXIF segment
150                                $EXIF_Location = $i;
151                        }
152                }
153
154        }
155
156        // Check if an EXIF segment was found
157        if ( $EXIF_Location == -1 )
158        {
159                // Couldn't find any EXIF block to decode
160                return FALSE;
161        }
162
163        $filehnd = @fopen($filename, 'rb');
164
165        // Check if the file opened successfully
166        if ( ! $filehnd  )
167        {
168                // Could't open the file - exit
169                echo "<p>Could not open file $filename</p>\n";
170                return FALSE;
171        }
172
173        fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6  );
174
175        // Decode the Exif segment into an array and return it
176        $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
177
178
179
180        // Close File
181        fclose($filehnd);
182        return $exif_data;
183}
184
185/******************************************************************************
186* End of Function:     get_EXIF_JPEG
187******************************************************************************/
188
189
190
191/******************************************************************************
192*
193* Function:     put_EXIF_JPEG
194*
195* Description:  Stores information into a Exchangeable Image File Format (EXIF)
196*               APP1 segment from an EXIF array.
197*
198*               WARNING: Because the EXIF standard allows pointers to data
199*               outside the APP1 segment, if there are any such pointers in
200*               a makernote, this function will DAMAGE them since it will not
201*               be aware that there is an external pointer. This will often
202*               happen with Makernotes that include an embedded thumbnail.
203*               This damage could be prevented where makernotes can be decoded,
204*               but currently this is not implemented.
205*
206*
207* Parameters:   exif_data - The array of EXIF data to insert into the JPEG header
208*               jpeg_header_data - The JPEG header into which the EXIF data
209*                                  should be stored, as from get_jpeg_header_data
210*
211* Returns:      jpeg_header_data - JPEG header array with the EXIF segment inserted
212*               FALSE - If an error occured
213*
214******************************************************************************/
215
216function put_EXIF_JPEG( $exif_data, $jpeg_header_data )
217{
218        // pack the EXIF data into its proper format for a JPEG file
219        $packed_data = get_TIFF_Packed_Data( $exif_data );
220        if ( $packed_data === FALSE )
221        {
222                return $jpeg_header_data;
223        }
224
225        $packed_data = "Exif\x00\x00$packed_data";
226
227        //Cycle through the header segments
228        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
229        {
230                // If we find an APP1 header,
231                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
232                {
233                        // And if it has the EXIF label,
234                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
235                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) )          // For some reason, some files have a faulty EXIF name which has a 0xFF in it
236                        {
237                                // Found a preexisting EXIF block - Replace it with the new one and return.
238                                $jpeg_header_data[$i]['SegData'] = $packed_data;
239                                return $jpeg_header_data;
240                        }
241                }
242        }
243
244        // No preexisting segment segment found, insert a new one at the start of the header data.
245
246        // Determine highest position of an APP segment at or below APP3, so we can put the
247        // new APP3 at this position
248
249
250        $highest_APP = -1;
251
252        //Cycle through the header segments
253        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
254        {
255                // Check if we have found an APP segment at or below APP3,
256                if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
257                {
258                        // Found an APP segment at or below APP12
259                        $highest_APP = $i;
260                }
261        }
262
263        // No preexisting EXIF block found, insert a new one at the start of the header data.
264        array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array(   "SegType" => 0xE1,
265                                                                              "SegName" => "APP1",
266                                                                              "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
267                                                                              "SegData" => $packed_data ) ) );
268        return $jpeg_header_data;
269
270}
271
272/******************************************************************************
273* End of Function:     put_EXIF_JPEG
274******************************************************************************/
275
276
277
278
279/******************************************************************************
280*
281* Function:     get_Meta_JPEG
282*
283* Description:  Retrieves information from a Meta APP3 segment and returns it
284*               in an array. Uses information supplied by the
285*               get_jpeg_header_data function.
286*               The Meta segment has the same format as an EXIF segment, but
287*               uses different tags
288*
289* Parameters:   filename - the filename of the JPEG image to process
290*
291* Returns:      OutputArray - Array of Meta records
292*               FALSE - If an error occured in decoding
293*
294******************************************************************************/
295
296function get_Meta_JPEG( $filename )
297{
298        // Change: Added as of version 1.11
299        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
300        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
301        {
302                // A HTTP or FTP wrapper is being used - show a warning and abort
303                echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified<br>";
304                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
305                echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );<br>\n";
306                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
307                return FALSE;
308        }
309
310        // get the JPEG headers
311        $jpeg_header_data = get_jpeg_header_data( $filename );
312
313
314        // Flag that an Meta segment has not been found yet
315        $Meta_Location = -1;
316
317        //Cycle through the header segments
318        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
319        {
320                // If we find an APP3 header,
321                if  ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
322                {
323                        // And if it has the Meta label,
324                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
325                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
326                        {
327                                // Save the location of the Meta segment
328                                $Meta_Location = $i;
329                        }
330                }
331        }
332
333        // Check if an EXIF segment was found
334        if ( $Meta_Location == -1 )
335        {
336                // Couldn't find any Meta block to decode
337                return FALSE;
338        }
339
340
341        $filehnd = @fopen($filename, 'rb');
342
343        // Check if the file opened successfully
344        if ( ! $filehnd  )
345        {
346                // Could't open the file - exit
347                echo "<p>Could not open file $filename</p>\n";
348                return FALSE;
349        }
350
351        fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 );
352
353        // Decode the Meta segment into an array and return it
354        $meta = process_TIFF_Header( $filehnd, "Meta" );
355
356         // Close File
357        fclose($filehnd);
358
359        return $meta;
360}
361
362/******************************************************************************
363* End of Function:     get_Meta
364******************************************************************************/
365
366
367
368
369
370
371
372/******************************************************************************
373*
374* Function:     put_Meta_JPEG
375*
376* Description:  Stores information into a Meta APP3 segment from a Meta array.
377*
378*
379*               WARNING: Because the Meta (EXIF) standard allows pointers to data
380*               outside the APP1 segment, if there are any such pointers in
381*               a makernote, this function will DAMAGE them since it will not
382*               be aware that there is an external pointer. This will often
383*               happen with Makernotes that include an embedded thumbnail.
384*               This damage could be prevented where makernotes can be decoded,
385*               but currently this is not implemented.
386*
387*
388* Parameters:   meta_data - The array of Meta data to insert into the JPEG header
389*               jpeg_header_data - The JPEG header into which the Meta data
390*                                  should be stored, as from get_jpeg_header_data
391*
392* Returns:      jpeg_header_data - JPEG header array with the Meta segment inserted
393*               FALSE - If an error occured
394*
395******************************************************************************/
396
397function put_Meta_JPEG( $meta_data, $jpeg_header_data )
398{
399        // pack the Meta data into its proper format for a JPEG file
400        $packed_data = get_TIFF_Packed_Data( $meta_data );
401        if ( $packed_data === FALSE )
402        {
403                return $jpeg_header_data;
404        }
405
406        $packed_data = "Meta\x00\x00$packed_data";
407
408        //Cycle through the header segments
409        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
410        {
411                // If we find an APP1 header,
412                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
413                {
414                        // And if it has the Meta label,
415                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
416                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
417                        {
418                                // Found a preexisting Meta block - Replace it with the new one and return.
419                                $jpeg_header_data[$i]['SegData'] = $packed_data;
420                                return $jpeg_header_data;
421                        }
422                }
423        }
424        // No preexisting segment segment found, insert a new one at the start of the header data.
425
426        // Determine highest position of an APP segment at or below APP3, so we can put the
427        // new APP3 at this position
428
429
430        $highest_APP = -1;
431
432        //Cycle through the header segments
433        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
434        {
435                // Check if we have found an APP segment at or below APP3,
436                if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
437                {
438                        // Found an APP segment at or below APP12
439                        $highest_APP = $i;
440                }
441        }
442
443        // No preexisting Meta block found, insert a new one at the start of the header data.
444        array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array(     "SegType" => 0xE3,
445                                                                                "SegName" => "APP3",
446                                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
447                                                                                "SegData" => $packed_data ) ) );
448        return $jpeg_header_data;
449
450}
451
452/******************************************************************************
453* End of Function:     put_Meta_JPEG
454******************************************************************************/
455
456
457
458/******************************************************************************
459*
460* Function:     get_EXIF_TIFF
461*
462* Description:  Retrieves information from a Exchangeable Image File Format (EXIF)
463*               within a TIFF file and returns it in an array.
464*
465* Parameters:   filename - the filename of the TIFF image to process
466*
467* Returns:      OutputArray - Array of EXIF records
468*               FALSE - If an error occured in decoding
469*
470******************************************************************************/
471
472function get_EXIF_TIFF( $filename )
473{
474        // Change: Added as of version 1.11
475        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
476        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
477        {
478                // A HTTP or FTP wrapper is being used - show a warning and abort
479                echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified<br>";
480                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
481                echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );<br>\n";
482                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
483                return FALSE;
484        }
485
486
487        $filehnd = @fopen($filename, 'rb');
488
489        // Check if the file opened successfully
490        if ( ! $filehnd  )
491        {
492                // Could't open the file - exit
493                echo "<p>Could not open file $filename</p>\n";
494                return FALSE;
495        }
496
497        // Decode the Exif segment into an array and return it
498        $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
499
500        // Close File
501        fclose($filehnd);
502        return $exif_data;
503}
504
505/******************************************************************************
506* End of Function:     get_EXIF_TIFF
507******************************************************************************/
508
509
510
511
512/******************************************************************************
513*
514* Function:     Interpret_EXIF_to_HTML
515*
516* Description:  Generates html detailing the contents an APP1 EXIF array
517*               which was retrieved with a get_EXIF_.... function.
518*               Can also be used for APP3 Meta arrays.
519*
520* Parameters:   Exif_array - the EXIF array,as read from get_EXIF_....
521*               filename - the name of the Image file being processed ( used
522*                          by scripts which displays EXIF thumbnails)
523*
524* Returns:      output_str - A string containing the HTML
525*
526******************************************************************************/
527
528function Interpret_EXIF_to_HTML( $Exif_array, $filename )
529{
530        // Create the string to receive the html output
531        $output_str = "";
532
533        // Check if the array to process is valid
534        if ( $Exif_array === FALSE )
535        {                // Exif Array is not valid - abort processing
536                return $output_str;
537        }
538      $output_str .= "<fieldset><legend>Contains " . $Exif_array[ 'Tags Name' ];
539        // Ouput the heading according to what type of tags were used in processing
540        if ( $Exif_array[ 'Tags Name' ] == "TIFF" ) {
541                $output_str .= " Exchangeable Image File Format (EXIF) " . $Exif_array[ 'Tags Name' ] . " Information</legend>";
542        } else if ( $Exif_array[ 'Tags Name' ] == "Meta" ) {
543                $output_str .= " Information (APP3)</legend>";
544        } else {
545                $output_str .= " Information </legend>";
546        }
547
548
549        // Check that there are actually items to process in the array
550        if ( count( $Exif_array ) < 1 )
551        {  // No items to process in array - abort processing
552                return  $output_str."</fieldset>" ;
553        }
554
555        // Output secondary heading
556         $output_str .="<fieldset id='".$Exif_array[ 'Tags Name' ]."_0' class='fieldset'> <legend>Main Image Information</legend>";
557        // Interpret the zeroth IFD to html
558         $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] );
559 
560 
561        // Check if there is a first IFD to process
562        if ( array_key_exists( 1, $Exif_array ) )
563        {
564                // There is a first IFD for a thumbnail
565                // Add a heading for it to the output
566                $output_str .= "<fieldset id='".$Exif_array[ 'Tags Name' ]."_1' class='fieldset'> <legend>Thumbnail Information</legend>";
567                // Interpret the IFD to html and add it to the output
568                $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] );           
569     
570        }
571 
572        // Cycle through any other IFD's
573        $i = 2;
574        while ( array_key_exists( $i, $Exif_array ) )
575        {
576                // Add a heading for the IFD
577                $output_str .= "<fieldset id='".$Exif_array[ 'Tags Name' ]."_".$i."' class='fieldset' > <legend>Image File Directory (IFD) $i Information</legend>";
578                // Interpret the IFD to html and add it to the output
579                $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] );
580                 
581                $i++;
582        }
583$output_str .="</fieldset>";
584        // Return the resulting HTML
585        return $output_str;
586}
587
588/******************************************************************************
589* End of Function:     Interpret_EXIF_to_HTML
590******************************************************************************/
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607/******************************************************************************
608*
609*         INTERNAL FUNCTIONS
610*
611******************************************************************************/
612
613
614
615
616
617
618
619
620
621
622
623/******************************************************************************
624*
625* Internal Function:     get_TIFF_Packed_Data
626*
627* Description:  Packs TIFF IFD data from EXIF or Meta into a form ready for
628*               either a JPEG EXIF/Meta segment or a TIFF file
629*               This function attempts to protect the contents of an EXIF makernote,
630*               by ensuring that it remains in the same position relative to the
631*               TIFF header
632*
633* Parameters:   tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG
634*
635* Returns:      packed_data - A string containing packed segment
636*
637******************************************************************************/
638
639function get_TIFF_Packed_Data( $tiff_data )
640{
641        // Check that the segment is valid
642        if ( $tiff_data === FALSE )
643        {
644                return FALSE;
645        }
646
647        // Get the byte alignment
648        $Byte_Align = $tiff_data['Byte_Align'];
649
650        // Add the Byte Alignment to the Packed data
651        $packed_data = $Byte_Align;
652
653        // Add the TIFF ID to the Packed Data
654        $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align );
655
656        // Create a string for the makernote
657        $makernote = "";
658
659        // Check if the makernote exists
660        if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE )
661        {
662                // A makernote exists - We need to ensure that it stays in the same position as it was
663                // Put the Makernote before any of the IFD's by padding zeros to the correct offset
664                $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) );
665                $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data'];
666        }
667
668        // Calculage where the zeroth ifd will be
669        $ifd_offset = strlen( $makernote ) + 8;
670
671        // Add the Zeroth IFD pointer to the packed data
672        $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align );
673
674        // Add the makernote to the packed data (if there was one)
675        $packed_data .= $makernote;
676
677        //Add the IFD's to the packed data
678        $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align );
679
680        // Return the result
681        return $packed_data;
682}
683
684/******************************************************************************
685* End of Function:     get_TIFF_Packed_Data
686******************************************************************************/
687
688
689
690
691/******************************************************************************
692*
693* Internal Function:     get_IFD_Array_Packed_Data
694*
695* Description:  Packs a chain of IFD's from EXIF or Meta segments into a form
696*               ready for either a JPEG EXIF/Meta segment or a TIFF file
697*
698* Parameters:   ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
699*               Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header
700*               Byte_Align - the Byte alignment to use - "MM" or "II"
701*
702* Returns:      packed_data - A string containing packed IFD's
703*
704******************************************************************************/
705
706function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align )
707{
708        // Create a string to receive the packed output
709        $packed_data = "";
710
711        // Count the IFDs
712        $ifd_count = 0;
713        foreach( $ifd_data as $key => $IFD )
714        {
715                // Make sure we only count the IFD's, not other information keys
716                if ( is_numeric( $key ) )
717                {
718                        $ifd_count++;
719                }
720        }
721
722
723        // Cycle through each IFD,
724        for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ )
725        {
726                // Check if this IFD is the last one
727                if ( $ifdno == $ifd_count - 1 )
728                {
729                        // This IFD is the last one, get it's packed data
730                        $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE );
731                }
732                else
733                {
734                        // This IFD is NOT the last one, get it's packed data
735                        $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE );
736                }
737
738        }
739
740        // Return the packed output
741        return $packed_data;
742}
743
744/******************************************************************************
745* End of Function:     get_IFD_Array_Packed_Data
746******************************************************************************/
747
748
749
750/******************************************************************************
751*
752* Internal Function:     get_IFD_Packed_Data
753*
754* Description:  Packs an IFD from EXIF or Meta segments into a form
755*               ready for either a JPEG EXIF/Meta segment or a TIFF file
756*
757* Parameters:   ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
758*               IFD_offset - The offset to the IFD from the start of the TIFF header
759*               Byte_Align - the Byte alignment to use - "MM" or "II"
760*               Another_IFD - boolean - false if this is the last IFD in the chain
761*                                     - true if it is not the last
762*
763* Returns:      packed_data - A string containing packed IFD's
764*
765******************************************************************************/
766
767function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD )
768{
769
770        $ifd_body_str = "";
771        $ifd_data_str = "";
772
773        $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ];
774
775
776        // Count the Tags in this IFD
777        $tag_count = 0;
778        foreach( $ifd_data as $key => $tag )
779        {
780                // Make sure we only count the Tags, not other information keys
781                if ( is_numeric( $key ) )
782                {
783                        $tag_count++;
784                }
785        }
786
787        // Add the Tag count to the packed data
788        $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align );
789
790        // Calculate the total length of the IFD (without the offset data)
791        $IFD_len = 2 + $tag_count * 12 + 4;
792
793
794        // Cycle through each tag
795        foreach( $ifd_data as $key => $tag )
796        {
797                // Make sure this is a tag, not another information key
798                if ( is_numeric( $key ) )
799                {
800
801                        // Add the tag number to the packed data
802                        $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align );
803
804                        // Add the Data type to the packed data
805                        $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align );
806
807                        // Check if this is a Print Image Matching entry
808                        if ( $tag['Type'] == "PIM" )
809                        {
810                                // This is a Print Image Matching entry,
811                                // encode it
812                                $data = Encode_PIM( $tag, $Byte_Align );
813                        }
814                                // Check if this is a IPTC/NAA Record within the EXIF IFD
815                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
816                                  ( $tag[ 'Tag Number' ] == 33723 ) )
817                        {
818                                // This is a IPTC/NAA Record, encode it
819                                $data = put_IPTC( $tag['Data'] );
820                        }
821                                // Change: Check for embedded XMP as of version 1.11
822                                // Check if this is a XMP Record within the EXIF IFD
823                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
824                                  ( $tag[ 'Tag Number' ] == 700 ) )
825                        {
826                                // This is a XMP Record, encode it
827                                $data = write_XMP_array_to_text( $tag['Data'] );
828                        }
829                                // Change: Check for embedded IRB as of version 1.11
830                                // Check if this is a Photoshop IRB Record within the EXIF IFD
831                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
832                                  ( $tag[ 'Tag Number' ] == 34377 ) )
833                        {
834                                // This is a Photoshop IRB Record, encode it
835                                $data = pack_Photoshop_IRB_Data( $tag['Data'] );
836                        }
837                                // Exif Thumbnail Offset
838                        else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) )
839                        {
840                                        // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown
841                                        // Hence we need to put the data into the packed string separately
842                                        // Calculate the thumbnail offset
843                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
844
845                                        // Create the Offset for the IFD
846                                        $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
847
848                                        // Store the thumbnail
849                                        $ifd_data_str .= $tag['Data'];
850                        }
851                                // Exif Thumbnail Length
852                        else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) )
853                        {
854                                        // Encode the Thumbnail Length
855                                        $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align );
856                        }
857                                // Sub-IFD
858                        else if ( $tag['Type'] == "SubIFD" )
859                        {
860                                        // This is a Sub-IFD
861                                        // Calculate the offset to the start of the Sub-IFD
862                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
863                                        // Get the packed data for the IFD chain as the data for this tag
864                                        $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align );
865                        }
866                        else
867                        {
868                                // Not a special tag
869
870                                // Create a string to receive the data
871                                $data = "";
872
873                                // Check if this is a type Unknown tag
874                                if ( $tag['Data Type'] != 7 )
875                                {
876                                        // NOT type Unknown
877                                        // Cycle through each data value and add it to the data string
878                                        foreach( $tag[ 'Data' ] as $data_val )
879                                        {
880                                                $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align );
881                                        }
882                                }
883                                else
884                                {
885                                        // This is a type Unknown - just add the data as is to the data string
886                                        $data .= $tag[ 'Data' ];
887                                }
888                        }
889
890                        // Pad the data string out to at least 4 bytes
891                        $data = str_pad ( $data, 4, "\x00" );
892
893
894                        // Check if the data type is an ASCII String or type Unknown
895                        if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) )
896                        {
897                                // This is an ASCII String or type Unknown
898                                // Add the Length of the string to the packed data as the Count
899                                $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align );
900                        }
901                        else
902                        {
903                                // Add the array count to the packed data as the Count
904                                $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align );
905                        }
906
907
908                        // Check if the data is over 4 bytes long
909                        if ( strlen( $data ) > 4 )
910                        {
911                                // Data is longer than 4 bytes - it needs to be offset
912                                // Check if this entry is the Maker Note
913                                if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) )
914                                {
915                                        // This is the makernote - It will have already been stored
916                                        // at its original offset to help preserve it
917                                        // all we need to do is add the Offset to the IFD packed data
918                                        $data_offset = $tag[ 'Offset' ];
919
920                                        $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
921                                }
922                                else
923                                {
924                                        // This is NOT the makernote
925                                        // Calculate the data offset
926                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
927
928                                        // Add the offset to the IFD packed data
929                                        $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
930
931                                        // Add the data to the offset packed data
932                                        $ifd_data_str .= $data;
933                                }
934                        }
935                        else
936                        {
937                                // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is
938                                $ifd_body_str .= $data;
939                        }
940
941                }
942        }
943
944        // Assemble the IFD body onto the packed data
945        $packed_data .= $ifd_body_str;
946
947        // Check if there is another IFD after this one
948        if( $Another_IFD === TRUE )
949        {
950                // There is another IFD after this
951                // Calculate the Next-IFD offset so that it goes immediately after this IFD
952                $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
953        }
954        else
955        {
956                // There is NO IFD after this - indicate with offset=0
957                $next_ifd_offset = 0;
958        }
959
960        // Add the Next-IFD offset to the packed data
961        $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align );
962
963        // Add the offset data to the packed data
964        $packed_data .= $ifd_data_str;
965
966        // Return the resulting packed data
967        return $packed_data;
968}
969
970/******************************************************************************
971* End of Function:     get_IFD_Packed_Data
972******************************************************************************/
973
974
975
976
977
978/******************************************************************************
979*
980* Internal Function:     process_TIFF_Header
981*
982* Description:  Decodes the information stored in a TIFF header and it's
983*               Image File Directories (IFD's). This information is returned
984*               in an array
985*
986* Parameters:   filehnd - The handle of a open image file, positioned at the
987*                          start of the TIFF header
988*               Tag_Definitions_Name - The name of the Tag Definitions group
989*                                      within the global array IFD_Tag_Definitions
990*
991*
992* Returns:      OutputArray - Array of IFD records
993*               FALSE - If an error occured in decoding
994*
995******************************************************************************/
996
997function process_TIFF_Header( $filehnd, $Tag_Definitions_Name )
998{
999
1000
1001        // Save the file position where the TIFF header starts, as offsets are relative to this position
1002        $Tiff_start_pos = ftell( $filehnd );
1003
1004
1005
1006        // Read the eight bytes of the TIFF header
1007        $DataStr = network_safe_fread( $filehnd, 8 );
1008
1009        // Check that we did get all eight bytes
1010        if ( strlen( $DataStr ) != 8 )
1011        {
1012                return FALSE;   // Couldn't read the TIFF header properly
1013        }
1014
1015        $pos = 0;
1016        // First two bytes indicate the byte alignment - should be 'II' or 'MM'
1017        // II = Intel (LSB first, MSB last - Little Endian)
1018        // MM = Motorola (MSB first, LSB last - Big Endian)
1019        $Byte_Align = substr( $DataStr, $pos, 2 );
1020
1021
1022
1023        // Check the Byte Align Characters for validity
1024        if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) )
1025        {
1026                // Byte align field is invalid - we won't be able to decode file
1027                return FALSE;
1028        }
1029
1030        // Skip over the Byte Align field which was just read
1031        $pos += 2;
1032
1033        // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment
1034        $TIFF_ID = substr( $DataStr, $pos, 2 );
1035
1036        if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 )
1037        {
1038                // TIFF header ID not found
1039                return FALSE;
1040        }
1041
1042        // Skip over the TIFF ID field which was just read
1043        $pos += 2;
1044
1045
1046        // Next four bytes are the offset to the first IFD
1047        $offset_str = substr( $DataStr, $pos, 4 );
1048        $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align );
1049
1050        // Done reading TIFF Header
1051
1052
1053        // Move to first IFD
1054
1055        if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 )
1056        {
1057                // Error seeking to position of first IFD
1058                return FALSE;
1059        }
1060
1061
1062
1063        // Flag that a makernote has not been found yet
1064        $GLOBALS[ "Maker_Note_Tag" ] = FALSE;
1065
1066        // Read the IFD chain into an array
1067        $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name );
1068
1069        // Check if a makernote was found
1070        if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE )
1071        {
1072                // Makernote was found - Process it
1073                // The makernote needs to be processed after all other
1074                // tags as it may require some of the other tags in order
1075                // to be processed properly
1076                $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd );
1077
1078        }
1079
1080        $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ];
1081
1082        // Save the Name of the Tags used in the output array
1083        $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name;
1084
1085
1086
1087        // Save the Byte alignment
1088        $Output_Array['Byte_Align'] = $Byte_Align;
1089
1090
1091        // Return the output array
1092        return $Output_Array ;
1093}
1094
1095/******************************************************************************
1096* End of Function:     process_TIFF_Header
1097******************************************************************************/
1098
1099
1100
1101
1102
1103
1104/******************************************************************************
1105*
1106* Internal Function:     read_Multiple_IFDs
1107*
1108* Description:  Reads and interprets a chain of standard Image File Directories (IFD's),
1109*               and returns the entries in an array. This chain is made up from IFD's
1110*               which have a pointer to the next IFD. IFD's are read until the next
1111*               pointer indicates there are no more
1112*
1113* Parameters:   filehnd - a handle for the image file being read, positioned at the
1114*                         start of the IFD chain
1115*               Tiff_offset - The offset of the TIFF header from the start of the file
1116*               Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
1117*               Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
1118*               local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
1119*                               False (normal) indicates offests are relative to start of Tiff header as per IFD standard
1120*               read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
1121*                               False indicates that no pointer follows the IFD
1122*
1123*
1124* Returns:      OutputArray - Array of IFD entries
1125*
1126******************************************************************************/
1127
1128function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
1129{
1130        // Start at the offset of the first IFD
1131        $Next_Offset = 0;
1132
1133        do
1134        {
1135                // Read an IFD
1136                list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr );
1137
1138                // Move to the position of the next IFD
1139                if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 )
1140                {
1141                        // Error seeking to position of next IFD
1142                        echo "<p>Error: Corrupted EXIF</p>\n";
1143                        return FALSE;
1144                }
1145
1146                $Output_Array[] = $IFD_Array;
1147
1148
1149        } while ( $Next_Offset != 0 );      // Until the Next IFD Offset is zero
1150
1151
1152        // return resulting array
1153
1154        return $Output_Array ;
1155}
1156
1157/******************************************************************************
1158* End of Function:     read_Multiple_IFDs
1159******************************************************************************/
1160
1161
1162
1163
1164
1165
1166
1167/******************************************************************************
1168*
1169* Internal Function:     read_IFD_universal
1170*
1171* Description:  Reads and interprets a standard or Non-standard Image File
1172*               Directory (IFD), and returns the entries in an array
1173*
1174* Parameters:   filehnd - a handle for the image file being read, positioned at the start
1175*                         of the IFD
1176*               Tiff_offset - The offset of the TIFF header from the start of the file
1177*               Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
1178*               Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
1179*               local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
1180*                               False (normal) indicates offests are relative to start of Tiff header as per IFD standard
1181*               read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
1182*                               False indicates that no pointer follows the IFD
1183*
1184* Returns:      OutputArray - Array of IFD entries
1185*               Next_Offset - Offset to next IFD (zero = no next IFD)
1186*
1187******************************************************************************/
1188
1189function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
1190{
1191        if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) )
1192        {
1193                return array (FALSE , 0);
1194        }
1195
1196        // Record the Name of the Tag Group used for this IFD in the output array
1197        $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name;
1198
1199        // Record the offset of the TIFF header in the output array
1200        $OutputArray[ 'Tiff Offset' ] = $Tiff_offset;
1201
1202        // First 2 bytes of IFD are number of entries in the IFD
1203        $No_Entries_str = network_safe_fread( $filehnd, 2 );
1204        $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align );
1205
1206
1207        // If the data is corrupt, the number of entries may be huge, which will cause errors
1208        // This is often caused by a lack of a Next-IFD pointer
1209        if ( $No_Entries> 10000 )
1210        {
1211                // Huge number of entries - abort
1212                echo "<p>Error: huge number of EXIF entries - EXIF is probably Corrupted</p>\n";
1213
1214                return array ( FALSE , 0);
1215        }
1216
1217        // If the data is corrupt or just stupid, the number of entries may zero,
1218        // Indicate this by returning false
1219        if ( $No_Entries === 0 )
1220        {
1221                // No entries - abort
1222                return array ( FALSE , 0);
1223        }
1224
1225        // Save the file position where first IFD record starts as non-standard offsets
1226        // need to know this to calculate an absolute offset
1227        $IFD_first_rec_pos = ftell( $filehnd );
1228
1229
1230        // Read in the IFD structure
1231        $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries );
1232
1233        // Check if the entire IFD was able to be read
1234        if ( strlen( $IFD_Data ) != (12 * $No_Entries) )
1235        {
1236                // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error
1237                echo "<p>Error: EXIF Corrupted</p>\n";
1238                return array(FALSE, 0);
1239        }
1240
1241
1242        // Last 4 bytes of a standard IFD are the offset to the next IFD
1243        // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read
1244
1245        // If the Next IFD pointer has been requested to be read,
1246        if ( $read_next_ptr )
1247        {
1248                // Read the pointer to the next IFD
1249
1250                $Next_Offset_str = network_safe_fread( $filehnd, 4 );
1251                $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align );
1252        }
1253        else
1254        {
1255                // Otherwise set the pointer to zero ( no next IFD )
1256                $Next_Offset = 0;
1257        }
1258
1259
1260
1261        // Initialise current position to the start
1262        $pos = 0;
1263
1264
1265        // Loop for reading IFD entries
1266
1267        for ( $i = 0; $i < $No_Entries; $i++ )
1268        {
1269                // First 2 bytes of IFD entry are the tag number ( Unsigned Short )
1270                $Tag_No_str = substr( $IFD_Data, $pos, 2 );
1271                $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align );
1272                $pos += 2;
1273
1274                // Next 2 bytes of IFD entry are the data format ( Unsigned Short )
1275                $Data_Type_str = substr( $IFD_Data, $pos, 2 );
1276                $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align );
1277                $pos += 2;
1278
1279                // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom
1280                if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) )
1281                {
1282                        $pos += 8;
1283                        continue 1;  // Stop trying to process the tag any further and skip to the next one
1284                }
1285
1286                // Next 4 bytes of IFD entry are the data count ( Unsigned Long )
1287                $Data_Count_str = substr( $IFD_Data, $pos, 4 );
1288                $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align );
1289                $pos += 4;
1290
1291                if ( $Data_Count > 100000 )
1292                {
1293                     //   echo "<p>Error: huge EXIF data count - EXIF is probably Corrupted</p>\n";
1294
1295                        // Some Casio files have no Next IFD pointer, hence cause errors
1296
1297                        return array ( FALSE , 0);
1298                }
1299
1300                // Total Data size is the Data Count multiplied by the size of the Data Type
1301                $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count;
1302
1303                $Data_Start_pos = -1;
1304
1305                // If the total data size is larger than 4 bytes, then the data part is the offset to the real data
1306                if ( $Total_Data_Size > 4 )
1307                {
1308                        // Not enough room for data - offset provided instead
1309                        $Data_Offset_str = substr( $IFD_Data, $pos, 4 );
1310                        $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align );
1311
1312
1313                        // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry
1314                        if ( $local_offsets )
1315                        {
1316                                // This is a NON-Standard IFD, seek relative to the start of the current tag
1317                                fseek( $filehnd, $IFD_first_rec_pos +  $pos - 8 + $Data_Start_pos );
1318                        }
1319                        else
1320                        {
1321                                // This is a normal IFD, seek relative to the start of the TIFF header
1322                                fseek( $filehnd, $Tiff_offset + $Data_Start_pos );
1323                        }
1324
1325                        // Read the data block from the offset position
1326                        $DataStr = network_safe_fread( $filehnd, $Total_Data_Size );
1327                }
1328                else
1329                {
1330                        // The data block is less than 4 bytes, and is provided in the IFD entry, so read it
1331                        $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size );
1332                }
1333
1334                // Increment the position past the data
1335                $pos += 4;
1336
1337
1338                // Now create the entry for output array
1339
1340                $Data_Array = array( );
1341
1342
1343                // Read the data items from the data block
1344
1345                if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) )
1346                {
1347                        // The data type is Numerical, Read the data items from the data block
1348                        for ( $j = 0; $j < $Data_Count; $j++ )
1349                        {
1350                                $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] );
1351                                $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align );
1352                        }
1353                }
1354                elseif ( $Data_Type == 2 )
1355                {
1356                        // The data type is String(s)   (type 2)
1357
1358                        // Strip the last terminating Null
1359                        $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 );
1360
1361                        // Split the data block into multiple strings whereever there is a Null
1362                        $Data_Array = explode( "\x00", $DataStr );
1363                }
1364                else
1365                {
1366                        // The data type is Unknown (type 7)
1367                        // Do nothing to data
1368                        $Data_Array = $DataStr;
1369                }
1370
1371
1372                // If this is a Sub-IFD entry,
1373                if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) &&
1374                     ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) )
1375                {
1376                        // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry
1377                        fseek( $filehnd, $Tiff_offset + $Data_Array[0] );
1378                        $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] );
1379                }
1380
1381                $desc = "";
1382                $units = "";
1383
1384                // Check if this tag exists in the list of tag definitions,
1385
1386                if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) )
1387                {
1388
1389                        if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
1390                        {
1391                                $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description'];
1392                        }
1393
1394                        if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
1395                        {
1396                                $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units'];
1397                        }
1398
1399                        // Tag exists in definitions, append details to output array
1400                        $OutputArray[ $Tag_No ] = array (       "Tag Number"      => $Tag_No,
1401                                                                "Tag Name"        => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'],
1402                                                                "Tag Description" => $desc,
1403                                                                "Data Type"       => $Data_Type,
1404                                                                "Type"            => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'],
1405                                                                "Units"           => $units,
1406                                                                "Data"            => $Data_Array );
1407
1408                }
1409                else
1410                {
1411                        // Tag doesnt exist in definitions, append unknown details to output array
1412
1413                        $OutputArray[ $Tag_No ] = array (       "Tag Number"      => $Tag_No,
1414                                                                "Tag Name"        => "Unknown Tag #" . $Tag_No,
1415                                                                "Tag Description" => "",
1416                                                                "Data Type"       => $Data_Type,
1417                                                                "Type"            => "Unknown",
1418                                                                "Units"           => "",
1419                                                                "Data"            => $Data_Array );
1420                }
1421
1422
1423
1424                // Some information of type "Unknown" (type 7) might require information about
1425                // how it's position and byte alignment in order to be decoded
1426                if ( $Data_Type == 7 )
1427                {
1428                        $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
1429                        $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align;
1430                }
1431
1432
1433                ////////////////////////////////////////////////////////////////////////
1434                // Special Data handling
1435                ////////////////////////////////////////////////////////////////////////
1436
1437
1438                // Check if this is a Print Image Matching entry
1439                if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" )
1440                {
1441                        // This is a Print Image Matching entry, decode it.
1442                        $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
1443                }
1444
1445
1446                // Interpret the entry into a text string using a custom interpreter
1447                $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
1448
1449                // Check if a text string was generated
1450                if ( $text_val !== FALSE )
1451                {
1452                        // A string was generated, append it to the output array entry
1453                        $OutputArray[ $Tag_No ]['Text Value'] = $text_val;
1454                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
1455                }
1456                else
1457                {
1458                        // A string was NOT generated, append a generic string to the output array entry
1459                        $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] )  . " " . $units;
1460                        $OutputArray[ $Tag_No ]['Decoded'] = FALSE;
1461                }
1462
1463
1464
1465
1466                // Check if this entry is the Maker Note
1467                if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) )
1468                {
1469
1470                        // Save some extra information which will allow Makernote Decoding with the output array entry
1471                        $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
1472                        $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset;
1473                        $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align;
1474
1475                        // Save a pointer to this entry for Maker note processing later
1476                        $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ];
1477                }
1478
1479
1480                // Check if this is a IPTC/NAA Record within the EXIF IFD
1481                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
1482                     ( $Tag_No == 33723 ) )
1483                {
1484                        // This is a IPTC/NAA Record, interpret it and put result in the data for this entry
1485                        $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr );
1486                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
1487                }
1488                // Change: Check for embedded XMP as of version 1.11
1489                // Check if this is a XMP Record within the EXIF IFD
1490                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
1491                     ( $Tag_No == 700 ) )
1492                {
1493                        // This is a XMP Record, interpret it and put result in the data for this entry
1494                        $OutputArray[ $Tag_No ]['Data'] =  read_XMP_array_from_text( $DataStr );
1495                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
1496                }
1497
1498                // Change: Check for embedded IRB as of version 1.11
1499                // Check if this is a Photoshop IRB Record within the EXIF IFD
1500                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
1501                     ( $Tag_No == 34377 ) )
1502                {
1503                        // This is a Photoshop IRB Record, interpret it and put result in the data for this entry
1504                        $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr );
1505                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
1506                }
1507
1508                // Exif Thumbnail
1509                // Check that both the thumbnail length and offset entries have been processed,
1510                // and that this is one of them
1511                if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) ||
1512                       ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) )  &&
1513                     ( $Tag_Definitions_Name == "TIFF" ) )
1514                {
1515                        // Seek to the start of the thumbnail using the offset entry
1516                        fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] );
1517
1518                        // Read the thumbnail data, and replace the offset data with the thumbnail
1519                        $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] );
1520                }
1521
1522
1523                // Casio Thumbnail
1524                // Check that both the thumbnail length and offset entries have been processed,
1525                // and that this is one of them
1526                if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) ||
1527                       ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) )  &&
1528                     ( $Tag_Definitions_Name == "Casio Type 2" ) )
1529                {
1530                        // Seek to the start of the thumbnail using the offset entry
1531                        fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] );
1532
1533                        // Read the thumbnail data, and replace the offset data with the thumbnail
1534                        $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] );
1535                }
1536
1537                // Minolta Thumbnail
1538                // Check that both the thumbnail length and offset entries have been processed,
1539                // and that this is one of them
1540                if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) ||
1541                       ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) )  &&
1542                     ( $Tag_Definitions_Name == "Olympus" ) )
1543                {
1544
1545                        // Seek to the start of the thumbnail using the offset entry
1546                        fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] );
1547
1548                        // Read the thumbnail data, and replace the offset data with the thumbnail
1549                        $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] );
1550
1551                        // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing)
1552
1553                        // Check if the thumbnail data exists
1554                        if ( $OutputArray[0x0088]['Data'] != "" )
1555                        {
1556                                // Thumbnail exists
1557
1558                                // Minolta Thumbnails are missing their first 0xFF for some reason,
1559                                // which is replaced with some weird character, so fix this
1560                                $OutputArray[0x0088]['Data']{0} = "\xFF";
1561                        }
1562                        else
1563                        {
1564                                // Thumbnail doesnt exist - make it obvious
1565                                $OutputArray[0x0088]['Data'] = FALSE;
1566                        }
1567                }
1568
1569        }
1570
1571
1572
1573
1574
1575
1576
1577        // Return the array of IFD entries and the offset to the next IFD
1578
1579        return array ($OutputArray , $Next_Offset);
1580}
1581
1582
1583
1584/******************************************************************************
1585* End of Function:     read_IFD_universal
1586******************************************************************************/
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599/******************************************************************************
1600*
1601* Internal Function:     get_Tag_Text_Value
1602*
1603* Description:  Attempts to interpret an IFD entry into a text string using the
1604*               information in the IFD_Tag_Definitions global array.
1605*
1606* Parameters:   Tag - The IFD entry to process
1607*               Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
1608*
1609* Returns:      String - if the tag was successfully decoded into a text string
1610*               FALSE - if the tag could not be decoded using the information
1611*                       in the IFD_Tag_Definitions global array
1612*
1613******************************************************************************/
1614
1615function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
1616{
1617        // Check what format the entry is specified as
1618
1619        if ( $Tag['Type'] == "String" )
1620        {
1621                // Format is Text String
1622
1623                // If "Unknown" (type 7) data type,
1624                if ( $Tag['Data Type'] == 7 )
1625                {
1626                        // Return data as is.
1627                        return $Tag['Data'];
1628                }
1629                else
1630                {
1631                        // Otherwise return the default string value of the datatype
1632                        return get_IFD_value_as_text( $Tag );
1633                }
1634        }
1635        else if ( $Tag['Type'] == "Character Coded String" )
1636        {
1637                // Format is Character Coded String (First 8 characters indicate coding scheme)
1638
1639                // Convert Data to a string
1640                if ( $Tag['Data Type'] == 7 )
1641                {
1642                        // If it is type "Unknown" (type 7) use data as is
1643                        $data =  $Tag['Data'];
1644                }
1645                else
1646                {
1647                        // Otherwise use the default string value of the datatype
1648                        $data = get_IFD_value_as_text( $Tag );
1649                }
1650
1651                // Some implementations allow completely data with no Coding Scheme Name,
1652                // so we need to handle this to avoid errors
1653                if ( trim( $data ) == "" )
1654                {
1655                        return "";
1656                }
1657
1658                // Extract the Coding Scheme Name from the first 8 characters
1659                $char_code = substr( $data, 0, 8 );
1660
1661                // Extract the Data part from after the first 8 characters
1662                $characters = substr( $data, 8 );
1663
1664                // Check coding scheme and interpret as neccessary
1665
1666                if ( $char_code === "ASCII\x00\x00\x00" )
1667                {
1668                        // ASCII coding - return data as is.
1669                        return $characters;
1670                }
1671                elseif ( ( $char_code === "UNICODE\x00" ) ||
1672                         ( $char_code === "Unicode\x00" ) )             // Note lowercase is non standard
1673                {
1674                        // Unicode coding - interpret and return result.
1675                        return xml_UTF16_clean( $characters, TRUE );
1676                }
1677                else
1678                {
1679                        // Unknown coding - return string indicating this
1680                        return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\"";
1681                }
1682                break;
1683        }
1684        else if ( $Tag['Type'] == "Numeric" )
1685        {
1686                // Format is numeric - return default text value with any required units text appended
1687                if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
1688                {
1689                        $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units'];
1690                }
1691                else
1692                {
1693                        $units = "";
1694                }
1695                return get_IFD_value_as_text( $Tag )  . " " . $units;
1696        }
1697        else if  ( $Tag['Type'] == "Lookup" )
1698        {
1699                // Format is a Lookup Table
1700
1701                // Get a numeric value to use in lookup
1702
1703                if ( is_array( $Tag['Data'] ) )
1704                {
1705                        // If data is an array, use first element
1706                        $first_val = $Tag['Data'][0];
1707                }
1708                else if ( is_string( $Tag['Data'] ) )
1709                {
1710                        // If data is a string, use the first character
1711                        $first_val = ord($Tag['Data']{0});
1712                }
1713                else
1714                {
1715                        // Otherwise use the data as is
1716                        $first_val = $Tag['Data'];
1717                }
1718
1719                // Check if the data value exists in the lookup table for this IFD entry
1720                if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
1721                {
1722                        // Data value exists in lookup table - return the matching string
1723                        return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ];
1724                }
1725                else
1726                {
1727                        // Data value doesnt exist in lookup table - return explanation string
1728                        return "Unknown Reserved value $first_val ";
1729                }
1730        }
1731        else if  ( $Tag['Type'] == "Special" )
1732        {
1733                // Format is special - interpret to text with special handlers
1734                return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name );
1735        }
1736        else if  ( $Tag['Type'] == "PIM" )
1737        {
1738                // Format is Print Image Matching info - interpret with custom handler
1739                return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name );
1740        }
1741        else if  ( $Tag['Type'] == "SubIFD" )
1742        {
1743                // Format is a Sub-IFD - this has no text value
1744                return "";
1745        }
1746        else
1747        {
1748                // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information
1749                return FALSE;
1750        }
1751}
1752
1753/******************************************************************************
1754* End of Function:     get_Tag_Text_Value
1755******************************************************************************/
1756
1757
1758
1759
1760
1761
1762/******************************************************************************
1763*
1764* Internal Function:     get_Special_Tag_Text_Value
1765*
1766* Description:  Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions
1767*               global array into a text string using custom handlers
1768*
1769* Parameters:   Tag - The IFD entry to process
1770*               Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
1771*
1772* Returns:      String - if the tag was successfully decoded into a text string
1773*               FALSE - if the tag could not be decoded
1774*
1775******************************************************************************/
1776
1777function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
1778{
1779        // Check what type of IFD is being decoded
1780
1781        if ( $Tag_Definitions_Name == "TIFF" )
1782        {
1783                // This is a TIFF IFD (bottom level)
1784
1785                // Check what tag number the IFD entry has.
1786                switch ( $Tag['Tag Number'] )
1787                {
1788                        case 530:  // YCbCr Sub Sampling Entry
1789
1790                                // Data contains two numerical values
1791
1792                                if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) )
1793                                {
1794                                        // Values are 2,1 - hence YCbCr 4:2:2
1795                                        return "YCbCr 4:2:2 ratio of chrominance components to the luminance components";
1796                                }
1797                                elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) )
1798                                {
1799                                        // Values are 2,2 - hence YCbCr 4:2:0
1800                                        return "YCbCr 4:2:0 ratio of chrominance components to the luminance components";
1801                                }
1802                                else
1803                                {
1804                                        // Other values are unknown
1805                                        return "Unknown Reserved value (" . $Tag['Data'][0] . ")";
1806                                }
1807                                break;
1808
1809                        default:
1810                                return FALSE;
1811                }
1812        }
1813        else if ( $Tag_Definitions_Name == "EXIF" )
1814        {
1815                // This is an EXIF IFD
1816
1817                // Check what tag number the IFD entry has.
1818                switch ( $Tag['Tag Number'] )
1819                {
1820
1821                        case 37121: // Components configuration
1822
1823                                // Data contains 4 numerical values indicating component type
1824
1825                                $output_str = "";
1826
1827                                // Cycle through each component
1828                                for ( $Num = 0; $Num < 4; $Num++ )
1829                                {
1830                                        // Construct first part of text string
1831                                        $output_str .= "Component " . ( $Num + 1 ) . ": ";
1832
1833                                        // Construct second part of text string via
1834                                        // lookup using numerical value
1835
1836                                        $value = ord( $Tag['Data']{$Num} );
1837                                        switch( $value )
1838                                        {
1839                                                case 0:
1840                                                        $output_str .= "Does not exist\n";
1841                                                        break;
1842                                                case 1:
1843                                                        $output_str .= "Y (Luminance)\n";
1844                                                        break;
1845                                                case 2:
1846                                                        $output_str .= "Cb (Chroma minus Blue)\n";
1847                                                        break;
1848                                                case 3:
1849                                                        $output_str .= "Cr (Chroma minus Red)\n";
1850                                                        break;
1851                                                case 4:
1852                                                        $output_str .= "Red\n";
1853                                                        break;
1854                                                case 5:
1855                                                        $output_str .= "Green\n";
1856                                                        break;
1857                                                case 6:
1858                                                        $output_str .= "Blue\n";
1859                                                        break;
1860                                                default:
1861                                                        $output_str .= "Unknown value $value\n";
1862                                        };
1863                                }
1864
1865                                // Return the completed string
1866
1867                                return $output_str;
1868                                break;
1869
1870
1871
1872                        case 41730: // Colour Filter Array Pattern
1873
1874                                // The first two characters are a SHORT for Horizontal repeat pixel unit -
1875                                $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] );
1876
1877                                // The next two characters are a SHORT for Vertical repeat pixel unit -
1878                                $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] );
1879
1880
1881                                // At least one camera type appears to have byte reversed values for N_Max and M_Max
1882                                // Check if they need reversing
1883                                if ( $n_max > 256 )
1884                                {
1885                                        $n_max = $n_max/256 + 256*($n_max%256);
1886                                }
1887
1888                                if ( $m_max > 256 )
1889                                {
1890                                        $m_max = $m_max/256 + 256*($m_max%256);
1891                                }
1892
1893
1894                                $output_str = "";
1895
1896
1897                                // Cycle through all the elements in the resulting 2 dimensional array,
1898                                for( $m = 1; $m <= $m_max; $m++ )
1899                                {
1900                                        for( $n = 1; $n <= $n_max; $n++ )
1901                                        {
1902
1903                                                // Append text from a lookup table according to
1904                                                // the value read for this element
1905
1906                                                switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) )
1907                                                {
1908                                                        case 0:
1909                                                                $output_str .= "RED     ";
1910                                                                break;
1911                                                        case 1:
1912                                                                $output_str .= "GREEN   ";
1913                                                                break;
1914                                                        case 2:
1915                                                                $output_str .= "BLUE    ";
1916                                                                break;
1917                                                        case 3:
1918                                                                $output_str .= "CYAN    ";
1919                                                                break;
1920                                                        case 4:
1921                                                                $output_str .= "MAGENTA ";
1922                                                                break;
1923                                                        case 5:
1924                                                                $output_str .= "YELLOW  ";
1925                                                                break;
1926                                                        case 6:
1927                                                                $output_str .= "WHITE   ";
1928                                                                break;
1929                                                        default:
1930                                                                $output_str .= "Unknown ";
1931                                                                break;
1932                                                };
1933                                        };
1934                                        $output_str .= "\n";
1935                                };
1936
1937                                // Return the resulting string
1938                                return $output_str;
1939                                break;
1940
1941                        default:
1942                                return FALSE;
1943                }
1944        }
1945        else
1946        {
1947                // Unknown IFD type, see if it is part of a makernote
1948                return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name );
1949        }
1950
1951
1952}
1953
1954/******************************************************************************
1955* End of Function:     get_Tag_Text_Value
1956******************************************************************************/
1957
1958
1959
1960
1961
1962
1963
1964
1965/******************************************************************************
1966*
1967* Function:     interpret_IFD
1968*
1969* Description:  Generates html detailing the contents a single IFD.
1970*
1971* Parameters:   IFD_array - the array containing an IFD
1972*               filename - the name of the Image file being processed ( used
1973*                          by scripts which displays EXIF thumbnails)
1974*
1975* Returns:      output_str - A string containing the HTML
1976*
1977******************************************************************************/
1978
1979function interpret_IFD( $IFD_array, $filename )
1980{
1981        // Create the output string with the table tag
1982        $output_str = "<table class=\"EXIF_Table\" border=1>\n";
1983
1984        // Create an extra output string to receive any supplementary html
1985        // which cannot go inside the table
1986        $extra_IFD_str = "";
1987
1988        // Check that the IFD array is valid
1989        if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) )
1990        {
1991                // the IFD array is NOT valid - exit
1992                return "";
1993        }
1994
1995        // Check if this is an EXIF IFD and if there is a makernote present
1996        if ( ( $IFD_array['Tags Name'] === "EXIF" ) &&
1997             ( ! array_key_exists( 37500, $IFD_array ) ) )
1998        {
1999
2000                // This is an EXIF IFD but NO makernote is present - Add a message to the output
2001              //  $extra_IFD_str .= "No Makernote Present";
2002        }
2003
2004        // Cycle through each tag in the IFD
2005
2006        foreach( $IFD_array as $Tag_ID => $Exif_Tag )
2007        {
2008            $Exif_Tag['Type_src']="exif";
2009                // Ignore the non numeric elements - they aren't tags
2010                if ( ! is_numeric ( $Tag_ID ) )
2011                {
2012                        // Skip Tags Name
2013                }
2014                        // Check if the Tag has been decoded successfully
2015                else if ( $Exif_Tag['Decoded'] == TRUE )
2016                {
2017                        // This tag has been successfully decoded
2018
2019                        // Table cells won't get drawn with nothing in them -
2020                        // Ensure that at least a non breaking space exists in them
2021
2022                        if ( trim($Exif_Tag['Text Value']) == "" )
2023                        {
2024                                $Exif_Tag['Text Value'] = "&nbsp;";
2025                        }
2026
2027                        // Check if the tag is a sub-IFD
2028                        if ( $Exif_Tag['Type'] == "SubIFD" )
2029                        {
2030                                // This is a sub-IFD tag
2031                                // Add a sub-heading for the sub-IFD
2032                                $recordname=str_replace(" ","_",$Exif_Tag['Tag Name']);
2033                                $recordname=str_replace("_(IFD)","", $recordname);
2034$entete = "<fieldset  id='" . $recordname . "' class='fieldset' ><legend>";
2035$extra_IFD_str .= $entete . $Exif_Tag['Tag Name'] . " contents</legend>";
2036                                // Cycle through each sub-IFD in the chain
2037                                foreach ( $Exif_Tag['Data'] as $subIFD )
2038                                {
2039                                        // Interpret this sub-IFD and add the html to the secondary output
2040                                        $extra_IFD_str .= interpret_IFD( $subIFD, $filename );
2041     
2042        }
2043                             
2044                        }
2045                                // Check if the tag is a makernote
2046                        else if ( $Exif_Tag['Type'] == "Maker Note" )
2047                        {
2048                                // This is a Makernote Tag
2049                                // Add a sub-heading for the Makernote
2050                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Maker Note Contents</h3>";
2051
2052                                // Interpret the Makernote and add the html to the secondary output
2053                                $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename );
2054                        }
2055                                // Check if this is a IPTC/NAA Record within the EXIF IFD
2056                        else if ( $Exif_Tag['Type'] == "IPTC" )
2057                        {
2058                                // This is a IPTC/NAA Record, interpret it and output to the secondary html
2059                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains IPTC/NAA Embedded in EXIF</h3>";
2060                                $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] );
2061                        }
2062                                // Change: Check for embedded XMP as of version 1.11
2063                                // Check if this is a XMP Record within the EXIF IFD
2064                        else if ( $Exif_Tag['Type'] == "XMP" )
2065                        {
2066                                // This is a XMP Record, interpret it and output to the secondary html
2067                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains XMP Embedded in EXIF</h3>";
2068                                $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] );
2069                        }
2070                                // Change: Check for embedded IRB as of version 1.11
2071                                // Check if this is a Photoshop IRB Record within the EXIF IFD
2072                        else if ( $Exif_Tag['Type'] == "IRB" )
2073                        {
2074                                // This is a Photoshop IRB Record, interpret it and output to the secondary html
2075                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains Photoshop IRB Embedded in EXIF</h3>";
2076                                $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename );
2077                        }
2078                                // Check if the tag is Numeric
2079                        else if ( $Exif_Tag['Type'] == "Numeric" )
2080                        {
2081                   
2082$output_str .= OutPut_exif($Exif_Tag);
2083                                // Numeric Tag - Output text value as is.
2084                        }
2085                        else
2086                        {
2087$output_str .= OutPut_exif($Exif_Tag);
2088                                // Other tag - Output text as preformatted
2089                         }
2090
2091                }
2092                else
2093                {
2094                        // Tag has NOT been decoded successfully
2095                        // Hence it is either an unknown tag, or one which
2096                        // requires processing at the time of html construction
2097
2098                        // Table cells won't get drawn with nothing in them -
2099                        // Ensure that at least a non breaking space exists in them
2100
2101                        if ( trim($Exif_Tag['Text Value']) == "" )
2102                        {
2103                                $Exif_Tag['Text Value'] = "&nbsp;";
2104                        }
2105
2106                        // Check if this tag is the first IFD Thumbnail
2107                        if ( ( $IFD_array['Tags Name'] == "TIFF" ) &&
2108                             ( $Tag_ID == 513 ) )
2109                        {
2110                                // This is the first IFD thumbnail - Add html to the output
2111
2112                                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
2113                                // Build the path of the thumbnail script and its filename parameter to put in a url
2114                                $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) );
2115                                $link_str .= "?filename=";
2116                                $link_str .= get_relative_path( $filename, dirname(__FILE__) );
2117
2118                                // Add thumbnail link to html
2119                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><a class=\"EXIF_First_IFD_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_First_IFD_Thumb\" src=\"$link_str\"></a></td></tr>\n";
2120                        }
2121                                // Check if this is the Makernote
2122                        else if ( $Exif_Tag['Type'] == "Maker Note" )
2123                        {
2124                                // This is the makernote, but has not been decoded
2125                                // Add a message to the secondary output
2126                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Makernote Coding Unknown</h3>\n";
2127                        }
2128                        else
2129                        {
2130                                // This is an Unknown Tag
2131
2132                                // Check if the user wants to hide unknown tags
2133                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE )
2134                                {
2135                                        // User wants to display unknown tags
2136                                    $output_str .= OutPut_exif($tabval);
2137
2138                                }
2139                        }
2140                }
2141        }
2142
2143        // Close the table in the output
2144        $output_str .= "</table>\n</fieldset>";
2145
2146        // Add the secondary output at the end of the main output
2147        $output_str .= "$extra_IFD_str";
2148
2149        // Return the resulting html
2150        return $output_str;
2151}
2152
2153/******************************************************************************
2154* End of Function:     interpret_IFD
2155******************************************************************************/
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171/******************************************************************************
2172*
2173* Function:     get_IFD_Data_Type
2174*
2175* Description:  Decodes an IFD field value from a binary data string, using
2176*               information supplied about the data type and byte alignment of
2177*               the stored data.
2178*               This function should be used for all datatypes except ASCII strings
2179*
2180* Parameters:   input_data - a binary data string containing the IFD value,
2181*                            must be exact length of the value
2182*               data_type - a number representing the IFD datatype as per the
2183*                           TIFF 6.0 specification:
2184*                               1 = Unsigned 8-bit Byte
2185*                               2 = ASCII String
2186*                               3 = Unsigned 16-bit Short
2187*                               4 = Unsigned 32-bit Long
2188*                               5 = Unsigned 2x32-bit Rational
2189*                               6 = Signed 8-bit Byte
2190*                               7 = Undefined
2191*                               8 = Signed 16-bit Short
2192*                               9 = Signed 32-bit Long
2193*                               10 = Signed 2x32-bit Rational
2194*                               11 = 32-bit Float
2195*                               12 = 64-bit Double
2196*               Byte_Align - Indicates the byte alignment of the data.
2197*                            MM = Motorola, MSB first, Big Endian
2198*                            II = Intel, LSB first, Little Endian
2199*
2200* Returns:      output - the value of the data (string or numeric)
2201*
2202******************************************************************************/
2203
2204function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
2205{
2206        // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long
2207        if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 ))
2208        {
2209                // This is a Unsigned Byte, Unsigned Short or Unsigned Long
2210
2211                // Check the byte alignment to see if the bytes need tp be reversed
2212                if ( $Byte_Align == "II" )
2213                {
2214                        // This is in Intel format, reverse it
2215                        $input_data = strrev ( $input_data );
2216                }
2217
2218                // Convert the binary string to a number and return it
2219                return hexdec( bin2hex( $input_data ) );
2220        }
2221                // Check if this is a ASCII string type
2222        elseif ( $data_type == 2 )
2223        {
2224                // Null terminated ASCII string(s)
2225                // The input data may represent multiple strings, as the
2226                // 'count' field represents the total bytes, not the number of strings
2227                // Hence this should not be processed here, as it would have
2228                // to return multiple values instead of a single value
2229
2230                echo "<p>Error - ASCII Strings should not be processed in get_IFD_Data_Type</p>\n";
2231                return "Error Should never get here"; //explode( "\x00", $input_data );
2232        }
2233                // Check if this is a Unsigned rational type
2234        elseif ( $data_type == 5 )
2235        {
2236                // This is a Unsigned rational type
2237
2238                // Check the byte alignment to see if the bytes need to be reversed
2239                if ( $Byte_Align == "MM" )
2240                {
2241                        // Motorola MSB first byte aligment
2242                        // Unpack the Numerator and denominator and return them
2243                        return unpack( 'NNumerator/NDenominator', $input_data );
2244                }
2245                else
2246                {
2247                        // Intel LSB first byte aligment
2248                        // Unpack the Numerator and denominator and return them
2249                        return unpack( 'VNumerator/VDenominator', $input_data );
2250                }
2251        }
2252                // Check if this is a Signed Byte, Signed Short or Signed Long
2253        elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) )
2254        {
2255                // This is a Signed Byte, Signed Short or Signed Long
2256
2257                // Check the byte alignment to see if the bytes need to be reversed
2258                if ( $Byte_Align == "II" )
2259                {
2260                        //Intel format, reverse the bytes
2261                        $input_data = strrev ( $input_data );
2262                }
2263
2264                // Convert the binary string to an Unsigned number
2265                $value = hexdec( bin2hex( $input_data ) );
2266
2267                // Convert to signed number
2268
2269                // Check if it is a Byte above 128 (i.e. a negative number)
2270                if ( ( $data_type == 6 ) && ( $value > 128 ) )
2271                {
2272                        // number should be negative - make it negative
2273                        return  $value - 256;
2274                }
2275
2276                // Check if it is a Short above 32767 (i.e. a negative number)
2277                if ( ( $data_type == 8 ) && ( $value > 32767 ) )
2278                {
2279                        // number should be negative - make it negative
2280                        return  $value - 65536;
2281                }
2282
2283                // Check if it is a Long above 2147483648 (i.e. a negative number)
2284                if ( ( $data_type == 9 ) && ( $value > 2147483648 ) )
2285                {
2286                        // number should be negative - make it negative
2287                        return  $value - 4294967296;
2288                }
2289
2290                // Return the signed number
2291                return $value;
2292        }
2293                // Check if this is Undefined type
2294        elseif ( $data_type == 7 )
2295        {
2296                // Custom Data - Do nothing
2297                return $input_data;
2298        }
2299                // Check if this is a Signed Rational type
2300        elseif ( $data_type == 10 )
2301        {
2302                // This is a Signed Rational type
2303
2304                // Signed Long not available with endian in unpack , use unsigned and convert
2305
2306                // Check the byte alignment to see if the bytes need to be reversed
2307                if ( $Byte_Align == "MM" )
2308                {
2309                        // Motorola MSB first byte aligment
2310                        // Unpack the Numerator and denominator
2311                        $value = unpack( 'NNumerator/NDenominator', $input_data );
2312                }
2313                else
2314                {
2315                        // Intel LSB first byte aligment
2316                        // Unpack the Numerator and denominator
2317                        $value = unpack( 'VNumerator/VDenominator', $input_data );
2318                }
2319
2320                // Convert the numerator to a signed number
2321                // Check if it is above 2147483648 (i.e. a negative number)
2322                if ( $value['Numerator'] > 2147483648 )
2323                {
2324                        // number is negative
2325                        $value['Numerator'] -= 4294967296;
2326                }
2327
2328                // Convert the denominator to a signed number
2329                // Check if it is above 2147483648 (i.e. a negative number)
2330                if ( $value['Denominator'] > 2147483648 )
2331                {
2332                        // number is negative
2333                        $value['Denominator'] -= 4294967296;
2334                }
2335
2336                // Return the Signed Rational value
2337                return $value;
2338        }
2339                // Check if this is a Float type
2340        elseif ( $data_type == 11 )
2341        {
2342                // IEEE 754 Float
2343                // TODO - EXIF - IFD datatype Float not implemented yet
2344                return "FLOAT NOT IMPLEMENTED YET";
2345        }
2346                // Check if this is a Double type
2347        elseif ( $data_type == 12 )
2348        {
2349                // IEEE 754 Double
2350                // TODO - EXIF - IFD datatype Double not implemented yet
2351                return "DOUBLE NOT IMPLEMENTED YET";
2352        }
2353        else
2354        {
2355                // Error - Invalid Datatype
2356                return "Invalid Datatype $data_type";
2357
2358        }
2359
2360}
2361
2362/******************************************************************************
2363* End of Function:     get_IFD_Data_Type
2364******************************************************************************/
2365
2366
2367
2368
2369
2370
2371/******************************************************************************
2372*
2373* Function:     put_IFD_Data_Type
2374*
2375* Description:  Encodes an IFD field from a value to a binary data string, using
2376*               information supplied about the data type and byte alignment of
2377*               the stored data.
2378*
2379* Parameters:   input_data - an IFD data value, numeric or string
2380*               data_type - a number representing the IFD datatype as per the
2381*                           TIFF 6.0 specification:
2382*                               1 = Unsigned 8-bit Byte
2383*                               2 = ASCII String
2384*                               3 = Unsigned 16-bit Short
2385*                               4 = Unsigned 32-bit Long
2386*                               5 = Unsigned 2x32-bit Rational
2387*                               6 = Signed 8-bit Byte
2388*                               7 = Undefined
2389*                               8 = Signed 16-bit Short
2390*                               9 = Signed 32-bit Long
2391*                               10 = Signed 2x32-bit Rational
2392*                               11 = 32-bit Float
2393*                               12 = 64-bit Double
2394*               Byte_Align - Indicates the byte alignment of the data.
2395*                            MM = Motorola, MSB first, Big Endian
2396*                            II = Intel, LSB first, Little Endian
2397*
2398* Returns:      output - the packed binary string of the data
2399*
2400******************************************************************************/
2401
2402function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
2403{
2404        // Process according to the datatype
2405        switch ( $data_type )
2406        {
2407                case 1: // Unsigned Byte - return character as is
2408                        return chr($input_data);
2409                        break;
2410
2411                case 2: // ASCII String
2412                        // Return the string with terminating null
2413                        return $input_data . "\x00";
2414                        break;
2415
2416                case 3: // Unsigned Short
2417                        // Check byte alignment
2418                        if ( $Byte_Align == "II" )
2419                        {
2420                                // Intel/Little Endian - pack the short and return
2421                                return pack( "v", $input_data );
2422                        }
2423                        else
2424                        {
2425                                // Motorola/Big Endian - pack the short and return
2426                                return pack( "n", $input_data );
2427                        }
2428                        break;
2429
2430                case 4: // Unsigned Long
2431                        // Check byte alignment
2432                        if ( $Byte_Align == "II" )
2433                        {
2434                                // Intel/Little Endian - pack the long and return
2435                                return pack( "V", $input_data );
2436                        }
2437                        else
2438                        {
2439                                // Motorola/Big Endian - pack the long and return
2440                                return pack( "N", $input_data );
2441                        }
2442                        break;
2443
2444                case 5: // Unsigned Rational
2445                        // Check byte alignment
2446                        if ( $Byte_Align == "II" )
2447                        {
2448                                // Intel/Little Endian - pack the two longs and return
2449                                return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
2450                        }
2451                        else
2452                        {
2453                                // Motorola/Big Endian - pack the two longs and return
2454                                return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
2455                        }
2456                        break;
2457
2458                case 6: // Signed Byte
2459                        // Check if number is negative
2460                        if ( $input_data < 0 )
2461                        {
2462                                // Number is negative - return signed character
2463                                return chr( $input_data + 256 );
2464                        }
2465                        else
2466                        {
2467                                // Number is positive - return character
2468                                return chr( $input_data );
2469                        }
2470                        break;
2471
2472                case 7: // Unknown - return as is
2473                        return $input_data;
2474                        break;
2475
2476                case 8: // Signed Short
2477                        // Check if number is negative
2478                        if (  $input_data < 0 )
2479                        {
2480                                // Number is negative - make signed value
2481                                $input_data = $input_data + 65536;
2482                        }
2483                        // Check byte alignment
2484                        if ( $Byte_Align == "II" )
2485                        {
2486                                // Intel/Little Endian - pack the short and return
2487                                return pack( "v", $input_data );
2488                        }
2489                        else
2490                        {
2491                                // Motorola/Big Endian - pack the short and return
2492                                return pack( "n", $input_data );
2493                        }
2494                        break;
2495
2496                case 9: // Signed Long
2497                        // Check if number is negative
2498                        if (  $input_data < 0 )
2499                        {
2500                                // Number is negative - make signed value
2501                                $input_data = $input_data + 4294967296;
2502                        }
2503                        // Check byte alignment
2504                        if ( $Byte_Align == "II" )
2505                        {
2506                                // Intel/Little Endian - pack the long and return
2507                                return pack( "v", $input_data );
2508                        }
2509                        else
2510                        {
2511                                // Motorola/Big Endian - pack the long and return
2512                                return pack( "n", $input_data );
2513                        }
2514                        break;
2515
2516                case 10: // Signed Rational
2517                        // Check if numerator is negative
2518                        if (  $input_data['Numerator'] < 0 )
2519                        {
2520                                // Number is numerator - make signed value
2521                                $input_data['Numerator'] = $input_data['Numerator'] + 4294967296;
2522                        }
2523                        // Check if denominator is negative
2524                        if (  $input_data['Denominator'] < 0 )
2525                        {
2526                                // Number is denominator - make signed value
2527                                $input_data['Denominator'] = $input_data['Denominator'] + 4294967296;
2528                        }
2529                        // Check byte alignment
2530                        if ( $Byte_Align == "II" )
2531                        {
2532                                // Intel/Little Endian - pack the two longs and return
2533                                return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
2534                        }
2535                        else
2536                        {
2537                                // Motorola/Big Endian - pack the two longs and return
2538                                return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
2539                        }
2540                        break;
2541
2542                case 11: // Float
2543                        // IEEE 754 Float
2544                        // TODO - EXIF - IFD datatype Float not implemented yet
2545                        return "FLOAT NOT IMPLEMENTED YET";
2546                        break;
2547
2548                case 12: // Double
2549                        // IEEE 754 Double
2550                        // TODO - EXIF - IFD datatype Double not implemented yet
2551                        return "DOUBLE NOT IMPLEMENTED YET";
2552                        break;
2553
2554                default:
2555                        // Error - Invalid Datatype
2556                        return "Invalid Datatype $data_type";
2557                        break;
2558
2559        }
2560
2561        // Shouldn't get here
2562        return FALSE;
2563}
2564
2565/******************************************************************************
2566* End of Function:     put_IFD_Data_Type
2567******************************************************************************/
2568
2569
2570
2571
2572
2573/******************************************************************************
2574*
2575* Function:     get_IFD_value_as_text
2576*
2577* Description:  Decodes an IFD field value from a binary data string, using
2578*               information supplied about the data type and byte alignment of
2579*               the stored data.
2580*               This function should be used for all datatypes except ASCII strings
2581*
2582* Parameters:   input_data - a binary data string containing the IFD value,
2583*                            must be exact length of the value
2584*               data_type - a number representing the IFD datatype as per the
2585*                           TIFF 6.0 specification:
2586*                               1 = Unsigned 8-bit Byte
2587*                               2 = ASCII String
2588*                               3 = Unsigned 16-bit Short
2589*                               4 = Unsigned 32-bit Long
2590*                               5 = Unsigned 2x32-bit Rational
2591*                               6 = Signed 8-bit Byte
2592*                               7 = Undefined
2593*                               8 = Signed 16-bit Short
2594*                               9 = Signed 32-bit Long
2595*                               10 = Signed 2x32-bit Rational
2596*                               11 = 32-bit Float
2597*                               12 = 64-bit Double
2598*               Byte_Align - Indicates the byte alignment of the data.
2599*                            MM = Motorola, MSB first, Big Endian
2600*                            II = Intel, LSB first, Little Endian
2601*
2602* Returns:      output - the value of the data (string or numeric)
2603*
2604******************************************************************************/
2605
2606function get_IFD_value_as_text( $Exif_Tag )
2607{
2608        // Create a string to receive the output text
2609        $output_str = "";
2610
2611        // Select Processing method according to the datatype
2612        switch  ($Exif_Tag['Data Type'])
2613        {
2614                case 1 : // Unsigned Byte
2615                case 3 : // Unsigned Short
2616                case 4 : // Unsigned Long
2617                case 6 : // Signed Byte
2618                case 8 : // Signed Short
2619                case 9 : // Signed Long
2620
2621                        // Cycle through each of the values for this tag
2622                        foreach ( $Exif_Tag['Data'] as $val )
2623                        {
2624                                // Check that this isn't the first value,
2625                                if ( $output_str != "" )
2626                                {
2627                                        // This isn't the first value, Add a Comma and Newline to the output
2628                                        $output_str .= ",\n";
2629                                }
2630                                // Add the Value to the output
2631                                $output_str .= $val;
2632                        }
2633                        break;
2634
2635                case 2 : // ASCII
2636                        // Append all the strings together, separated by Newlines
2637                        $output_str .= implode ( "\n", $Exif_Tag['Data']);
2638                        break;
2639
2640                case 5 : // Unsigned Rational
2641                case 10: // Signed Rational
2642
2643                        // Cycle through each of the values for this tag
2644                        foreach ( $Exif_Tag['Data'] as $val )
2645                        {
2646                                // Check that this isn't the first value,
2647                                if ( $output_str != "" )
2648                                {
2649                                        // This isn't the first value, Add a Comma and Newline to the output
2650                                        $output_str .= ",\n";
2651                                }
2652
2653                                // Add the Full Value to the output
2654                                $output_str .= $val['Numerator'] ."/" . $val['Denominator'];
2655
2656                                // Check if division by zero might be a problem
2657                                if ( $val['Denominator'] != 0 )
2658                                {
2659                                        // Denominator is not zero, Add the Decimal Value to the output text
2660                                        $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")";
2661                                }
2662                        }
2663                        break;
2664
2665                case 11: // Float
2666                case 12: // Double
2667                        // TODO - EXIF - IFD datatype Double and Float not implemented yet
2668                        $output_str .= "Float and Double not implemented yet";
2669                        break;
2670
2671                case 7 : // Undefined
2672                        // Unless the User has asked to see the raw binary data, this
2673                        // type should not be displayed
2674
2675                        // Check if the user has requested to see the binary data in hex
2676                        if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE)
2677                        {
2678                                // User has requested to see the binary data in hex
2679                                // Add the value in hex
2680                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] )  ;
2681                        }
2682                                // Check if the user has requested to see the binary data as is
2683                        else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE)
2684                        {
2685                                // User has requested to see the binary data as is
2686                                // Add the value as is
2687                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data']  ;
2688                        }
2689                        else
2690                        {
2691                                // User has NOT requested to see binary data,
2692                                // Add a message indicating the number of bytes to the output
2693                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) "  ;
2694                        }
2695                        break;
2696
2697                default :
2698                        // Error - Unknown IFD datatype
2699                        $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid";
2700                        break;
2701        }
2702
2703        // Return the resulting text string
2704        return $output_str;
2705}
2706
2707/******************************************************************************
2708* End of Function:     get_IFD_value_as_text
2709******************************************************************************/
2710
2711
2712
2713
2714/******************************************************************************
2715* Global Variable:      IFD_Data_Sizes
2716*
2717* Contents:     The sizes (in bytes) of each EXIF IFD Datatype, indexed by
2718*               their datatype number
2719*
2720******************************************************************************/
2721
2722$GLOBALS['IFD_Data_Sizes'] = array(     1 => 1,         // Unsigned Byte
2723                                        2 => 1,         // ASCII String
2724                                        3 => 2,         // Unsigned Short
2725                                        4 => 4,         // Unsigned Long
2726                                        5 => 8,         // Unsigned Rational
2727                                        6 => 1,         // Signed Byte
2728                                        7 => 1,         // Undefined
2729                                        8 => 2,         // Signed Short
2730                                        9 => 4,         // Signed Long
2731                                        10 => 8,        // Signed Rational
2732                                        11 => 4,        // Float
2733                                        12 => 8 );      // Double
2734
2735/******************************************************************************
2736* End of Global Variable:     IFD_Data_Sizes
2737******************************************************************************/
2738
2739
2740function OutPut_exif($tabval) { 
2741
2742 $retour = "<tr align='left' ><td>".$tabval['Type_src'] ."</td><td style='border:solid red 2px' >" .$tabval['Tag Name']. "</td><td><input size=90 name='" .$tabval['Tag Name']."' id='" .$tabval['Tag Name']. "' type='text' style='border:solid green 2px' value=' ". trim( $tabval['Text Value'] ) . "'/></td></tr>";
2743 
2744 return $retour;
2745  } 
2746?>
Note: See TracBrowser for help on using the repository browser.