source: extensions/AMetaData/JpegMetaData/JpegMetaData.class.php @ 6889

Last change on this file since 6889 was 6889, checked in by grum, 14 years ago

Update JpegMetadata class to recognize some digiKam xmp tag
bug:1826

  • Property svn:executable set to *
File size: 25.8 KB
Line 
1<?php
2/**
3 * --:: JPEG MetaDatas ::-------------------------------------------------------
4 *
5 * Version : 1.1.0
6 * Date    : 2010-07-29
7 *
8 *  Author    : Grum
9 *   email    : grum at piwigo.org
10 *   website  : http://photos.grum.fr
11 *
12 *   << May the Little SpaceFrog be with you ! >>
13 *
14 * +-----------------------------------------------------------------------+
15 * | JpegMetaData - a PHP based Jpeg Metadata manager                      |
16 * +-----------------------------------------------------------------------+
17 * | Copyright(C) 2010  Grum - http://www.grum.fr                          |
18 * +-----------------------------------------------------------------------+
19 * | This program is free software; you can redistribute it and/or modify  |
20 * | it under the terms of the GNU General Public License as published by  |
21 * | the Free Software Foundation                                          |
22 * |                                                                       |
23 * | This program is distributed in the hope that it will be useful, but   |
24 * | WITHOUT ANY WARRANTY; without even the implied warranty of            |
25 * | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
26 * | General Public License for more details.                              |
27 * |                                                                       |
28 * | You should have received a copy of the GNU General Public License     |
29 * | along with this program; if not, write to the Free Software           |
30 * | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
31 * | USA.                                                                  |
32 * +-----------------------------------------------------------------------+
33 *
34 *
35 * +-:: HISTORY ::--------+-----------------------------------------------------
36 * |         |            |
37 * | Release | Date       |
38 * +---------+------------+-----------------------------------------------------
39 * | 0.1.0a  | 2009-12-26 |
40 * |         |            |
41 * | 1.0.0   |            | * first public release
42 * |         |            |
43 * | 1.1.0   | 2010-07-29 | * mantis bug:1686
44 * |         |            |   . bug reported on IfdReader
45 * |         |            |     When sub IFD (0x8769) refers to a sub IFD with
46 * |         |            |     an offset lower than the current IFD, the reader
47 * |         |            |     loops on the current data block and terminate
48 * |         |            |     with an error 500 ; fix consist to ignore this
49 * |         |            |     kind of offset, but it's not the right solution
50 * |         |            |     (right solution: to be able to read negative
51 * |         |            |     offset)
52 * |         |            |
53 * |         |            | * mantis feature : 1719
54 * |         |            |   . Coding a DateTime class ; used only if there is
55 * |         |            |     no PHP built-in DateTime class
56 * |         |            |
57 * |         |            | * add the "schema" property to Tag class
58 * |         |            |
59 * |         |            | * fixed bug about filtering schema
60 * |         |            |   . when loading metadata, filter on schema are now
61 * |         |            |     applied ; 'magic' metadata are computed even if
62 * |         |            |     the other schema are filtered
63 * |         |            |
64 * |         |            | * add the "getTag" function
65 * |         |            |
66 * | 1.1.1   | 2010-09-13 | * mantis bug:1826
67 * |         |            |   . digiKam XMP tags are not recognized
68 * |         |            |
69 * |         |            |
70 * |         |            |
71 * |         |            |
72 * |         |            |
73 * |         |            |
74 * +---------+------------+-----------------------------------------------------
75 *
76 *
77 * -----------------------------------------------------------------------------
78 *
79 * References about definition & interpretation of metadata tags :
80 *  - EXIF 2.20 Specification    => http://www.exif.org/Exif2-2.PDF
81 *  - TIFF 6.0 Specification     => http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
82 *  - Exiftool by Phil Harvey    => http://www.sno.phy.queensu.ca/~phil/exiftool/
83 *                                  http://owl.phy.queensu.ca/~phil/exiftool/TagNames
84 *  - Exiv2 by Andreas Huggel    => http://www.exiv2.org/
85 *  - MetaData working group     => http://www.metadataworkinggroup.org/specs/
86 *  - Adobe XMP Developer Center => http://www.adobe.com/devnet/xmp/
87 *  - Gezuz                      => http://gezus.one.free.fr/?Plugin-EXIF-pour-Spip-1-9-2
88 *  - JPEG format                => http://crousseau.free.fr/imgfmt_jpeg.htm
89 *  - International Press Telecomunication Council specifications
90 *                               => http://www.iptc.org/
91 *  - IPTC headers structure     => http://www.codeproject.com/KB/graphics/iptc.aspx?msg=1014929
92 *  - CPAN                       => http://search.cpan.org/dist/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/Structures.pod
93 *                               => http://search.cpan.org/~bettelli/Image-MetaData-JPEG/lib/Image/MetaData/JPEG/MakerNotes.pod
94 *
95 * -----------------------------------------------------------------------------
96 * To support internationalization the JpegMetaData package uses ".po" and ".mo"
97 * files, and use "php-gettext"
98 * ==> See files in External/php-gettext for more information about this project
99 * -----------------------------------------------------------------------------
100 *
101 * The JpegMetaData is the main class for reading metadata of a Jpeg file
102 *
103 * It provides two essentialy high level functions to read different kind of
104 * metadata (EXIF, IPTC, XMP) :
105 *  - (static) getTagList
106 *  - load
107 *  - getTags
108 *  - getTag
109 *
110 * -----------------------------------------------------------------------------
111 *
112 * .. Notes ..
113 *
114 * About tags and translation in local lang
115 * With the 'getTags()' method, the JpegMetaData returns an array of Tag objects
116 * found in the jpeg file.
117 *
118 * A Tag object have 2 properties that can be translated into a local language :
119 *  - the name, getted with 'getName()'
120 *  - the valueLabel, getted with 'getLabel()'
121 *
122 * Theses properties ARE NOT translated automatically.
123 *
124 * You can translate it with the Locale class, by using the static method 'get'
125 *
126 * Example :
127 *  Locale::get($myTag->getName()) will return the translated name of the Tag
128 *  Locale::get($myTag->getLabel()) will return the translated value of the Tag
129 *
130 * ===========> See Tag.class.php to know more about the Tag class <============
131 * ========> See Locale.class.php to know more about the Locale class <=========
132 *
133 *
134 * -----------------------------------------------------------------------------
135 */
136
137  define("JPEG_METADATA_DIR", dirname(__FILE__)."/");
138
139  require_once(JPEG_METADATA_DIR."Common/DateTime.class.php");
140  require_once(JPEG_METADATA_DIR."Readers/JpegReader.class.php");
141  require_once(JPEG_METADATA_DIR."TagDefinitions/MagicTags.class.php");
142
143  class JpegMetaData
144  {
145    const TAGFILTER_KNOWN       = 0x01;
146    const TAGFILTER_IMPLEMENTED = 0x02;
147    const TAGFILTER_ALL         = 0x03;
148
149    private $jpeg = null;
150    protected $tags = Array();
151    private $options = Array();
152
153    /**
154     * this static function returns an array of tags definitions
155     *
156     * the only parameter is an array to determine filter options
157     *
158     * ---------------------+---------------------------------------------------
159     * key                  | descriptions/values
160     * ---------------------+---------------------------------------------------
161     * filter               | Integer
162     *                      | This options is used to filter implemented tag
163     *                      |  JpegMetaData::TAGFILTER_ALL
164     *                      |  => returns all the tags
165     *                      |  JpegMetaData::TAGFILTER_IMPLEMENTED
166     *                      |  => returns only the implemented tags
167     *                      |
168     * optimizeIptcDateTime | Boolean
169     *                      | IPTC Date/Time are separated into 2 tags
170     *                      | if this option is set to true, only dates tags are
171     *                      | returned (assuming this option is used when an
172     *                      | image file is loaded)
173     *                      |
174     * exif                 | Boolean
175     * iptc                 | If set to true, the function returns all the tags
176     *                      | known for the specified type tag
177     * xmp                  |
178     * maker                | maker => returns specifics tags from all the known
179     * magic                |          makers
180     *                      |
181     * ---------------------+---------------------------------------------------
182     *
183     * returned value is an array
184     * each keys is a tag name and the associated value is a 2-level array
185     *  'implemented' => Boolean, allowing to know if the tags is implemented or
186     *                   not
187     *  'translatable'=> Boolean, allowing to know if the tag value can be
188     *                   translated
189     *  'name'        => String, the tag name translated in locale language
190     *
191     * @Param Array $options  (optional)
192     * @return Array(keyName => Array('implemented' => Boolean, 'name' => String))
193     */
194    static public function getTagList($options=Array())
195    {
196      $default=Array(
197        'filter' => self::TAGFILTER_ALL,
198        'optimizeIptcDateTime' => false,
199        'exif'  => true,
200        'iptc'  => true,
201        'xmp'   => true,
202        'maker' => true,
203        'magic' => true,
204      );
205
206      foreach($default as $key => $val)
207      {
208        if(array_key_exists($key, $options))
209          $default[$key]=$options[$key];
210      }
211
212      $list=Array();
213      $returned=Array();
214
215      if($default['exif'])
216      {
217        $list[]="exif";
218        $list[]="gps";
219      }
220
221      if($default['maker'])
222      {
223        $list[]=MAKER_PENTAX;
224        $list[]=MAKER_NIKON;
225        $list[]=MAKER_CANON;
226      }
227
228      if($default['iptc'])
229        $list[]="iptc";
230
231      if($default['xmp'])
232        $list[]="xmp";
233
234      if($default['magic'])
235        $list[]="magic";
236
237      foreach($list as $val)
238      {
239        unset($tmp);
240
241        switch($val)
242        {
243          case "exif":
244            $tmp=new IfdTags();
245            $schema=Schemas::EXIF;
246            break;
247          case "gps":
248            $tmp=new GpsTags();
249            $schema=Schemas::EXIF_GPS;
250            break;
251          case "iptc":
252            $tmp=new IptcTags();
253            $schema=Schemas::IPTC;
254            break;
255          case "xmp":
256            $tmp=new XmpTags();
257            $schema=Schemas::XMP;
258            break;
259          case "magic":
260            $tmp=new MagicTags();
261            $schema=Schemas::MAGIC;
262            break;
263          case MAKER_PENTAX:
264            include_once(JPEG_METADATA_DIR."TagDefinitions/PentaxTags.class.php");
265            $tmp=new PentaxTags();
266            $schema=Schemas::EXIF_MAKER.'.'.MAKER_PENTAX;
267            break;
268          case MAKER_NIKON:
269            include_once(JPEG_METADATA_DIR."TagDefinitions/NikonTags.class.php");
270            $tmp=new NikonTags();
271            $schema=Schemas::EXIF_MAKER.'.'.MAKER_NIKON;
272            break;
273          case MAKER_CANON:
274            include_once(JPEG_METADATA_DIR."TagDefinitions/CanonTags.class.php");
275            $tmp=new CanonTags();
276            $schema=Schemas::EXIF_MAKER.'.'.MAKER_CANON;
277            break;
278          default:
279            $tmp=null;
280            $schema="?";
281            break;
282        }
283
284        if(!is_null($tmp))
285          foreach($tmp->getTags() as $key => $tag)
286          {
287            if(self::filter(true, $tag['implemented'], $default['filter']))
288            {
289              if(array_key_exists('tagName', $tag))
290                $name=$tag['tagName'];
291              else
292                $name=$key;
293
294              if(array_key_exists('schema', $tag) and $val==Schemas::EXIF)
295                $subSchema=".".$tag['schema'];
296              else
297                $subSchema="";
298
299              if($val==Schemas::XMP)
300                $keyName=$schema.$subSchema.".".$key;
301              else
302                $keyName=$schema.$subSchema.".".$name;
303              $returned[$keyName]=Array(
304                'implemented' => $tag['implemented'],
305                'translatable' => $tag['translatable'],
306                'name' => $name
307              );
308            }
309          }
310      }
311
312      ksort($returned);
313
314      return($returned);
315    }
316
317
318    /**
319     * the filter function is used by the classe to determine if a tag is
320     * filtered or not
321     *
322     * @Param Boolean $known
323     * @Param Boolean $implemented
324     * @Param Integer $filter
325     *
326     */
327    static public function filter($known, $implemented, $filter)
328    {
329      return(($known and (($filter & self::TAGFILTER_KNOWN) == self::TAGFILTER_KNOWN )) or
330                ($implemented and (($filter & self::TAGFILTER_IMPLEMENTED) == self::TAGFILTER_IMPLEMENTED )));
331    }
332
333    /**
334     * the constructor need an optional filename and options
335     *
336     * if no filename is given, you can use the "load" function after the object
337     * is instancied
338     *
339     * if no options are given, the class use the default values
340     *
341     * ---------------------+---------------------------------------------------
342     * key                  | descriptions/values
343     * ---------------------+---------------------------------------------------
344     * filter               | Integer
345     *                      | This options is used to filter implemented tag
346     *                      |  JpegMetaData::TAGFILTER_ALL
347     *                      |  => returns all the tags
348     *                      |  JpegMetaData::TAGFILTER_IMPLEMENTED
349     *                      |  => returns only the implemented tags, not
350     *                      |     implemented tag are excluded
351     *                      |  JpegMetaData::TAGFILTER_KNOWN
352     *                      |  => returns only the known tags (implemented or
353     *                      |     not), unknown tag are excluded
354     *                      |
355     * optimizeIptcDateTime | Boolean
356     *                      | IPTC Date/Time are separated into 2 tags
357     *                      | if this option is set to true, only dates tags are
358     *                      | returned (in this case, time is included is the
359     *                      | date)
360     *                      |
361     * exif                 | Boolean
362     * iptc                 | If set to true, the function returns all the tags
363     * xmp                  | known for the specified type tag
364     * magic                | the exif parameter include the maker tags
365     *                      |
366     * ---------------------+---------------------------------------------------
367     *
368     * @Param String $file    (optional)
369     * @Param Array  $options (optional)
370     *
371     */
372    function __construct($file = "", $options = Array())
373    {
374      $this->load($file, $options);
375    }
376
377    function __destruct()
378    {
379      $this->unsetAll();
380    }
381
382    /**
383     * load a file
384     *
385     * options values are the same than the constructor's options
386     *
387     * @Param String $file
388     * @Param Array  $options (optional)
389     *
390     */
391    public function load($file, $options = Array())
392    {
393      $this->unsetAll();
394
395      $this->initializeOptions($options);
396      $this->tags = Array();
397      $this->jpeg = new JpegReader($file);
398
399      if($this->jpeg->isLoaded() and $this->jpeg->isValid())
400      {
401        foreach($this->jpeg->getAppMarkerSegments() as $key => $appMarkerSegment)
402        {
403          if($appMarkerSegment->dataLoaded())
404          {
405            $data=$appMarkerSegment->getData();
406
407            if($data instanceof TiffReader)
408            {
409              /*
410               * Load Exifs tags from Tiff block
411               */
412              if($data->getNbIFDs()>0 and
413                 ($this->options['magic'] or $this->options['exif'] or $this->options['maker']))
414              {
415                $this->loadIfdTags($data->getIFD(0), Schemas::EXIF_TIFF);
416              }
417            }
418            elseif($data instanceof XmpReader)
419            {
420              /*
421               * Load Xmp tags from Xmp block
422               */
423              if($this->options['magic'] or $this->options['xmp'])
424              {
425                $this->loadTags($data->getTags(), Schemas::XMP);
426              }
427            }
428            elseif($data instanceof IptcReader)
429            {
430              /*
431               * Load IPTC tags from IPTC block
432               */
433              if($this->options['optimizeIptcDateTime'])
434                $data->optimizeDateTime();
435
436              if($this->options['magic'] or $this->options['iptc'])
437              {
438                $this->loadTags($data->getTags(), Schemas::IPTC);
439              }
440            }
441          }
442        }
443
444        if($this->options['magic'])
445        {
446          $this->processMagicTags();
447        }
448
449        // clean all unwanted metadata
450        foreach($this->tags as $key => $tag)
451        {
452          if(isset($this->options[$tag->getSchema()]) and !$this->options[$tag->getSchema()]) unset($this->tags[$key]);
453        }
454
455        ksort($this->tags);
456      }
457    }
458
459    /**
460     * This function returns an array of tags found in the loaded file
461     *
462     * It's possible to made a second selection to filter items
463     *
464     * ---------------------+---------------------------------------------------
465     * key                  | descriptions/values
466     * ---------------------+---------------------------------------------------
467     * tagFilter            | Integer
468     *                      | This options is used to filter implemented tag
469     *                      |  JpegMetaData::TAGFILTER_ALL
470     *                      |  => returns all the tags
471     *                      |  JpegMetaData::TAGFILTER_IMPLEMENTED
472     *                      |  => returns only the implemented tags, not
473     *                      |     implemented tag are excluded
474     *                      |  JpegMetaData::TAGFILTER_KNOWN
475     *                      |  => returns only the known tags (implemented or
476     *                      |     not), unknown tag are excluded
477     *                      |
478     * ---------------------+---------------------------------------------------
479     *
480     * Note, the filter is applied on loaded tags. If a filter was applied when
481     * the file was loaded, you cannot expand the tag list, only reduce
482     * example :
483     *  $jpegmd = new JpegMetadata($file, Array('filter' => JpegMetaData::TAGFILTER_IMPLEMENTED));
484     *     => the unknown tag are not loaded
485     *  $jpegmd->getTags(JpegMetaData::TAGFILTER_ALL)
486     *     => unknown tag will not be restitued because they are not loaded...
487     *
488     * the function returns an array of Tag.
489     *
490     *
491     * ===========> See the Tag.class.php to know all about a tag <=============
492     *
493     * @Param Integer $tagFilter (optional)
494     *
495     */
496    public function getTags($tagFilter = self::TAGFILTER_ALL)
497    {
498      $returned=Array();
499      foreach($this->tags as $key => $val)
500      {
501        if(self::filter($val->isKnown(), $val->isImplemented(), $tagFilter))
502        {
503          $returned[$key]=$val;
504        }
505      }
506      return($returned);
507    }
508
509
510    /**
511     * This function returns the tag object for the given tag name
512     *
513     * @param String $key : the tag name (ie: "exif.exif.ApertureValue")
514     * @return Tag : the tag object, null if the tag doesn't exist
515     */
516    public function getTag($key)
517    {
518      if(array_key_exists($key, $this->tags))
519      {
520        return($this->tags[$key]);
521      }
522      return(null);
523    }
524
525    /**
526     * initialize the options...
527     *
528     * @Param Array $options (optional)
529     *
530     */
531    private function initializeOptions($options=Array())
532    {
533      $this->options = Array(
534        'filter' => self::TAGFILTER_ALL,
535        'optimizeIptcDateTime' => false,
536        'exif'  => true,
537        'iptc'  => true,
538        'xmp'   => true,
539        'magic' => true
540      );
541
542      foreach($this->options as $key => $val)
543      {
544        if(array_key_exists($key, $options))
545          $this->options[$key]=$options[$key];
546      }
547    }
548
549    /**
550     * load tags from an IFD structure
551     *
552     * see Tiff.class.php and IfdReader.class.php for more informations
553     *
554     * @Param IfdReader $ifd
555     * @Param String    $exifKey
556     *
557     */
558    private function loadIfdTags($ifd, $exifKey)
559    {
560      foreach($ifd->getTags() as $key => $tag)
561      {
562        if((self::filter($tag->getTag()->isKnown(), $tag->getTag()->isImplemented(), $this->options['filter'])) or
563           ($tag->getTag()->getName()=='Exif IFD Pointer' or
564            $tag->getTag()->getName()=='MakerNote' or
565            $tag->getTag()->getName()=='GPS IFD Pointer'))
566        {
567          /*
568           * only tag responding to the filter are selected
569           * note the tags 'Exif IFD Pointer', 'MakerNote' & 'GPS IFD Pointer'
570           * are not declared as implemented (otherwise they are visible with
571           * the static 'getTagList' function) but must be selected even if
572           * filter says "implemented only"
573           */
574          if($tag->getTag()->getLabel() instanceof IfdReader)
575          {
576            switch($tag->getTag()->getName())
577            {
578              case 'Exif IFD Pointer':
579                $exifKey2=Schemas::EXIF_EXIF;
580                break;
581              case 'MakerNote':
582                $exifKey2=Schemas::EXIF_MAKER.".".$tag->getTag()->getLabel()->getMaker();
583                break;
584              case 'GPS IFD Pointer':
585                $exifKey2=Schemas::EXIF_GPS;
586                break;
587              default:
588                $exifKey2=$exifKey;
589                break;
590            }
591            $this->loadIfdTags($tag->getTag()->getLabel(), $exifKey2);
592          }
593          else
594          {
595            $this->tags[$exifKey.".".$tag->getTag()->getName()]=$tag->getTag();
596          }
597        }
598      }
599    }
600
601    /**
602     * Used to load tags from an IPTc or XMP structure
603     *
604     * see IptcReader.class.php and XmpReader.class.php
605     *
606     * @Param Tag[]  $ifd
607     * @Param String $tagKey
608     *
609     */
610    private function loadTags($tags, $tagKey)
611    {
612      foreach($tags as $key => $tag)
613      {
614        if(self::filter($tag->isKnown(), $tag->isImplemented(), $this->options['filter']))
615        {
616          $this->tags[$tagKey.".".$tag->getName()]=$tag;
617        }
618      }
619    }
620
621    /**
622     * MagicTags are build with this function
623     */
624    protected function processMagicTags()
625    {
626      $magicTags=new MagicTags();
627
628      foreach($magicTags->getTags() as $key => $val)
629      {
630        $tag=new Tag($key,0,$key);
631
632        for($i=0; $i<count($val['tagValues']); $i++)
633        {
634          $found=true;
635          preg_match_all('/{([a-z0-9:\.\s\/]*)(\[.*\])?}/i', $val['tagValues'][$i], $returned, PREG_PATTERN_ORDER);
636          foreach($returned[1] as $testKey)
637          {
638            $found=$found & array_key_exists($testKey, $this->tags);
639          }
640          if(count($returned[1])==0) $found=false;
641
642          if($found)
643          {
644            $returned=trim(preg_replace_callback(
645                '/{([a-z0-9:\.\s\/\[\]]*)}/i',
646                Array(&$this, "processMagicTagsCB"),
647                $val['tagValues'][$i]
648            ));
649
650            $returned=$this->processSpecialMagicTag($key, $returned);
651
652            $tag->setValue($returned);
653            $tag->setLabel($returned);
654            $tag->setKnown(true);
655            $tag->setImplemented($val['implemented']);
656            $tag->setTranslatable($val['translatable']);
657            $tag->setSchema(Schemas::MAGIC);
658
659            $i=count($val['tagValues']);
660          }
661        }
662
663        if($tag->isImplemented() and $found)
664        {
665          $this->tags["magic.".$key]=$tag;
666        }
667
668        unset($tag);
669      }
670      unset($magicTags);
671    }
672
673    /**
674     * this function is called by the processMagicTags to replace tagId by the
675     * tag values
676     *
677     * @param Array $matches : array[1] = the tagId
678     * @return String : the tag value
679     */
680    protected function processMagicTagsCB($matches)
681    {
682      $label="";
683      preg_match_all('/([a-z0-9:\.\s\/]*)\[(.*)\]/i', $matches[1], $result, PREG_PATTERN_ORDER);
684      if(count($result[0])>0)
685      {
686
687        if(array_key_exists($result[1][0], $this->tags))
688        {
689          $tag=$this->tags[$result[1][0]]->getLabel();
690
691          preg_match_all('/([a-z0-9:\.\s\/]*)\[(.*)\]/i', $result[2][0], $result2, PREG_PATTERN_ORDER);
692
693          if(count($result2[0])>0)
694          {
695            if(array_key_exists($result2[2][0], $tag[$result2[1][0]] ))
696              $label=$tag[$result2[1][0]][$result2[2][0]];
697          }
698          else
699          {
700            if(array_key_exists($result[2][0], $tag))
701              $label=$tag[$result[2][0]];
702          }
703        }
704      }
705      else
706      {
707        if(array_key_exists($matches[1], $this->tags))
708        {
709          $label=$this->tags[$matches[1]]->getLabel();
710        }
711      }
712
713      if($label instanceof DateTime)
714        return($label->format("Y-m-d H:i:s"));
715
716      $label=XmpTags::getAltValue($label, L10n::getLanguage());
717
718      if(is_array($label))
719        return(implode(", ", $label));
720
721      return(trim($label));
722    }
723
724    /**
725     *
726     *
727     */
728    protected function processSpecialMagicTag($key, $value)
729    {
730      switch($key)
731      {
732        case "GPS.LatitudeNum":
733        case "GPS.LongitudeNum":
734          preg_match_all('/(\d{1,3})°\s*(\d{1,2})\'(?:\s*(\d+(?:\.\d+)?)")?[\s\|\.]*(N|S|E|W)/i', $value, $result);
735          $num=(float)$result[1][0] + ((float) $result[2][0])/60;
736          if($result[3][0]!="") $num+= ((float) $result[3][0])/3600;
737          if($result[4][0]=="W" or $result[4][0]=="S") $num=-$num;
738          return($num);
739        default:
740          return($value);
741      }
742    }
743
744
745    /**
746     * used by the destructor to clean variables
747     */
748    private function unsetAll()
749    {
750      unset($this->tags);
751      unset($this->jpeg);
752      unset($this->options);
753    }
754
755
756  } // class JpegMetaData
757
758?>
Note: See TracBrowser for help on using the repository browser.