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

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

Implement feature:1965

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