Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
6 kaklik 1
<?php
2
/*************************
3
  Coppermine Photo Gallery
4
  ************************
5
  Copyright (c) 2003-2005 Coppermine Dev Team
6
  v1.1 originaly written by Gregory DEMAR
7
 
8
  This program is free software; you can redistribute it and/or modify
9
  it under the terms of the GNU General Public License as published by
10
  the Free Software Foundation; either version 2 of the License, or
11
  (at your option) any later version.
12
  ********************************************
13
  Coppermine version: 1.3.3
14
  $Source: /cvsroot/coppermine/stable/include/exifReader.inc.php,v $
15
  $Revision: 1.4 $
16
  $Author: gaugau $
17
  $Date: 2005/04/19 03:17:11 $
18
**********************************************/
19
 
20
/**
21
 * PHP Class to read EXIF information
22
 * that most of the digital camera produce
23
 *
24
 * This class is based on jhead (in C) by Matthias Wandel
25
 *
26
 * Vinay Yadav (vinayRas) < vinay@sanisoft.com >
27
 * http://www.sanisoft.com/phpexifrw/
28
 *
29
 * For more information on EXIF
30
 * http://www.exif.org/
31
 *
32
 * Features:
33
 *   - Read Exif Information
34
 *   - Extract and display emdedded thumbnails
35
 *
36
 *  Tested With
37
 
38
        - Sony
39
            - Cybershot (Sony)
40
            - DSC-D700
41
            - PowerShotA5
42
        - SANYO Electric Co.,Ltd
43
            - SR6
44
            - SX113
45
        - OLYMPUS OPTICAL CO.,LTD
46
            - C960Z,D460Z
47
        - Canon
48
            PowerShot A40 (Canon)
49
            Canon DIGITAL IXUS
50
        - RICOH
51
            - Caplio RR30
52
            - RDC-5300
53
        - NIKON
54
            - D100 (NIKON CORPORATION)
55
            - E5700 (NIKON)
56
            - E950
57
        - CASIO QV-8000SX
58
        - KODAK
59
            - DC290 Zoom Digital Camera (V01.00) [Eastman Kodak Company]
60
            - DC210 Zoom (V05.00) [Eastman Kodak Company]
61
            - KODAK DC240 ZOOM DIGITAL CAMERA
62
        - FujiFilm
63
            DX10
64
            FinePix40i
65
            MX-1700ZOOM
66
 *
67
 *
68
 */
69
 
70
/** * Start Of Frame N */
71
define("M_SOF0",0xC0);
72
/** * N indicates which compression process */
73
define("M_SOF1",0xC1);
74
/** * Only SOF0-SOF2 are now in common use */
75
define("M_SOF2",0xC2);
76
/** *  */
77
define("M_SOF3",0xC3);
78
/** * NB: codes C4 and CC are NOT SOF markers */
79
define("M_SOF5",0xC5);
80
/** *  */
81
define("M_SOF6",0xC6);
82
/** *  */
83
define("M_SOF7",0xC7);
84
/** *  */
85
define("M_SOF9",0xC9);
86
/** *  */
87
define("M_SOF10",0xCA);
88
/** *  */
89
define("M_SOF11",0xCB);
90
/** *  */
91
define("M_SOF13",0xCD);
92
/** *  */
93
define("M_SOF14",0xCE);
94
/** *  */
95
define("M_SOF15",0xCF);
96
/** * Start Of Image (beginning of datastream) */
97
define("M_SOI",0xD8);
98
/** * End Of Image (end of datastream) */
99
define("M_EOI",0xD9);
100
/** * Start Of Scan (begins compressed data) */
101
define("M_SOS",0xDA);
102
/** * Jfif marker */
103
define("M_JFIF",0xE0);
104
/** * Exif marker */
105
define("M_EXIF",0xE1);
106
/** * Image Title -- */
107
define("M_COM",0xFE);
108
 
109
define("NUM_FORMATS","12");
110
 
111
/** * Tag Data Format */
112
define("FMT_BYTE","1");
113
/** * ASCII */
114
define("FMT_STRING","2");
115
/** * Short */
116
define("FMT_USHORT","3");
117
/** * Long */
118
define("FMT_ULONG","4");
119
/** * Rational */
120
define("FMT_URATIONAL","5");
121
/** * Byte */
122
define("FMT_SBYTE","6");
123
/** * Undefined */
124
define("FMT_UNDEFINED","7");
125
/** * Short */
126
define("FMT_SSHORT","8");
127
/** * Long */
128
define("FMT_SLONG","9");
129
/** * Rational */
130
define("FMT_SRATIONAL","10");
131
/** * Single */
132
define("FMT_SINGLE","11");
133
/** * Double */
134
define("FMT_DOUBLE","12");
135
 
136
/** * Exif IFD */
137
define("TAG_EXIF_OFFSET","0x8769");
138
/** * Interoperability tag */
139
define("TAG_INTEROP_OFFSET","0xa005");
140
/** * Image input equipment manufacturer */
141
define("TAG_MAKE","0x010F");
142
/** * Image input equipment model */
143
define("TAG_MODEL","0x0110");
144
/** * Orientation of image */
145
define("TAG_ORIENTATION","0x0112");
146
/** * Exposure Time */
147
define("TAG_EXPOSURETIME","0x829A");
148
/** * F Number */
149
define("TAG_FNUMBER","0x829D");
150
/** * Shutter Speed */
151
define("TAG_SHUTTERSPEED","0x9201");
152
/** * Aperture */
153
define("TAG_APERTURE","0x9202");
154
/** * Aperture */
155
define("TAG_MAXAPERTURE","0x9205");
156
/** * Lens Focal Length */
157
define("TAG_FOCALLENGTH","0x920A");
158
/** * The date and time when the original image data was generated. */
159
define("TAG_DATETIME_ORIGINAL","0x9003");
160
/** * User Comments */
161
define("TAG_USERCOMMENT","0x9286");
162
/** * subject Location */
163
define("TAG_SUBJECT_DISTANCE","0x9206");
164
/** * Flash */
165
define("TAG_FLASH","0x9209");
166
/** * Focal Plane X Resolution */
167
define("TAG_FOCALPLANEXRES","0xa20E");
168
/** * Focal Plane Resolution Units */
169
define("TAG_FOCALPLANEUNITS","0xa210");
170
/** * Image Width */
171
define("TAG_EXIF_IMAGEWIDTH","0xA002");
172
/** * Image Height */
173
define("TAG_EXIF_IMAGELENGTH","0xA003");
174
/** * Exposure Bias */
175
define("TAG_EXPOSURE_BIAS","0x9204");
176
/** * Light Source */
177
define("TAG_WHITEBALANCE","0x9208");
178
/** * Metering Mode */
179
define("TAG_METERING_MODE","0x9207");
180
/** * Exposure Program */
181
define("TAG_EXPOSURE_PROGRAM","0x8822");
182
/** * ISO Equivalent Speed Rating */
183
define("TAG_ISO_EQUIVALENT","0x8827");
184
/** * Compressed Bits Per Pixel */
185
define("TAG_COMPRESSION_LEVEL","0x9102");
186
/** * Thumbnail Start Offset */
187
define("TAG_THUMBNAIL_OFFSET","0x0201");
188
/** * Thumbnail Length */
189
define("TAG_THUMBNAIL_LENGTH","0x0202");
190
/** * Image Marker */
191
define("PSEUDO_IMAGE_MARKER",0x123);
192
/** * Max Image Title Length */
193
define("MAX_COMMENT",2000);
194
 
195
define("TAG_ARTIST","0x013B");
196
define("TAG_COPYRIGHT","0x8298");
197
 
198
//--------------------------------
199
 
200
define("TAG_IMAGE_WD","0x0100"); // image width
201
define("TAG_IMAGE_HT","0x0101"); // image height
202
define("TAG_IMAGE_BPS","0x0102"); // Bits Per sample
203
 
204
define("TAG_IMAGE_PHOTO_INT","0x0106"); // photometricinterpretation
205
define("TAG_IMAGE_SOFFSET","0x0111"); // stripoffsets
206
 
207
define("TAG_IMAGE_SPP","0x0115"); // Samples per pixel - 277
208
define("TAG_IMAGE_RPS","0x0116"); // RowsPerStrip - 278
209
define("TAG_IMAGE_SBC","0x0117"); // StripByteCounts - 279
210
 
211
define("TAG_IMAGE_P_CONFIG","0x011C"); // Planar Configuration - 284
212
 
