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

Last change on this file since 20035 was 12126, checked in by cljosse, 13 years ago

[extensions] edit_gmaps add functions

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