ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
module.graphic.jpg.php
Go to the documentation of this file.
1 <?php
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
8 // See readme.txt for more details //
10 // //
11 // module.graphic.jpg.php //
12 // module for analyzing JPEG Image files //
13 // dependencies: PHP compiled with --enable-exif (optional) //
14 // module.tag.xmp.php (optional) //
15 // ///
17 
18 
20 {
21 
22 
23  public function Analyze() {
24  $info = &$this->getid3->info;
25 
26  $info['fileformat'] = 'jpg';
27  $info['video']['dataformat'] = 'jpg';
28  $info['video']['lossless'] = false;
29  $info['video']['bits_per_sample'] = 24;
30  $info['video']['pixel_aspect_ratio'] = (float) 1;
31 
32  $this->fseek($info['avdataoffset']);
33 
34  $imageinfo = array();
35  //list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo);
36  list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474
37 
38 
39  if (isset($imageinfo['APP13'])) {
40  // http://php.net/iptcparse
41  // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
42  $iptc_parsed = iptcparse($imageinfo['APP13']);
43  if (is_array($iptc_parsed)) {
44  foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
45  list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
46  $iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
47  foreach ($iptc_values as $key => $value) {
48  $IPTCrecordName = $this->IPTCrecordName($iptc_record);
49  $IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey);
50  if (isset($info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName])) {
51  $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value;
52  } else {
53  $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName] = array($value);
54  }
55  }
56  }
57  }
58  }
59 
60  $returnOK = false;
61  switch ($type) {
62  case IMG_JPG:
63  $info['video']['resolution_x'] = $width;
64  $info['video']['resolution_y'] = $height;
65 
66  if (isset($imageinfo['APP1'])) {
67  if (function_exists('exif_read_data')) {
68  if (substr($imageinfo['APP1'], 0, 4) == 'Exif') {
69 //$this->warning('known issue: https://bugs.php.net/bug.php?id=62523');
70 //return false;
71  set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
72  if (!(error_reporting() & $errno)) {
73  // error is not specified in the error_reporting setting, so we ignore it
74  return false;
75  }
76 
77  $errcontext['info']['warning'][] = 'Error parsing EXIF data ('.$errstr.')';
78  });
79 
80  $info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false);
81 
82  restore_error_handler();
83  } else {
84  $this->warning('exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")');
85  }
86  } else {
87  $this->warning('EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'));
88  }
89  }
90  $returnOK = true;
91  break;
92 
93  default:
94  break;
95  }
96 
97 
98  $cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL');
99  foreach ($cast_as_appropriate_keys as $exif_key) {
100  if (isset($info['jpg']['exif'][$exif_key])) {
101  foreach ($info['jpg']['exif'][$exif_key] as $key => $value) {
102  $info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value);
103  }
104  }
105  }
106 
107 
108  if (isset($info['jpg']['exif']['GPS'])) {
109 
110  if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) {
111  for ($i = 0; $i < 4; $i++) {
112  $version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1));
113  }
114  $info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts);
115  }
116 
117  if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) {
118  $explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']);
119  $computed_time[5] = (isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : '');
120  $computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : '');
121  $computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : '');
122 
123  $computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0);
124  if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) {
125  foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) {
126  $computed_time[$key] = getid3_lib::DecimalizeFraction($value);
127  }
128  }
129  $info['jpg']['exif']['GPS']['computed']['timestamp'] = gmmktime($computed_time[0], $computed_time[1], $computed_time[2], $computed_time[3], $computed_time[4], $computed_time[5]);
130  }
131 
132  if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) {
133  $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1);
134  foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) {
135  $computed_latitude[$key] = getid3_lib::DecimalizeFraction($value);
136  }
137  $info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600));
138  }
139 
140  if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) {
141  $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1);
142  foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) {
143  $computed_longitude[$key] = getid3_lib::DecimalizeFraction($value);
144  }
145  $info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600));
146  }
147  if (isset($info['jpg']['exif']['GPS']['GPSAltitudeRef'])) {
148  $info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level
149  }
150  if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) {
151  $direction_multiplier = (!empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1); // 0 = above sea level; 1 = below sea level
152  $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']);
153  }
154 
155  }
156 
157 
158  getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, true);
159  if (isset($info['filenamepath'])) {
160  $image_xmp = new Image_XMP($info['filenamepath']);
161  $xmp_raw = $image_xmp->getAllTags();
162  foreach ($xmp_raw as $key => $value) {
163  if (strpos($key, ':')) {
164  list($subsection, $tagname) = explode(':', $key);
165  $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value);
166  } else {
167  $this->warning('XMP: expecting "<subsection>:<tagname>", found "'.$key.'"');
168  }
169  }
170  }
171 
172  if (!$returnOK) {
173  unset($info['fileformat']);
174  return false;
175  }
176  return true;
177  }
178 
179 
180  public function CastAsAppropriate($value) {
181  if (is_array($value)) {
182  return $value;
183  } elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) {
184  return getid3_lib::DecimalizeFraction($value);
185  } elseif (preg_match('#^[0-9]+$#', $value)) {
186  return getid3_lib::CastAsInt($value);
187  } elseif (preg_match('#^[0-9\.]+$#', $value)) {
188  return (float) $value;
189  }
190  return $value;
191  }
192 
193 
194  public function IPTCrecordName($iptc_record) {
195  // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
196  static $IPTCrecordName = array();
197  if (empty($IPTCrecordName)) {
198  $IPTCrecordName = array(
199  1 => 'IPTCEnvelope',
200  2 => 'IPTCApplication',
201  3 => 'IPTCNewsPhoto',
202  7 => 'IPTCPreObjectData',
203  8 => 'IPTCObjectData',
204  9 => 'IPTCPostObjectData',
205  );
206  }
207  return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
208  }
209 
210 
211  public function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
212  // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
213  static $IPTCrecordTagName = array();
214  if (empty($IPTCrecordTagName)) {
215  $IPTCrecordTagName = array(
216  1 => array( // IPTC EnvelopeRecord Tags
217  0 => 'EnvelopeRecordVersion',
218  5 => 'Destination',
219  20 => 'FileFormat',
220  22 => 'FileVersion',
221  30 => 'ServiceIdentifier',
222  40 => 'EnvelopeNumber',
223  50 => 'ProductID',
224  60 => 'EnvelopePriority',
225  70 => 'DateSent',
226  80 => 'TimeSent',
227  90 => 'CodedCharacterSet',
228  100 => 'UniqueObjectName',
229  120 => 'ARMIdentifier',
230  122 => 'ARMVersion',
231  ),
232  2 => array( // IPTC ApplicationRecord Tags
233  0 => 'ApplicationRecordVersion',
234  3 => 'ObjectTypeReference',
235  4 => 'ObjectAttributeReference',
236  5 => 'ObjectName',
237  7 => 'EditStatus',
238  8 => 'EditorialUpdate',
239  10 => 'Urgency',
240  12 => 'SubjectReference',
241  15 => 'Category',
242  20 => 'SupplementalCategories',
243  22 => 'FixtureIdentifier',
244  25 => 'Keywords',
245  26 => 'ContentLocationCode',
246  27 => 'ContentLocationName',
247  30 => 'ReleaseDate',
248  35 => 'ReleaseTime',
249  37 => 'ExpirationDate',
250  38 => 'ExpirationTime',
251  40 => 'SpecialInstructions',
252  42 => 'ActionAdvised',
253  45 => 'ReferenceService',
254  47 => 'ReferenceDate',
255  50 => 'ReferenceNumber',
256  55 => 'DateCreated',
257  60 => 'TimeCreated',
258  62 => 'DigitalCreationDate',
259  63 => 'DigitalCreationTime',
260  65 => 'OriginatingProgram',
261  70 => 'ProgramVersion',
262  75 => 'ObjectCycle',
263  80 => 'By-line',
264  85 => 'By-lineTitle',
265  90 => 'City',
266  92 => 'Sub-location',
267  95 => 'Province-State',
268  100 => 'Country-PrimaryLocationCode',
269  101 => 'Country-PrimaryLocationName',
270  103 => 'OriginalTransmissionReference',
271  105 => 'Headline',
272  110 => 'Credit',
273  115 => 'Source',
274  116 => 'CopyrightNotice',
275  118 => 'Contact',
276  120 => 'Caption-Abstract',
277  121 => 'LocalCaption',
278  122 => 'Writer-Editor',
279  125 => 'RasterizedCaption',
280  130 => 'ImageType',
281  131 => 'ImageOrientation',
282  135 => 'LanguageIdentifier',
283  150 => 'AudioType',
284  151 => 'AudioSamplingRate',
285  152 => 'AudioSamplingResolution',
286  153 => 'AudioDuration',
287  154 => 'AudioOutcue',
288  184 => 'JobID',
289  185 => 'MasterDocumentID',
290  186 => 'ShortDocumentID',
291  187 => 'UniqueDocumentID',
292  188 => 'OwnerID',
293  200 => 'ObjectPreviewFileFormat',
294  201 => 'ObjectPreviewFileVersion',
295  202 => 'ObjectPreviewData',
296  221 => 'Prefs',
297  225 => 'ClassifyState',
298  228 => 'SimilarityIndex',
299  230 => 'DocumentNotes',
300  231 => 'DocumentHistory',
301  232 => 'ExifCameraInfo',
302  ),
303  3 => array( // IPTC NewsPhoto Tags
304  0 => 'NewsPhotoVersion',
305  10 => 'IPTCPictureNumber',
306  20 => 'IPTCImageWidth',
307  30 => 'IPTCImageHeight',
308  40 => 'IPTCPixelWidth',
309  50 => 'IPTCPixelHeight',
310  55 => 'SupplementalType',
311  60 => 'ColorRepresentation',
312  64 => 'InterchangeColorSpace',
313  65 => 'ColorSequence',
314  66 => 'ICC_Profile',
315  70 => 'ColorCalibrationMatrix',
316  80 => 'LookupTable',
317  84 => 'NumIndexEntries',
318  85 => 'ColorPalette',
319  86 => 'IPTCBitsPerSample',
320  90 => 'SampleStructure',
321  100 => 'ScanningDirection',
322  102 => 'IPTCImageRotation',
323  110 => 'DataCompressionMethod',
324  120 => 'QuantizationMethod',
325  125 => 'EndPoints',
326  130 => 'ExcursionTolerance',
327  135 => 'BitsPerComponent',
328  140 => 'MaximumDensityRange',
329  145 => 'GammaCompensatedValue',
330  ),
331  7 => array( // IPTC PreObjectData Tags
332  10 => 'SizeMode',
333  20 => 'MaxSubfileSize',
334  90 => 'ObjectSizeAnnounced',
335  95 => 'MaximumObjectSize',
336  ),
337  8 => array( // IPTC ObjectData Tags
338  10 => 'SubFile',
339  ),
340  9 => array( // IPTC PostObjectData Tags
341  10 => 'ConfirmedObjectSize',
342  ),
343  );
344 
345  }
346  return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
347  }
348 
349 }
$type
IPTCrecordName($iptc_record)
static DecimalizeFraction($fraction)
Definition: getid3.lib.php:96
warning($text)
Definition: getid3.php:1758
IPTCrecordTagName($iptc_record, $iptc_tagkey)
static IncludeDependency($filename, $sourcefile, $DieOnFailure=false)
getID3() by James Heinrich info@getid3.org //
static CastAsInt($floatnum)
Definition: getid3.lib.php:65
$i
Definition: disco.tpl.php:19
fseek($bytes, $whence=SEEK_SET)
Definition: getid3.php:1711
CastAsAppropriate($value)
$info
Definition: index.php:5
$key
Definition: croninfo.php:18
getID3() by James Heinrich info@getid3.org //