213
define("TAG_IMAGE_DESC","0x010E"); // image title
214
define("TAG_X_RESOLUTION","0x011A"); // Image resolution in width direction
215
define("TAG_Y_RESOLUTION","0x011B"); // Image resolution in height direction
216
define("TAG_RESOLUTION_UNIT","0x0128"); // Unit of X and Y resolution
217
define("TAG_SOFTWARE","0x0131"); // Software used
218
define("TAG_FILE_MODDATE","0x0132"); // DateTime File change date and time
219
define("TAG_YCBCR_POSITIONING","0x0213"); // Y and C positioning
220
define("TAG_EXIF_VERSION","0x9000"); // Exif version
221
define("TAG_DATE_TIME_DIGITIZED","0x9004"); // Date and time of digital data
222
define("TAG_COMPONENT_CONFIG","0x9101"); // Component configuration
223
define("TAG_MAKER_NOTE","0x927C");
224
define("TAG_SUB_SEC_TIME","0x9290");
225
define("TAG_SUB_SEC_TIME_ORIG","0x9291");
226
define("TAG_SUB_SEC_TIME_DIGITIZED","0x9292");
227
define("TAG_FLASHPIX_VER","0xA000"); //FlashPixVersion
228
define("TAG_COLOR_SPACE","0xA001"); //ColorSpace
229
define("TAG_RELATED_SOUND_FILE","0xA004"); //Related audio file
230
 
231
define("TAG_GPS_LATITUDE_REF","0x0001"); //
232
define("TAG_GPS_LATITUDE","0x0002"); //
233
 
234
define("TAG_SENSING_METHOD","0xA217"); // SensingMethod
235
 
236
define("TAG_SOUCE_TYPE","0xA300"); // FileSource
237
define("TAG_SCENE_TYPE","0xA301"); // SceneType
238
 
239
define("TAG_CFA_PATTERN","0xA302"); // CFA Pattern
240
 
241
/** Tags in EXIF 2.2 Only */
242
define("TAG_COMPRESS_SCHEME","0x0103"); //
243
define("TAG_CUSTOM_RENDERED","0xA401"); //  CustomRendered
244
define("TAG_EXPOSURE_MODE","0xA402"); // Exposure mode      ExposureMode
245
define("TAG_WHITE_BALANCE","0xA403"); // White balance      WhiteBalance
246
define("TAG_DIGITAL_ZOOM_RATIO","0xA404"); // Digital zoom ratio      DigitalZoomRatio
247
define("TAG_FLENGTH_IN35MM","0xA405"); // Focal length in 35 mm film      FocalLengthIn35mmFilm
248
define("TAG_SCREEN_CAP_TYPE","0xA406"); // Scene capture type      SceneCaptureType
249
define("TAG_GAIN_CONTROL","0xA407"); //Gain control
250
define("TAG_CONTRAST","0xA408"); // Contrast
251
define("TAG_SATURATION","0xA409"); // Saturation
252
define("TAG_SHARPNESS","0xA40A"); // Sharpness
253
define("TAG_DEVICE_SETTING_DESC","0xA40B"); // SDevice settings description      DeviceSettingDescription
254
define("TAG_DIST_RANGE","0xA40C"); //Subject distance range SubjectDistanceRange
255
 
256
define("TAG_FOCALPLANE_YRESOL","0xA20F");; //FocalPlaneYResolution
257
define("TAG_BRIGHTNESS","0x9203");; //Brightness
258
//--------------------------------
259
/** error Description  */
260
/**
261
  1 - File does not exists!
262
  2 -
263
  3 - Filename not provided
264
 
265
  10 - too many padding bytes
266
  11 - "invalid marker"
267
  12 - Premature end of file?
268
 
269
 
270
  51 - "Illegal subdirectory link"
271
  52 - "NOT EXIF FORMAT"
272
  53 - "Invalid Exif alignment marker.\n"
273
  54 - "Invalid Exif start (1)"
274
 
275
*/
276
 
277
 
278
/**
279
 * PHP Class to read, write and transfer EXIF information
280
 * that most of the digital camera produces
281
 * Currenty it can only read JPEG file.
282
 */
283
 /**
284
 * @author Vinay Yadav (vinayRas) < vinay@sanisoft.com >
285
 *
286
 * @todo Writing exif information to the file.
287
 * @todo Add EXIF audio reading methods (I think it exists!)
288
 * @todo Support of additional tags.
289
 * @todo Handling Unicode character in UserComment tag of EXif Information.
290
 *
291
 * @version 0.5
292
 * @licence http://opensource.org/licenses/lgpl-license.php GNU LGPL
293
 */
