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

Last change on this file was 17554, checked in by grum, 12 years ago

feature:2701
bug:2702
bug:2720
bug:2722

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