source: extensions/AMetaData/JpegMetaData/Readers/IptcReader.class.php @ 6729

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

feature:1777

  • Weight of the metadata database can becomes very heavy
  • Property svn:executable set to *
File size: 11.0 KB
Line 
1<?php
2/*
3 * --:: JPEG MetaDatas ::-------------------------------------------------------
4 *
5 *  Author    : Grum
6 *   email    : grum at piwigo.org
7 *   website  : http://photos.grum.fr
8 *
9 *   << May the Little SpaceFrog be with you ! >>
10 *
11 *
12 * +-----------------------------------------------------------------------+
13 * | JpegMetaData - a PHP based Jpeg Metadata manager                      |
14 * +-----------------------------------------------------------------------+
15 * | Copyright(C) 2010  Grum - http://www.grum.fr                          |
16 * +-----------------------------------------------------------------------+
17 * | This program is free software; you can redistribute it and/or modify  |
18 * | it under the terms of the GNU General Public License as published by  |
19 * | the Free Software Foundation                                          |
20 * |                                                                       |
21 * | This program is distributed in the hope that it will be useful, but   |
22 * | WITHOUT ANY WARRANTY; without even the implied warranty of            |
23 * | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
24 * | General Public License for more details.                              |
25 * |                                                                       |
26 * | You should have received a copy of the GNU General Public License     |
27 * | along with this program; if not, write to the Free Software           |
28 * | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
29 * | USA.                                                                  |
30 * +-----------------------------------------------------------------------+
31 *
32 *
33 * -----------------------------------------------------------------------------
34 *
35 * The IptcReader class is the dedicated class to read IPTC from the APP13
36 * segment
37 *
38 * The APP13 segment (the 'photoshop' segment) contain 8BIM blocks.
39 * The IPTC are stored inside the specific 0x0404 block. If there is more than
40 * one 8BIM 0x0404 block, the IptcReader reads all blocks and merge IPTC tags.
41 *
42 * =======> See HeightBIMReader.class.php to know more about 8BIM blocks <======
43 *
44 * -----------------------------------------------------------------------------
45 *
46 * .. Notes ..
47 *
48 * The IptcReader class is derived from the GenericReader class.
49 *
50 * ======> See GenericReader.class.php to know more about common methods <======
51 *
52 * This class provides theses public functions :
53 *  - optimizeDateTime
54 *
55 * -----------------------------------------------------------------------------
56 */
57
58  require_once(JPEG_METADATA_DIR."Common/Data.class.php");
59  require_once(JPEG_METADATA_DIR."Readers/HeightBIMReader.class.php");
60  require_once(JPEG_METADATA_DIR."TagDefinitions/IptcTags.class.php");
61
62
63  class IptcReader extends GenericReader
64  {
65    const HEADER_1 = "Photoshop 3.0\x00";
66    const HEADER_2 = "Adobe_Photoshop2.5:\x00";
67    protected $schema = Schemas::IPTC;
68
69    private $header = "";
70
71    /**
72     * The constructor needs, like the ancestor, the datas to be parsed
73     *
74     * @param String $data
75     */
76    function __construct($data)
77    {
78      parent::__construct($data);
79
80      /*
81       * read the header
82       * if header is a valid header, read entries
83       */
84      if($this->readHeader())
85      {
86        $this->initializeEntries();
87      }
88    }
89
90    function __destruct()
91    {
92      parent::__destruct();
93    }
94
95
96    public function toString()
97    {
98      $returned="IPTC ; NbEntries: ".sprintf("%02d", $this->nbEntries);
99      return($returned);
100    }
101
102    /**
103     * All IPTC Date & Time are separated into distinct tags
104     * this function complete "date" tags with the associated "time" tags, and
105     * delete the "time" tags
106     *
107     * Example :
108     *  0x146 "Date Sent" = 2009/12/24 => tag value : 2009/12/24 00:00:00
109     *  0x150 "Time Sent" = 19:43:28   => tag value : 0001/01/01 19:43:28
110     *
111     * Optimize Date merge date & time => tag value : 2009/12/24 19:43:28
112     *
113     */
114    public function optimizeDateTime()
115    {
116      $assoc=Array(
117        Array(0x0146, 0x0150),
118        Array(0x021E, 0x0223),
119        Array(0x0225, 0x0226),
120        Array(0x0237, 0x023C),
121        Array(0x023E, 0x023F),
122      );
123
124      foreach($assoc as $val)
125      {
126        $tagD=$this->getTagIndexById($val[0]);
127        $tagT=$this->getTagIndexById($val[1]);
128
129        if($tagD>-1 and $tagT>-1)
130        {
131          /*
132           * can't use the timestamp function because not compatible with php < 5.3
133           */
134          if($this->entries[$tagD]->getLabel() instanceof DateTime and
135             $this->entries[$tagT]->getLabel() instanceof DateTime)
136          {
137            $this->entries[$tagD]->getLabel()->setTime(
138              (int)$this->entries[$tagT]->getLabel()->format("H"),
139              (int)$this->entries[$tagT]->getLabel()->format("i"),
140              (int)$this->entries[$tagT]->getLabel()->format("s")
141            );
142          }
143          array_splice($this->entries, $tagT, 1);
144        }
145        unset($tagD);
146        unset($tagT);
147      }
148    }
149
150
151    /**
152     * initialize the definition for IPTC tags
153     */
154    protected function initializeTagDef()
155    {
156      $this->tagDef = new IptcTags();
157    }
158
159    /**
160     * read the header of the APP13 segment, and try to determinate wich kind of
161     * data are stored
162     *
163     * at now, only "Photoshop 3.0" data structure is known
164     * the "Adobe_Photoshop2.5" data structure is not recognized yet
165     *
166     * @return Boolean : true if the header is known
167     */
168    private function readHeader()
169    {
170      $this->data->seek();
171      $header=$this->data->readASCII(strlen(self::HEADER_1));
172      if($header==self::HEADER_1)
173      {
174        $this->header=$header;
175        return(true);
176      }
177
178      $this->data->seek();
179      $header=$this->data->readASCII(strlen(self::HEADER_2));
180      if($header==self::HEADER_2)
181      {
182        $this->header=$header;
183        /*
184         * structure from an HEADER_2 is not known....
185         */
186        return(false);
187      }
188
189      return(false);
190    }
191
192    /**
193     * reads all the 8BIM blocks of the segment. If the 8BIM block is an IPTC
194     * block, read all the IPTC entries and set the Tag properties
195     *
196     * An entry is a Tag object
197     *
198     * Add the entry to the entries array
199     *
200     */
201    protected function initializeEntries()
202    {
203      $blocks=explode("8BIM", $this->data->readASCII());
204      foreach($blocks as $key=> $val)
205      {
206        $block=new HeightBIMReader("8BIM".$val);
207        if($block->isValid())
208        {
209          /* merge entries from all 8BIM blocks */
210          $this->entries=array_merge($this->entries, $block->getTags());
211        }
212        unset($block);
213      }
214      unset($blocks);
215
216      foreach($this->entries as $key => $tag)
217      {
218        $this->setTagProperties($tag);
219      }
220    }
221
222    /**
223     * Interprets the tag values into 'human readable values'
224     *
225     * @param Tag $entry
226     */
227    private function setTagProperties($tag)
228    {
229      /*
230       * if the given tag id is defined, analyzing its values
231       */
232      if($this->tagDef->tagIdExists($tag->getId()))
233      {
234        $tagProperties=$this->tagDef->getTagById($tag->getId());
235
236        $tag->setKnown(true);
237        $tag->setName($tagProperties['tagName']);
238        $tag->setImplemented($tagProperties['implemented']);
239        $tag->setTranslatable($tagProperties['translatable']);
240        $tag->setSchema($this->schema);
241
242        /*
243         * if there is values defined for the tag, analyze it
244         */
245        if(array_key_exists('tagValues', $tagProperties))
246        {
247          if(array_key_exists($tag->getValue(), $tagProperties['tagValues']))
248          {
249            $tag->setLabel($tagProperties['tagValues'][$tag->getValue()]);
250          }
251          else
252          {
253            $tag->setLabel("[unknow value 0x".sprintf("%04x", $tag->getValue())."]");
254          }
255        }
256        else
257        {
258          /*
259           * there is no values defined for the tag, analyzing it with dedicated
260           * function
261           */
262          $tag->setLabel($this->processSpecialTag($tag->getId(), $tag->getValue(), 0, 0));
263        }
264      }
265    }
266
267    /**
268     * this function can be overrided to process special tags
269     */
270    protected function processSpecialTag($tagId, $values, $type, $valuesOffset=0)
271    {
272      switch($tagId)
273      {
274        /*
275         * Tags managed
276         */
277        case 0x0105: // 2:05  - Destination
278        case 0x011E: // 1:30  - Service Identifier
279        case 0x0128: // 1:40  - Envelope Number
280        case 0x0132: // 1:50  - Product I.D.
281        case 0x0205: // 2:05  - Title
282        case 0x0207: // 2:07  - Edit Status
283        case 0x020F: // 2:15  - Category
284        case 0x0214: // 2:20  - Supplemental Category
285        case 0x0216: // 2:22  - Fixture Identifier
286        case 0x0219: // 2:25  - Keywords
287        case 0x021A: // 2:25  - Content Location Code
288        case 0x021B: // 2:25  - Content Location Name
289        case 0x0228: // 2:40  - Special Instructions
290        case 0x0241: // 2:65  - Originating Program
291        case 0x0246: // 2:70  - Program Version
292        case 0x0250: // 2:80  - By-line
293        case 0x0255: // 2:80  - By-line Title
294        case 0x025A: // 2:90  - City
295        case 0x025C: // 2:92  - Sublocation
296        case 0x025F: // 2:95  - Province/State
297        case 0x0264: // 2:100 - Country Code
298        case 0x0265: // 2:101 - Country
299        case 0x0267: // 2:103 - Original Transmission Reference
300        case 0x0269: // 2:105 - Headline
301        case 0x026E: // 2:110 - credit
302        case 0x0273: // 2:115 - source
303        case 0x0274: // 2:116 - Copyright Notice
304        case 0x0276: // 2:118 - Contact
305        case 0x0278: // 2:120 - Description
306        case 0x027A: // 2:122 - Writer/Editor
307        case 0x0287: // 2:150 - Language Identifier
308          $returned=$values;
309          break;
310        case 0x0114: // 1:20  - File Format
311          $tag=$this->tagDef->getTagById(0x0114);
312          $returned=$tag['tagValues.special'][ConvertData::toUShort($values, BYTE_ORDER_BIG_ENDIAN)];
313          unset($tag);
314          break;
315        case 0x0203: // 2:03  - Object Type Reference
316        case 0x0204: // 2:04  - Intellectual Genre
317          $returned=explode(":", $values);
318          break;
319        case 0x0146: // 1:70  - Date Sent
320        case 0x021E: // 2:30  - Release Date
321        case 0x0225: // 2:37  - Expiration Date
322        case 0x0237: // 2:55  - Date Created
323        case 0x023E: // 2:62  - Digital Creation Date
324          $returned=ConvertData::toDateTime($values);
325          break;
326        case 0x0150: // 1:80  - Time Sent
327        case 0x0223: // 2:35  - Release Time
328        case 0x0226: // 2:38  - Expiration Time
329        case 0x023C: // 2:60  - Time Created
330        case 0x023F: // 2:63  - Digital Creation Time
331          $returned=ConvertData::toDateTime("00010101T".$values);
332          break;
333        /*
334         * Tags not managed
335         */
336        default:
337          $returned="Not yet implemented;".ConvertData::toHexDump($tagId, ByteType::USHORT)." => ".ConvertData::toHexDump($values, $type, 64)." [$values]";
338          break;
339      }
340      return($returned);
341    }
342  }
343
344
345
346
347?>
Note: See TracBrowser for help on using the repository browser.