294
class phpExifReader {
295
 
296
    /***
297
    * Array containg all Exif and JPEG image attributes
298
    * into regular expressions for themselves.
299
    * $ImageInfo[TAG] = TAG_VALUE;
300
    *
301
    * @var       array
302
    * @access    private
303
    *
304
    */
305
    var $ImageInfo = array();
306
 
307
    var $MotorolaOrder = 0;
308
    var $ExifImageWidth = 0; //
309
    var $FocalplaneXRes = 0; //
310
    var $FocalplaneUnits = 0; //
311
    var $sections = array();
312
    var $currSection = 0;  /** Stores total number fo Sections */
313
 
314
    var $BytesPerFormat = array(0,1,1,2,4,8,1,1,2,4,8,4,8);
315
 
316
    var $ReadMode = array(
317
                            "READ_EXIF" => 1,
318
                            "READ_IMAGE" => 2,
319
                            "READ_ALL" => 3
320
                        );
321
 
322
    var $ImageReadMode = 3; /** related to $RealMode arrays values */
323
    var $file =  "";     /** JPEG file to parse for EXIF data */
324
    var $newFile = 1;   /** flag to check if the current file has been parsed or not. */
325
 
326
    var $thumbnail = ""; /* Name of thumbnail */
327
    var $thumbnailURL = ""; /* */
328
 
329
    var $exifSection = -1;   // mark the exif section index out of all sections
330
 
331
    var $errno = 0;
332
    var $errstr = "";
333
 
334
    var $debug = false;
335
 
336
    // Caching ralated variables
337
    var $caching = false; /* Should cacheing of image thumnails be allowed? */
338
    var $cacheDir = ""; /* Checkout constructor for default path. */
339
 
340
    /**
341
     * Constructor
342
     * @param string File name to be parsed.
343
     *
344
     */
345
    function phpExifReader($file = "") {
346
      $this->timeStart = $this->cpgGetMicroTime();
347
      if(!empty($file)) {
348
        $this->file = $file;
349
      }
350
 
351
      /**
352
      * Initialize some variables. Avoid lots of errors with fulll error_reporting
353
      */
354
      $this->ExifImageLength       = 0;
355
      $this->ImageInfo['h']["resolutionUnit"] = 0;
356
 
357
      $this->ImageInfo[TAG_MAXAPERTURE] = 0;
358
      $this->ImageInfo[TAG_ISO_EQUIVALENT] = 0;
359
      $this->ImageInfo[TAG_ORIENTATION] = 0;
360
 
361
      $this->ThumbnailSize = 0;
362
 
363
      if($this->caching) {
364
        $this->cacheDir = dirname(__FILE__)."/.cache_thumbs";
365
 
366
        /**
367
        * If Cache directory does not exists then attempt to create it.
368
        */
369
        if(!is_dir($this->cacheDir)) {
370
             mkdir($this->cacheDir);
371
        }
372
 
373
          // Prepare the ame of thumbnail
374
          if(is_dir($this->cacheDir)) {
375
            $this->thumbnail = $this->cacheDir."/".basename($this->file);
376
            $this->thumbnailURL = ".cache_thumbs/".basename($this->file);
377
          }
378
      }
379
 
380
      /** check if file exists! */
381
      if(!file_exists($this->file)) {
382
         $this->errno = 1;
383
         $this->errstr = "File '".$this->file."' does not exists!";
384
      }
385
      $this->currSection = 0;
386
 
387
      $this->processFile();
388
    }
389
 
390
    /**
391
     * Show Debugging information
392
     *
393
     * @param   string     Debugging message to display
394
     * @param   int   Type of error (0 - Warning, 1 - Error)
395
     * @return    void
396
     *
397
     */
398
    function debug($str,$TYPE = 0,$file="",$line=0) {
399
       if($this->debug) {
400
        echo "<br>[$file:$line:".($this->getDiffTime())."]$str";
401
        flush();
402
        if($TYPE == 1) {
403
           exit;
404
        }
405
       }
406
    }
407
 
408
    /**
409
     * Processes the whole file.
410
     *
411
     */
412
    function processFile() {
413
        /** dont reparse the whole file. */
414
        if(!$this->newFile) return true;
415
 
416
        if(!file_exists($this->file)) {
417
            echo "<br>Error: File ".($this->file)."does not exists!";
418
            exit;
419
        }
420
 
421
        $this->debug("Stating Processing of ".$this->newFile,0,__FILE__,__LINE__);
422
 
423
        $i = 0; $exitAll = 0;
424
        /** Open the JPEG in binary safe reading mode */
425
        $fp = fopen($this->file,"rb");
426
 
427
        $this->ImageInfo["h"]["FileName"] = $this->file;
428
        $this->ImageInfo["h"]["FileSize"] = filesize($this->file); /** Size of the File */
429
        $this->ImageInfo["h"]["FileDateTime"] = filectime($this->file); /** File node change time */
430
 
431
        /** check whether jped image or not */
432
        $a = fgetc($fp);
433
        if (ord($a) != 0xff || ord(fgetc($fp)) != M_SOI){
434
                $this->debug("Not a JPEG FILE",1);
435
                $this->errorno = 1;
436
                $this->errorstr = "File '".$this->file."' is not a JPEG File!";
437
        }
438
        $tmpTestLevel = 0;
439
        /** Examines each byte one-by-one */
440
        while(!feof($fp)) {
441
            $data = array();
442
                for ($a=0;$a<7;$a++){
443
                        $marker = fgetc($fp);
444
                        if (ord($marker) != 0xff) break;
445
                        if ($a >= 6){
446
                                $this->errno = 10;
447
                                $this->errstr = "too many padding bytes!";
448
                                $this->debug($this->errstr,1);
449
                                return false;
450
                        }
451
                }
452
 
453
                if (ord($marker) == 0xff){
454
                    // 0xff is legal padding, but if we get that many, something's wrong.
455
                    $this->errno = 10;
456
                    $this->errstr = "too many padding bytes!";
457
                    $this->debug($this->errstr,1);
458
                }
459
 
460
        $marker = ord($marker);
461
        $this->sections[$this->currSection]["type"] = $marker;
462
 
463
        // Read the length of the section.
464
        $lh = ord(fgetc($fp));
465
        $ll = ord(fgetc($fp));
466
 
467
        $itemlen = ($lh << 8) | $ll;
468
 
469
        if ($itemlen < 2){
470
                $this->errno = 11;
471
                $this->errstr = "invalid marker";
472
                $this->debug($this->errstr,1);
473
        }
474
        $this->sections[$this->currSection]["size"] = $itemlen;
475
 
476
        $tmpDataArr = array();  /** Temporary Array */
477
 
478
        $tmpStr = fread($fp,$itemlen-2);
479
        /*
480
        $tmpDataArr[] = chr($lh);
481
        $tmpDataArr[] = chr($ll);
482
 
483
        $chars = preg_split('//', $tmpStr, -1, PREG_SPLIT_NO_EMPTY);
484
        $tmpDataArr = array_merge($tmpDataArr,$chars);
485
 
486
        $data = $tmpDataArr;
487
        */
488
        $data = chr($lh).chr($ll).$tmpStr;
489
 
490
        //$this->sections[$this->currSection]["data"] = $data;
491
 
492
        $this->debug("<hr><h1>".$this->currSection.":</h1>");
493
        //print_r($data);
494
        $this->debug("<hr>");
495
 
496
        //if(count($data) != $itemlen) {
497
        if(strlen($data) != $itemlen) {
498
            $this->errno = 12;
499
            $this->errstr = "Premature end of file?";
500
            $this->debug($this->errstr,1);
501
        }
502
 
503
        $this->currSection++; /** */
504
 
505
        switch($marker) {
506
                case M_SOS:
507
                    $this->debug("<br>Found '".M_SOS."' Section, Prcessing it... <br>");;
508
                        // If reading entire image is requested, read the rest of the data.
509
                        if ($this->ImageReadMode & $this->ReadMode["READ_IMAGE"]){
510
                        // Determine how much file is left.
511
                                $cp = ftell($fp);
512
                                fseek($fp,0, SEEK_END);
513
                                $ep = ftell($fp);
514
                                fseek($fp, $cp, SEEK_SET);
515
 
516
                        $size = $ep-$cp;
517
                        $got = fread($fp, $size);
518
 
519
                        $this->sections[$this->currSection]["data"] = $got;
520
                        $this->sections[$this->currSection]["size"] = $size;
521
                        $this->sections[$this->currSection]["type"] = PSEUDO_IMAGE_MARKER;
522
                        $this->currSection++;
523
                        $HaveAll = 1;
524
                        $exitAll =1;
525
                        }
526
                        $this->debug("<br>'".M_SOS."' Section, PROCESSED<br>");
527
                    break;
528
                case M_COM: // Comment section
529
                        $this->debug("<br>Found '".M_COM."'(Comment) Section, Processing<br>");
530
                        $this->process_COM($data, $itemlen);
531
                        $this->debug("<br>'".M_COM."'(Comment) Section, PROCESSED<br>");
532
 
533
                        $tmpTestLevel++;
534
                    break;
535
                case M_SOI:
536
                        $this->debug(" <br> === START OF IMAGE =====<br>");
537
                break;
538
                case M_EOI:
539
                        $this->debug(" <br>=== END OF IMAGE =====<br> ");
540
                break;
541
                case M_JFIF:
542
                        // Regular jpegs always have this tag, exif images have the exif
543
                        // marker instead, althogh ACDsee will write images with both markers.
544
                        // this program will re-create this marker on absence of exif marker.
545
                        // hence no need to keep the copy from the file.
546
                        //echo " <br> === M_JFIF =====<br>";
547
                        $this->sections[--$this->currSection]["data"] = "";
548
                        break;
549
                case M_EXIF:
550
                        // Seen files from some 'U-lead' software with Vivitar scanner
551
                        // that uses marker 31 for non exif stuff.  Thus make sure
552
                        // it says 'Exif' in the section before treating it as exif.
553
                        $this->debug("<br>Found '".M_EXIF."'(Exif) Section, Proccessing<br>");
554
                        $this->exifSection = $this->currSection-1;
555
                        if (($this->ImageReadMode & $this->ReadMode["READ_EXIF"]) && ($data[2].$data[3].$data[4].$data[5]) == "Exif"){
556
                                $this->process_EXIF($data, $itemlen);
557
                        }else{
558
                                // Discard this section.
559
                                $this->sections[--$this->currSection]["data"] = "";
560
                        }
561
                        $this->debug("<br>'".M_EXIF."'(Exif) Section, PROCESSED<br>");
562
                        $tmpTestLevel++;
563
                break;
564
                case M_SOF0:
565
                case M_SOF1:
566
                case M_SOF2:
567
                case M_SOF3:
568
                case M_SOF5:
569
                case M_SOF6:
570
                case M_SOF7:
571
                case M_SOF9:
572
                case M_SOF10:
573
                case M_SOF11:
574
                case M_SOF13:
575
                case M_SOF14:
576
                case M_SOF15:
577
                        $this->debug("<br>Found M_SOFn Section, Processing<br>");
578
                        $this->process_SOFn($data,$marker);
579
                        $this->debug("<br>M_SOFn Section, PROCESSED<br>");
580
                break;
581
                default:
582
                        $this->debug("DEFAULT: Jpeg section marker 0x$marker x size $itemlen\n");
583
        }
584
        $i++;
585
        if($exitAll == 1)  break;
586
            //if($tmpTestLevel == 2)  break;
587
        }
588
        fclose($fp);
589
        $this->newFile = 0;
590
    }
591
 
592
    /**
593
     * Changing / Assiging new file
594
     * @param   string    JPEG file to process
595
     *
596
     */
597
    function assign($file) {
598
 
599
      if(!empty($file)) {
600
        $this->file = $file;
601
      }
602
 
603
      /** check for existance of file! */
604
      if(!file_exists($this->file)) {
605
         $this->errorno = 1;
606
         $this->errorstr = "File '".$this->file."' does not exists!";
607
      }
608
      $this->newFile = 1;
609
    }
610
 
611
    /**
612
     * Process SOFn section of Image
613
     * @param  array    An array containing whole section.
614
     * @param   hex  Marker to specify the type of section.
615
     *
616
     */
617
    function process_SOFn($data,$marker) {
618
        $data_precision = 0;
619
        $num_components = 0;
620
 
621
        $data_precision = ord($data[2]);
622
 
623
        if($this->debug) {
624
          print("Image Dimension Calculation:");
625
          print("((ord($data[3]) << 8) | ord($data[4]));");
626
        }
627
        $this->ImageInfo["h"]["Height"] = ((ord($data[3]) << 8) | ord($data[4]));
628
        $this->ImageInfo["h"]["Width"] = ((ord($data[5]) << 8) | ord($data[6]));
629
 
630
        $num_components = ord($data[7]);
631
 
632
        if ($num_components == 3){
633
            $this->ImageInfo["h"]["IsColor"] = 1;
634
        }else{
635
            $this->ImageInfo["h"]["IsColor"] = 0;
636
        }
637
 
638
        $this->ImageInfo["h"]["Process"] = $marker;
639
        $this->debug("JPEG image is ".$this->ImageInfo["h"]["Width"]." * ".$this->ImageInfo["h"]["Height"].", $num_components color components, $data_precision bits per sample\n");
640
    }
641
 
642
    /**
643
     * Process Comments
644
     * @param   array    Section data
645
     * @param   int  Length of the section
646
     *
647
     */
648
    function process_COM($data,$length) {
649
        if ($length > MAX_COMMENT) $length = MAX_COMMENT;
650
            /** Truncate if it won't fit in our structure. */
651
 
652
        $nch = 0; $Comment = "";
653
        for ($a=2;$a<$length;$a++){
654
            $ch = $data[$a];
655
            if ($ch == '\r' && $data[$a+1] == '\n') continue; // Remove cr followed by lf.
656
 
657
            $Comment .= $ch;
658
        }
659
        //$this->ImageInfo[M_COM] = $Comment;
660
        $this->ImageInfo["h"]["imageComment"] = $Comment;
661
        $this->debug("COM marker comment: $Comment\n");
662
    }
663
    /**
664
     * Process one of the nested EXIF directories.
665
     * @param   string        All directory information
666
     * @param   string     whole Section
667
     * @param   int  Length of exif section
668
     *
669
    */
670
    function ProcessExifDir($DirStart, $OffsetBase, $ExifLength) {
671
 
672
        $NumDirEntries = 0;
673
        $ValuePtr = array();
674
 
675
        $NumDirEntries = $this->Get16u($DirStart[0],$DirStart[1]);
676
 
677
 
678
        $this->debug("<br>Directory with $NumDirEntries entries\n");
679
 
680
        for ($de=0;$de<$NumDirEntries;$de++){
681
            //$DirEntry = array_slice($DirStart,2+12*$de);
682
            $DirEntry = substr($DirStart,2+12*$de);
683
 
684
            $Tag = $this->Get16u($DirEntry[0],$DirEntry[1]);
685
            $Format = $this->Get16u($DirEntry[2],$DirEntry[3]);
686
            $Components = $this->Get32u($DirEntry[4],$DirEntry[5],$DirEntry[6],$DirEntry[7]);
687
 
688
            /**
689
            if ((Format-1) >= NUM_FORMATS) {
690
                // (-1) catches illegal zero case as unsigned underflows to positive large.
691
                ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
692
                continue;
693
            }
694
            */
695
 
696
            $ByteCount = $Components * $this->BytesPerFormat[$Format];
697
 
698
            if ($ByteCount > 4){
699
                $OffsetVal = $this->Get32u($DirEntry[8],$DirEntry[9],$DirEntry[10],$DirEntry[11]);
700
                if ($OffsetVal+$ByteCount > $ExifLength){
701
                    $this->debug("Illegal value pointer($OffsetVal) for tag $Tag",1);
702
                }
703
                //$ValuePtr = array_slice($OffsetBase,$OffsetVal);
704
                $ValuePtr = substr($OffsetBase,$OffsetVal);
705
            } else {
706
                //$ValuePtr = array_slice($DirEntry,8);
707
                $ValuePtr = substr($DirEntry,8);
708
            }
709
 
710
            switch($Tag){
711
 
712
                case TAG_MAKE:
713
                    $this->ImageInfo["h"]["make"] = substr($ValuePtr,0,$ByteCount);
714
                    break;
715
 
716
                case TAG_MODEL:
717
                    $this->ImageInfo["h"]["model"] = substr($ValuePtr,0,$ByteCount);
718
                    break;
719
 
720
                case TAG_DATETIME_ORIGINAL:
721
                    $this->ImageInfo[TAG_DATETIME_ORIGINAL] =  substr($ValuePtr,0,$ByteCount);
722
                    $this->ImageInfo["h"]["DateTime"]  = substr($ValuePtr,0,$ByteCount);
723
                    break;
724
 
725
                case TAG_USERCOMMENT:
726
                    // Olympus has this padded with trailing spaces.  Remove these first.
727
                    for ($a=$ByteCount;;){
728
                        $a--;
729
                        if ($ValuePtr[$a] == ' '){
730
                            //$ValuePtr[$a] = '\0';
731
                        } else {
732
                            break;
733
                        }
734
                        if ($a == 0) break;
735
                    }
736
 
737
                    // Copy the comment
738
                    if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4]) == "ASCII"){
739
                        for ($a=5;$a<10;$a++){
740
                            $c = $ValuePtr[$a];
741
                            if ($c != '\0' && $c != ' '){
742
                                $tmp = substr($ValuePtr,0,$ByteCount);
743
                                    break;
744
                            }
745
                        }
746
                    } else if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4].$ValuePtr[5].$ValuePtr[6]) == "Unicode"){
747
                        $tmp = substr($ValuePtr,0,$ByteCount);
748
                        //  * Handle Unicode characters here...
749
                    } else {
750
                        //$this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount));
751
                        $tmp = substr($ValuePtr,0,$ByteCount);
752
                    }
