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

Last change on this file since 7511 was 7511, checked in by grum, 13 years ago

Update JpegMetadata class to implement COM segment as a tag and keywords in "magic" tags
feature:1975, feature:1976

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