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

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

fix small bugs not referenced + bug:1870

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