753
                    $this->ImageInfo['h']["exifComment"] = $tmp;
754
                    break;
755
 
756
                                case TAG_ARTIST:
757
                    $this->ImageInfo['h']["artist"] = substr($ValuePtr,0,$ByteCount);
758
                                        break;
759
 
760
                                case TAG_COPYRIGHT:
761
                    $this->ImageInfo['h']["copyright"] = htmlentities(substr($ValuePtr,0,$ByteCount));
762
                                        break;
763
 
764
                case TAG_FNUMBER:
765
                    // Simplest way of expressing aperture, so I trust it the most.
766
                    // (overwrite previously computd value if there is one)
767
                    $tmp = $this->ConvertAnyFormat(substr($ValuePtr,0), $Format);
768
                    $this->ImageInfo['h']["fnumber"] = sprintf("f/%3.1f",(double)$tmp[0]);
769
                    break;
770
 
771
                case TAG_APERTURE:
772
                case TAG_MAXAPERTURE:
773
                    // More relevant info always comes earlier, so only use this field if we don't
774
                    // have appropriate aperture information yet.
775
                    if (!isset($this->ImageInfo['h']["aperture"])){
776
                        $tmpArr =  $this->ConvertAnyFormat($ValuePtr, $Format);
777
                        $this->ImageInfo['h']["aperture"] = exp($tmpArr[0]*log(2)*0.5);
778
                    }
779
                    break;
780
 
781
                case TAG_FOCALLENGTH:
782
                    // Nice digital cameras actually save the focal length as a function
783
                    // of how farthey are zoomed in.
784
                    $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
