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

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

JpegMetadata => Implement multiple IPTC keywords managements & manage ISO-8859-1 to UTF8 charset
bug:1859, bug:1861

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