785
                    $this->ImageInfo['h']["focalLength"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
786
                    if (isset($this->ImageInfo['h']["CCDWidth"])){
787
                        $this->ImageInfo['h']["focalLength"] .= sprintf("(35mm equivalent: %dmm)",(int)($tmp[0]/$this->ImageInfo['h']["CCDWidth"]*36 + 0.5));
788
                    }
789
                    break;
790
 
791
                case TAG_SUBJECT_DISTANCE:
792
                    // Inidcates the distacne the autofocus camera is focused to.
793
                    // Tends to be less accurate as distance increases.
794
                    //$this->ImageInfo["h"]["Distance"] =  $this->ConvertAnyFormat($ValuePtr, $Format);
795
                    $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
796
                    $this->ImageInfo['h']["Distance"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
797
                    if ($this->ImageInfo['h']["Distance"] < 0){
798
                            $this->ImageInfo['h']["focusDistance"] = "Infinite";
799
                    } else {
800
                            $this->ImageInfo['h']["focusDistance"] = sprintf("%4.2fm",(double)$this->ImageInfo['h']["Distance"]);
801
                    }
802
 
803
 
804
                    break;
805
 
806
                case TAG_EXPOSURETIME:
807
                    // Simplest way of expressing exposure time, so I trust it most.
808
                    // (overwrite previously computd value if there is one)
809
                    $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
810
                    $this->ImageInfo['h']["exposureTime"] = sprintf("%6.3f s (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
811
                    if ($tmp[0] <= 0.5 && $tmp[1][0] != 1){
812
                            $this->ImageInfo['h']["exposureTime"] .= sprintf(" (1/%d)",(int)(0.5 + 1/$tmp[0]));
813
                    }
814
                    break;
815
 
816
                case TAG_SHUTTERSPEED:
817
                    // More complicated way of expressing exposure time, so only use
818
                    // this value if we don't already have it from somewhere else.
819
                    if ($this->ImageInfo[TAG_EXPOSURETIME] == 0){
820
                        $sp = $this->ConvertAnyFormat($ValuePtr, $Format);
821
                        $this->ImageInfo[TAG_SHUTTERSPEED] = (1/exp($sp[0]*log(2)));
822
                    }
823
                    break;
824
 
825
                case TAG_FLASH:
826
                    $this->ImageInfo["h"]["flashUsed"] = "No";
827
                    if ($this->ConvertAnyFormat($ValuePtr, $Format) & 7){
828
                        $this->ImageInfo["h"]["flashUsed"] = "Yes";
829
                    }
830
                    break;
831
 
832
                case TAG_ORIENTATION:
833
                    $this->ImageInfo[TAG_ORIENTATION] = $this->ConvertAnyFormat($ValuePtr, $Format);
834
                    if ($this->ImageInfo[TAG_ORIENTATION] < 1 || $this->ImageInfo[TAG_ORIENTATION] > 8){
835
                            $this->debug(sprintf("Undefined rotation value %d", $this->ImageInfo[TAG_ORIENTATION], 0),1);
836
                        $this->ImageInfo[TAG_ORIENTATION] = 0;
837
                    }
838
                    break;
839
 
840
                case TAG_EXIF_IMAGELENGTH:
841
                    //       * Image height
842
                    $a = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
843
                    if ($this->ExifImageLength < $a) $this->ExifImageLength = $a;
844
                    $this->ImageInfo[TAG_EXIF_IMAGELENGTH] = $this->ExifImageLength;
845
                    $this->ImageInfo["h"]["Height"] = $this->ExifImageLength;
846
                    break;
847
                case TAG_EXIF_IMAGEWIDTH:
848
                    // Use largest of height and width to deal with images that have been
849
                    // rotated to portrait format.
850
                    $a = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
851
                    if ($this->ExifImageWidth < $a) $this->ExifImageWidth = $a;
852
                    $this->ImageInfo[TAG_EXIF_IMAGEWIDTH] = $this->ExifImageWidth;
853
                    $this->ImageInfo["h"]["Width"] = $this->ExifImageWidth;
854
 
855
                    break;
856
 
857
                case TAG_FOCALPLANEXRES:
858
                    $this->FocalplaneXRes = $this->ConvertAnyFormat($ValuePtr, $Format);
859
                    $this->FocalplaneXRes = $this->FocalplaneXRes[0];
860
                    $this->ImageInfo[TAG_FOCALPLANEXRES] = $this->FocalplaneXRes[0];
861
                    break;
862
 
863
                case TAG_FOCALPLANEUNITS:
864
                    switch($this->ConvertAnyFormat($ValuePtr, $Format)){
865
                        case 1: $this->FocalplaneUnits = 25.4; break; // inch
866
                        case 2:
867
                            // According to the information I was using, 2 means meters.
868
                            // But looking at the Cannon powershot's files, inches is the only
869
                            // sensible value.
870
                            $this->FocalplaneUnits = 25.4;
871
                            break;
872
 
873
                        case 3: $this->FocalplaneUnits = 10;   break;  // centimeter
874
                        case 4: $this->FocalplaneUnits = 1;    break;  // milimeter
875
                        case 5: $this->FocalplaneUnits = .001; break;  // micrometer
876
                    }
877
                    $this->ImageInfo[TAG_FOCALPLANEUNITS] = $this->FocalplaneUnits;
878
                    break;
879
 
880
                    // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de)
881
 
882
                case TAG_EXPOSURE_BIAS:
883
                    $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
884
                    $this->ImageInfo['h']["exposureBias"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
885
                    break;
886
 
887
                case TAG_WHITEBALANCE:
888
                    $tmp = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
889
                    $tmpArr = array("1"=>"Sunny","2"=>"fluorescent","3"=>"incandescent");
890
                    $this->ImageInfo['h']["whiteBalance"] =
891
                        (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Cloudy");
892
                    break;
893
 
894
                case TAG_METERING_MODE:
895
                    $tmp = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
896
 
897
                    $tmpArr = array("2"=>"center weight","3"=>"spot","5"=>"matrix");
898
                    $this->ImageInfo['h']["meteringMode"] =
899
                        (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
900
                    break;
901
 
902
                case TAG_EXPOSURE_PROGRAM:
903
                    $tmp = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
904
                    $tmpArr = array("2"=>"program (auto)","3"=>"aperture priority (semi-auto)","4"=>"shutter priority (semi-auto)");
905
                    $this->ImageInfo['h']["exposure"] =
906
                        (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
907
 
908
                    break;
909
 
910
                case TAG_ISO_EQUIVALENT:
911
                    $tmp = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
912
                    if ( $tmp < 50 ) $tmp *= 200;
913
                    $this->ImageInfo['h']["isoEquiv"] = sprintf("%2d",(int)$tmp);
914
                    break;
915
 
916
                case TAG_COMPRESSION_LEVEL:
917
                    $tmp = (int) $this->ConvertAnyFormat($ValuePtr, $Format);
918
                    $tmpArr = array("1"=>"Basic","2"=>"Normal","4"=>"Fine");
919
                    $this->ImageInfo['h']["jpegQuality"] =
920
                        (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
921
                    break;
922
 
923
                case TAG_THUMBNAIL_OFFSET:
924
                    $this->ThumbnailOffset = $this->ConvertAnyFormat($ValuePtr, $Format);
925
                    $this->DirWithThumbnailPtrs = $DirStart;
926
                    break;
927
 
928
                case TAG_THUMBNAIL_LENGTH:
929
                    $this->ThumbnailSize = $this->ConvertAnyFormat($ValuePtr, $Format);
930
                    $this->ImageInfo[TAG_THUMBNAIL_LENGTH] = $this->ThumbnailSize;
931
                    break;
932
 
933
                //----------------------------------------------
934
                case TAG_IMAGE_DESC:
935
                        $this->ImageInfo['h']["imageDesc"] = substr($ValuePtr,0,$ByteCount);
936
                        break;
937
                case TAG_X_RESOLUTION:
938
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
939
                        $this->ImageInfo['h']["xResolution"] = sprintf("%4.2f (%d/%d) %s",(double)$tmp[0],$tmp[1][0],$tmp[1][1],$this->ImageInfo['h']["resolutionUnit"]);
940
                        break;
941
                case TAG_Y_RESOLUTION:
942
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
943
                        $this->ImageInfo['h']["yResolution"] = sprintf("%4.2f (%d/%d) %s",(double)$tmp[0],$tmp[1][0],$tmp[1][1],$this->ImageInfo['h']["resolutionUnit"]);
944
                        break;
945
                case TAG_RESOLUTION_UNIT:
946
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
947
                        $tmpArr = array("2"=>"Inches","3"=>"Centimeters");
948
 
949
                        $this->ImageInfo['h']["resolutionUnit"] =
950
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
951
                        break;
952
                case TAG_SOFTWARE:
953
                        $this->ImageInfo['h']["software"] = substr($ValuePtr,0,$ByteCount);
954
                        break;
955
                case TAG_FILE_MODDATE;
956
                        $this->ImageInfo['h']["fileModifiedDate"] = substr($ValuePtr,0,$ByteCount);
957
                        break;
958
                case TAG_YCBCR_POSITIONING:
959
                        $this->ImageInfo['h']["YCbCrPositioning"] = $this->ConvertAnyFormat($ValuePtr, $Format);
960
                        break;
961
                case TAG_EXIF_VERSION:
962
                        $this->ImageInfo['h']["exifVersion"] = substr($ValuePtr,0,$ByteCount);
963
                        break;
964
                case TAG_DATE_TIME_DIGITIZED:
965
                        $this->ImageInfo['h']["dateTimeDigitized"] = substr($ValuePtr,0,$ByteCount);
966
                        break;
967
                case TAG_COMPONENT_CONFIG: // need more tests for this
968
                        $tmp = (int)$this->ConvertAnyFormat($ValuePtr, $Format);
969
 
970
                        $tmpArr = array("0"=>"Does Not Exists","1"=>"Y","2"=>"Cb","3"=>"Cr","4"=>"R","5"=>"G","6"=>"B");
971
 
972
                        if(strlen($tmp) < 4 ) {
973
                            $this->ImageInfo['h']["componentConfig"] = $tmpArr["0"];
974
                        } else {
975
                            for($i=0;$i<strlen($tmp);$i++) {
976
                                if($tmp["$i"] != 0) {
977
                                    $this->ImageInfo['h']["componentConfig"] .= $tmpArr[$tmp["$i"]];
978
                                }
979
                            }
980
                        }
981
                        break;
982
                case TAG_MAKER_NOTE:
983
                        //$this->ImageInfo['h']["makerNote"] = substr($ValuePtr,0,$ByteCount);
984
                        $this->ImageInfo['h']["makerNote"] = "NOT IMPLEMENTED";
985
                        break;
986
                case TAG_SUB_SEC_TIME:
987
                        $this->ImageInfo['h']["subSectionTime"] = substr($ValuePtr,0,$ByteCount);
988
                        break;
989
                case TAG_SUB_SEC_TIME_ORIG:
990
                        $this->ImageInfo['h']["subSectionTimeOriginal"] = substr($ValuePtr,0,$ByteCount);
991
                        break;
992
                case TAG_SUB_SEC_TIME_DIGITIZED:
993
                        $this->ImageInfo['h']["subSectionTimeDigtized"] = substr($ValuePtr,0,$ByteCount);
994
                        break;
995
                case TAG_FLASHPIX_VER:
996
                        $this->ImageInfo['h']["flashpixVersion"] = substr($ValuePtr,0,$ByteCount);
997
                        break;
998
                case TAG_COLOR_SPACE:
999
                        $this->ImageInfo['h']["colorSpace"] = substr($ValuePtr,0,$ByteCount);
1000
                        break;
1001
                case TAG_RELATED_SOUND_FILE:
1002
                        $this->ImageInfo['h']["relatedSoundFile"] = substr($ValuePtr,0,$ByteCount);
1003
                        break;
1004
                case TAG_GPS_LATITUDE_REF:
1005
                        $this->ImageInfo['h']["GPSLatitudeRef"] = substr($ValuePtr,0,$ByteCount);
1006
                        break;
1007
                case TAG_GPS_LATITUDE:
1008
                        $this->ImageInfo['h']["GPSLatitude"] = substr($ValuePtr,0,$ByteCount);
1009
                        break;
1010
                case TAG_SENSING_METHOD:
1011
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1012
                        $tmpArr = array("1"=>"Not Defined","2"=>"One-chip color area sensor","3"=>"Two-chip color area sensor",
1013
                                        "4"=>"Three -chip color area sensor","5"=>"Color sequential area sensor",
1014
                                        "6"=>"Trilinear sensor", "7"=>"Color sequential linear sensor"
1015
                                        );
1016
 
1017
                        $this->ImageInfo['h']["sensing"] =
1018
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1019
                        break;
1020
                case TAG_SOUCE_TYPE:
1021
                        $this->ImageInfo['h']["sourceType"] = substr($ValuePtr,0,$ByteCount);
1022
                        break;
1023
                case TAG_SCENE_TYPE:
1024
                        $this->ImageInfo['h']["sceneType"] = substr($ValuePtr,0,$ByteCount);
1025
                        break;
1026
                case TAG_CFA_PATTERN:
1027
                        $this->ImageInfo['h']["CFAPattern"] = substr($ValuePtr,0,$ByteCount);
1028
                        break;
1029
                case TAG_CUSTOM_RENDERED:
1030
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1031
                        $this->ImageInfo['h']["customRendered"] = ($mp == 0) ? 'Normal Process' : ($mp == 1 ? 'Custom Process' : 'Reserved');
1032
                        break;
1033
                case TAG_EXPOSURE_MODE:
1034
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1035
                        $tmpArr = array('Auto Exposure','Manual Exposure','Auto Bracket');
1036
                        $this->ImageInfo['h']["exposureMode"] =
1037
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1038
                        break;
1039
                case TAG_WHITE_BALANCE:
1040
                        $this->ImageInfo['h']["whiteBalance"] = $this->ConvertAnyFormat($ValuePtr, $Format);
1041
                        break;
1042
                case TAG_DIGITAL_ZOOM_RATIO:
1043
                        $tmp = $this->ImageInfo['h']["zoomRatio"] = $this->ConvertAnyFormat($ValuePtr, $Format);
1044
                        $this->ImageInfo['h']["zoomRatio"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
1045
                        break;
1046
                case TAG_FLENGTH_IN35MM:
1047
                        $this->ImageInfo['h']["flength35mm"] = $this->ConvertAnyFormat($ValuePtr, $Format);
1048
                        break;
1049
                case TAG_SCREEN_CAP_TYPE:
1050
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1051
                        $tmpArr = array("Standard","Landscape","Portrait","Night Scene");
1052
                        $this->ImageInfo['h']["screenCaptureType"] =
1053
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1054
                        break;
1055
                case TAG_GAIN_CONTROL:
1056
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1057
                        $tmpArr = array("None","Low Gain Up","High Gain Up","Low Gain Down","High Gain Down");
1058
                        $this->ImageInfo['h']["gainControl"] =
1059
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1060
                        break;
1061
                case TAG_CONTRAST:
1062
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1063
                        $tmpArr = array("Normal","Soft","Hard");
1064
                        $this->ImageInfo['h']["contrast"] =
1065
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1066
                        break;
1067
                case TAG_SATURATION:
1068
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1069
                        $tmpArr = array("Normal","Low Saturation","High Saturation");
1070
                        $this->ImageInfo['h']["saturation"] =
1071
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1072
                        break;
1073
                case TAG_SHARPNESS:
1074
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1075
                        $tmpArr = array("Normal","Soft","Hard");
1076
                        $this->ImageInfo['h']["sharpness"] =
1077
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1078
                        break;
1079
                case TAG_DIST_RANGE:
1080
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1081
                        $tmpArr = array("Unknown","Macro","Close View","Distant View");
1082
                        $this->ImageInfo['h']["distanceRange"] =
1083
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1084
                        break;
1085
                case TAG_DEVICE_SETTING_DESC:
1086
                        /*
1087
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1088
                        $tmpArr = array("Unknown","Macro","Close View","Distant View");
1089
                        $this->ImageInfo['h']["distanceRange"] =
1090
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1091
                        */
1092
                        $this->ImageInfo['h']["deviceSettingDesc"] = "NOT IMPLEMENTED";
1093
                        break;
1094
                case TAG_COMPRESS_SCHEME:
1095
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1096
                        $tmpArr = array("1"=>"Uncompressed","6"=>"JPEG compression (thumbnails only)");
1097
                        $this->ImageInfo['h']["compressScheme"] =
1098
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1099
                        break;
1100
                case TAG_IMAGE_WD:
1101
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1102
                        $this->ImageInfo['h']["jpegImageWidth"] = $tmp;
1103
                        break;
1104
                case TAG_IMAGE_HT:
1105
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1106
                        $this->ImageInfo['h']["jpegImageHeight"] = $tmp;
1107
                        break;
1108
                case TAG_IMAGE_BPS:
1109
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1110
                        $this->ImageInfo['h']["jpegBitsPerSample"] = $tmp;
1111
                        break;
1112
                case TAG_IMAGE_PHOTO_INT:
1113
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1114
                        $this->ImageInfo['h']["jpegPhotometricInt"] = $tmp;
1115
                        $tmpArr = array("2"=>"RGB","6"=>"YCbCr");
1116
                        $this->ImageInfo['h']["jpegPhotometricInt"] =
1117
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1118
 
1119
                        break;
1120
                case TAG_IMAGE_SOFFSET:
1121
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1122
                        $this->ImageInfo['h']["jpegStripOffsets"] = $tmp;
1123
                        break;
1124
                case TAG_IMAGE_SPP:
1125
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1126
                        $this->ImageInfo['h']["jpegSamplesPerPixel"] = $tmp;
1127
                        break;
1128
                case TAG_IMAGE_RPS:
1129
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1130
                        $this->ImageInfo['h']["jpegRowsPerStrip"] = $tmp;
1131
                        break;
1132
                case TAG_IMAGE_SBC:
1133
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1134
                        $this->ImageInfo['h']["jpegStripByteCounts"] = $tmp;
1135
                        break;
1136
                case TAG_IMAGE_P_CONFIG:
1137
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1138
                        $tmpArr = array("1"=>"Chunky Format","2"=>"Planar Format");
1139
                        $this->ImageInfo['h']["jpegPlanarConfig"] =
1140
                            (isset($tmpArr["$tmp"]) ? $tmpArr["$tmp"] : "Reserved");
1141
                        break;
1142
                case TAG_FOCALPLANE_YRESOL:
1143
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1144
                        $this->ImageInfo['h']["focalPlaneYResolution"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
1145
                        break;
1146
                case TAG_BRIGHTNESS:
1147
                        $tmp = $this->ConvertAnyFormat($ValuePtr, $Format);
1148
                        $this->ImageInfo['h']["brightness"] = sprintf("%4.2f (%d/%d)",(double)$tmp[0],$tmp[1][0],$tmp[1][1]);
1149
                        break;
1150
                //---------------------------------------------
1151
                case TAG_EXIF_OFFSET:
1152
                case TAG_INTEROP_OFFSET:
1153
                    {
1154
 
1155
                        $SubdirStart = substr($OffsetBase,$this->Get32u($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]));
1156
                        //if ($SubdirStart < $OffsetBase || $SubdirStart > $OffsetBase+$ExifLength){
1157
                        //    debug("Illegal exif or interop ofset directory link",1);
1158
                        //}else{
1159
                            //echo "<h1>Calling sub-exif dir</h1>";
1160
                            $this->ProcessExifDir($SubdirStart, $OffsetBase, $ExifLength);
1161
                        //}
1162
                        continue;
1163
                    }
1164
                default: {
1165
                    $this->debug("UNKNOWN TAG: $Tag");
1166
                    }
1167
            }
1168
        }
1169
 
1170
        {
1171
        // In addition to linking to subdirectories via exif tags,
1172
        // there's also a potential link to another directory at the end of each
1173
        // directory.  this has got to be the result of a comitee!
1174
        $tmpDirStart = substr($DirStart,2+12*$NumDirEntries);
1175
        if (strlen($tmpDirStart) + 4 <= strlen($OffsetBase)+$ExifLength){
1176
            $Offset = $this->Get32u($tmpDirStart[0],$tmpDirStart[1],$tmpDirStart[2],$tmpDirStart[3]);
1177
            if ($Offset){
1178
                $SubdirStart = substr($OffsetBase,$Offset);
1179
                if (strlen($SubdirStart) > strlen($OffsetBase)+$ExifLength){
1180
                    if (strlen($SubdirStart) < strlen($OffsetBase)+$ExifLength+20){
1181
                        // Jhead 1.3 or earlier would crop the whole directory!
1182
                        // As Jhead produces this form of format incorrectness,
1183
                        // I'll just let it pass silently
1184
                    } else {
1185
                        $this->errno = 51;
1186
                        $this->errstr = "Illegal subdirectory link";
1187
                        $this->debug($this->errstr,1);
1188
                    }
1189
                }else{
1190
                    if (strlen($SubdirStart) <= strlen($OffsetBase)+$ExifLength){
1191
                        $this->ProcessExifDir($SubdirStart, $OffsetBase, $ExifLength);
1192
                    }
1193
                }
1194
            }
1195
        } else {
1196
            // The exif header ends before the last next directory pointer.
1197
        }
1198
    }
1199
 
1200
    /**
1201
    * Check if thumbnail has been cached or not.
1202
    * If yes! then read the file.
1203
    */
1204
    if(file_exists($this->thumbnail) && $this->caching && (filemtime($this->thumbnail) == filemtime($this->file) )) {
1205
        $this->ImageInfo["h"]["Thumbnail"] = $this->thumbnail;
1206
        $this->ImageInfo["h"]["ThumbnailSize"] =  sprintf("%d bytes",filesize($this->thumbnail));
1207
    } else{
1208
        if ($this->ThumbnailSize && $this->ThumbnailOffset){
1209
            if ($this->ThumbnailSize + $this->ThumbnailOffset <= $ExifLength){
1210
                // The thumbnail pointer appears to be valid.  Store it.
1211
                $this->ImageInfo["h"]["Thumbnail"] = substr($OffsetBase,$this->ThumbnailOffset);
1212
 
1213
                // Save the thumbnail /
1214
                if($this->caching && is_dir($this->cacheDir)) {
1215
                    $this->saveThumbnail($this->thumbnail);
1216
                    $this->ImageInfo["h"]["Thumbnail"] = $this->thumbnail;
1217
                }
1218
                $this->ImageInfo["h"]["ThumbnailSize"] =  sprintf("%d bytes",strlen($this->ImageInfo["h"]["Thumbnail"]));
1219
            }
1220
        }
1221
    }
1222
    }
1223
 
1224
    /**
1225
     * Process Exif data
1226
     * @param   array    Section data as an array
1227
     * @param   int  Length of the section (length of data array)
1228
     *
1229
     */
1230
    function process_EXIF($data,$length) {
1231
 
1232
        $this->debug("Exif header $length bytes long\n");
1233
        if(($data[2].$data[3].$data[4].$data[5]) != "Exif") {
1234
            $this->errno = 52;
1235
            $this->errstr = "NOT EXIF FORMAT";
1236
            $this->debug($this->errstr,1);
1237
        }
1238
 
1239
        $this->ImageInfo["h"]["FlashUsed"] = 0;
1240
            /** If it s from a digicam, and it used flash, it says so. */
1241
 
1242
        $this->FocalplaneXRes = 0;
1243
        $this->FocalplaneUnits = 0;
1244
        $this->ExifImageWidth = 0;
1245
 
1246
        if(($data[8].$data[9]) == "II") {
1247
            $this->debug("Exif section in Intel order\n");
1248
            $this->MotorolaOrder = 0;
1249
        } else if(($data[8].$data[9]) == "MM") {
1250
            $this->debug("Exif section in Motorola order\n");
1251
            $this->MotorolaOrder = 1;
1252
        } else {
1253
            $this->errno = 53;
1254
            $this->errstr = "Invalid Exif alignment marker.\n";
1255
            $this->debug($this->errstr,1);
1256
            return;
1257
        }
1258
 
1259
        if($this->Get16u($data[10],$data[11]) != 0x2A || $this->Get32s($data[12],$data[13],$data[14],$data[15]) != 0x08) {
1260
            $this->errno = 54;
1261
            $this->errstr = "Invalid Exif start (1)";
1262
            $this->debug($this->errstr,1);
1263
        }
1264
 
1265
        $DirWithThumbnailPtrs = NULL;
1266
 
1267
        //$this->ProcessExifDir(array_slice($data,16),array_slice($data,8),$length);
1268
        $this->ProcessExifDir(substr($data,16),substr($data,8),$length);
1269
 
1270
        // Compute the CCD width, in milimeters.                      2
1271
        if ($this->FocalplaneXRes != 0){
1272
            $this->ImageInfo["h"]["CCDWidth"] = sprintf("%4.2fmm",(float)($this->ExifImageWidth * $this->FocalplaneUnits / $this->FocalplaneXRes));
1273
        }
1274
 
1275
        $this->debug("Non settings part of Exif header: ".$length." bytes\n");
1276
    } // end of function process_EXIF
1277
 
1278
    /**
1279
     * Converts two byte number into its equivalent int integer
1280
     * @param   int
1281
     * @param   int
1282
     *
1283
     */
1284
    function Get16u($val,$by) {
1285
        if($this->MotorolaOrder){
1286
            return ((ord($val) << 8) | ord($by));
1287
        } else {
1288
            return ((ord($by) << 8) | ord($val));
1289
        }
1290
    }
1291
 
1292
    /**
1293
     * Converts 4-byte number into its equivalent integer
1294
     *
1295
     * @param   int
1296
     * @param   int
1297
     * @param   int
1298
     * @param   int
1299
     *
1300
     * @return int
1301
     */
1302
    function Get32s($val1,$val2,$val3,$val4)
1303
    {
1304
        $val1 = ord($val1);
1305
        $val2 = ord($val2);
1306
        $val3 = ord($val3);
1307
        $val4 = ord($val4);
1308
 
1309
        if ($this->MotorolaOrder){
1310
            return (($val1 << 24) | ($val2 << 16) | ($val3 << 8 ) | ($val4 << 0 ));
1311
        }else{
1312
            return  (($val4 << 24) | ($val3 << 16) | ($val2 << 8 ) | ($val1 << 0 ));
1313
        }
1314
    }
1315
    /**
1316
     * Converts 4-byte number into its equivalent integer with the help of Get32s
1317
     *
1318
     * @param   int
1319
     * @param   int
1320
     * @param   int
1321
     * @param   int
1322
     *
1323
     * @return int
1324
     *
1325
     */
1326
    function get32u($val1,$val2,$val3,$val4) {
1327
        return ($this->Get32s($val1,$val2,$val3,$val4) & 0xffffffff);
1328
    }
1329
 
1330
    //--------------------------------------------------------------------------
1331
    // Evaluate number, be it int, rational, or float from directory.
1332
    //--------------------------------------------------------------------------
1333
    function ConvertAnyFormat($ValuePtr, $Format)
1334
    {
1335
        $Value = 0;
1336
 
1337
        switch($Format){
1338
            case FMT_SBYTE:     $Value = $ValuePtr[0];  break;
1339
            case FMT_BYTE:      $Value = $ValuePtr[0];        break;
1340
 
1341
            case FMT_USHORT:    $Value = $this->Get16u($ValuePtr[0],$ValuePtr[1]);          break;
1342
            case FMT_ULONG:     $Value = $this->Get32u($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]);          break;
1343
 
1344
            case FMT_URATIONAL:
1345
            case FMT_SRATIONAL:
1346
                {
1347
 
1348
                    $Num = $this->Get32s($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]);
1349
                    $Den = $this->Get32s($ValuePtr[4],$ValuePtr[5],$ValuePtr[6],$ValuePtr[7]);
1350
                    if ($Den == 0){
1351
                        $Value = 0;
1352
                    }else{
1353
                        $Value = (double) ($Num/$Den);
1354
                    }
1355
                    return array($Value,array($Num,$Den));
1356
                    break;
1357
                }
1358
 
1359
            case FMT_SSHORT:    $Value = $this->Get16u($ValuePtr[0],$ValuePtr[1]);  break;
1360
            case FMT_SLONG:     $Value = $this->Get32s($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]);                break;
1361
 
1362
            // Not sure if this is correct (never seen float used in Exif format)
1363
            case FMT_SINGLE:    $Value = $ValuePtr[0];      break;
1364
            case FMT_DOUBLE:    $Value = $ValuePtr[0];             break;
1365
        }
1366
        return $Value;
1367
    }
1368
 
1369
    /**
1370
     * Function to extract thumbnail from Exif data of the image.
1371
     * and store it in a filename given by $ThumbFile
1372
     *
1373
     * @param   String   Files name to store the thumbnail
1374
     *
1375
     */
1376
    function saveThumbnail($ThumbFile) {
1377
         $ThumbFile = trim($ThumbFile);
1378
         $file = basename($this->file);
1379
 
1380
         if(empty($ThumbFile)) $ThumbFile = "th_$file";
1381
 
1382
         if (!empty($this->ImageInfo["h"]["Thumbnail"])){
1383
            $tp = fopen($ThumbFile,"wb");
1384
            if(!$tp) {
1385
                $this->errno = 2;
1386
                $this->errstr = "Cannot Open file '$ThumbFile'";
1387
            }
1388
            fwrite($tp,$this->ImageInfo["h"]["Thumbnail"]);
1389
            fclose($tp);
1390
            touch($ThumbFile,filemtime($this->file));
1391
         }
1392
         //$this->thumbnailURL = $ThumbFile;
1393
         $this->ImageInfo["h"]["Thumbnail"] = $ThumbFile;
1394
    }
1395
 
1396
    /**
1397
     * Returns thumbnail url along with parameter supplied.
1398
     * Should be called in src attribute of image
1399
     *
1400
     * @return  string  File URL
1401
     *
1402
     */
1403
    function showThumbnail() {
1404
        return "showThumbnail.php?file=".$this->file;
1405
        //$this->ImageInfo["h"]["Thumbnail"]
1406
    }
1407
 
1408
    /**
1409
     * Function to give back thumbail image
1410
     * @return string   full image
1411
     *
1412
     */
1413
    function getThumbnail() {
1414
        return $this->ImageInfo["h"]["Thumbnail"];
1415
    }
1416
 
1417
    /**
1418
    *
1419
    */
1420
    function getImageInfo() {
1421
 
1422
        $imgInfo = $this->ImageInfo["h"];
1423
 
1424
        $retArr = $imgInfo;
1425
        $retArr["FileName"] = $imgInfo["FileName"];
1426
        $retArr["FileSize"] = $imgInfo["FileSize"]." bytes";
1427
 
1428
        $retArr["FileDateTime"] = date("d-M-Y H:i:s",$imgInfo["FileDateTime"]);
1429
 
1430
        $retArr["resolution"] = $imgInfo["Width"]."x".$imgInfo["Height"];
1431
 
1432
 
1433
        if ($this->ImageInfo[TAG_ORIENTATION] > 1){
1434
                // Only print orientation if one was supplied, and if its not 1 (normal orientation)
1435
 
1436
                // 1 - "The 0th row is at the visual top of the image,    and the 0th column is the visual left-hand side."
1437
                // 2 - "The 0th row is at the visual top of the image,    and the 0th column is the visual right-hand side."
1438
                // 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side."
1439
                // 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side."
1440
 
1441
                // 5 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual top."
1442
                // 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top."
1443
                // 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom."
1444
                // 8 - "The 0th row is the visual left-hand side of of the image,  and the 0th column is the visual bottom."
1445
 
1446
                // Note: The descriptions here are the same as the name of the command line
1447
                // ption to pass to jpegtran to right the image
1448
                $OrientTab = array(
1449
                "Undefined",
1450
                "Normal",           // 1
1451
                "flip horizontal",  // left right reversed mirror
1452
                "rotate 180",       // 3
1453
                "flip vertical",    // upside down mirror
1454
                "transpose",        // Flipped about top-left <--> bottom-right axis.
1455
                "rotate 90",        // rotate 90 cw to right it.
1456
                "transverse",       // flipped about top-right <--> bottom-left axis
1457
                "rotate 270",       // rotate 270 to right it.
1458
                );
1459
 
1460
                $retArr["orientation"] = $OrientTab[$this->ImageInfo[TAG_ORIENTATION]];
1461
        }
1462
 
1463
        $retArr["color"] = ($imgInfo["IsColor"] == 0) ? "Black and white" : "Color";
1464
 
1465
        if(isset($imgInfo["Process"])) {
1466
            switch($imgInfo["Process"]) {
1467
                case M_SOF0: $process = "Baseline";break;
1468
                case M_SOF1: $process = "Extended sequential";break;
1469
                case M_SOF2: $process = "Progressive";break;
1470
                case M_SOF3: $process = "Lossless";break;
1471
                case M_SOF5: $process = "Differential sequential";break;
1472
                case M_SOF6: $process = "Differential progressive";break;
1473
                case M_SOF7: $process = "Differential lossless";break;
1474
                case M_SOF9: $process = "Extended sequential, arithmetic coding";break;
1475
                case M_SOF10: $process = "Progressive, arithmetic coding";break;
1476
                case M_SOF11: $process = "Lossless, arithmetic coding";break;
1477
                case M_SOF13: $process = "Differential sequential, arithmetic coding";break;
1478
                case M_SOF14: $process = "Differential progressive, arithmetic coding";break;
1479
                case M_SOF15: $process =   "Differential lossless, arithmetic coding";break;
1480
                default: $process = "Unknown";
1481
            }
1482
            $retArr["jpegProcess"] = $process;
1483
        }
1484
 
1485
        if(file_exists($this->thumbnailURL)) {
1486
                $retArr["Thumbnail"] = "<a href='$this->thumbnailURL'>$this->thumbnailURL</a>";
1487
        }
1488
 
1489
        return $retArr;
1490
    }
1491
 
1492
    /**
1493
    * Returns time in microseconds
1494
    */
1495
    function cpgGetMicroTime(){
1496
        list($usec, $sec) = explode(" ",microtime());
1497
        return ((float)$usec + (float)$sec);
1498
    }
1499
 
1500
    /**
1501
    *  Get the time difference
1502
    */
1503
    function getDiffTime() {
1504
            return ($this->cpgGetMicroTime() - $this->timeStart);
1505
    }
1506
} // end of class
1507
?>