| [ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * JPEG metadata reader/writer 4 * 5 * @license PHP license 2.0 (http://www.php.net/license/2_02.txt) 6 * @link http://www.zonageek.com/software/php/jpeg/index.php 7 * @author Sebastian Delmont <sdelmont@zonageek.com> 8 * @author Andreas Gohr <andi@splitbrain.org> 9 * @author Hakan Sandell <hakan.sandell@mydata.se> 10 * @todo Add support for Maker Notes, Extend for GIF and PNG metadata 11 */ 12 13 // This class is a modified and enhanced version of the JPEG class by 14 // Sebastian Delmont. Original Copyright notice follows: 15 // 16 // +----------------------------------------------------------------------+ 17 // | PHP version 4.0 | 18 // +----------------------------------------------------------------------+ 19 // | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group | 20 // +----------------------------------------------------------------------+ 21 // | This source file is subject to version 2.0 of the PHP license, | 22 // | that is bundled with this package in the file LICENSE, and is | 23 // | available at through the world-wide-web at | 24 // | http://www.php.net/license/2_02.txt. | 25 // | If you did not receive a copy of the PHP license and are unable to | 26 // | obtain it through the world-wide-web, please send a note to | 27 // | license@php.net so we can mail you a copy immediately. | 28 // +----------------------------------------------------------------------+ 29 // | Authors: Sebastian Delmont <sdelmont@zonageek.com> | 30 // +----------------------------------------------------------------------+ 31 32 class JpegMeta 33 { 34 var $_fileName; 35 var $_fp = null; 36 var $_type = 'unknown'; 37 38 var $_markers; 39 var $_info; 40 41 42 /** 43 * Constructor 44 * 45 * @author Sebastian Delmont <sdelmont@zonageek.com> 46 */ 47 function JpegMeta($fileName) 48 { 49 50 $this->_fileName = $fileName; 51 52 $this->_fp = null; 53 $this->_type = 'unknown'; 54 55 unset($this->_info); 56 unset($this->_markers); 57 } 58 59 /** 60 * Returns all gathered info as multidim array 61 * 62 * @author Sebastian Delmont <sdelmont@zonageek.com> 63 */ 64 function & getRawInfo() 65 { 66 $this->_parseAll(); 67 68 if ($this->_markers == null) { 69 return false; 70 } 71 72 return $this->_info; 73 } 74 75 /** 76 * Returns basic image info 77 * 78 * @author Sebastian Delmont <sdelmont@zonageek.com> 79 */ 80 function & getBasicInfo() 81 { 82 $this->_parseAll(); 83 84 $info = array(); 85 86 if ($this->_markers == null) { 87 return false; 88 } 89 90 $info['Name'] = $this->_info['file']['Name']; 91 if (isset($this->_info['file']['Url'])) { 92 $info['Url'] = $this->_info['file']['Url']; 93 $info['NiceSize'] = "???KB"; 94 } 95 else { 96 $info['Size'] = $this->_info['file']['Size']; 97 $info['NiceSize'] = $this->_info['file']['NiceSize']; 98 } 99 100 if (@isset($this->_info['sof']['Format'])) { 101 $info['Format'] = $this->_info['sof']['Format'] . " JPEG"; 102 } 103 else { 104 $info['Format'] = $this->_info['sof']['Format'] . " JPEG"; 105 } 106 107 if (@isset($this->_info['sof']['ColorChannels'])) { 108 $info['ColorMode'] = ($this->_info['sof']['ColorChannels'] > 1) ? "Color" : "B&W"; 109 } 110 111 $info['Width'] = $this->getWidth(); 112 $info['Height'] = $this->getHeight(); 113 $info['DimStr'] = $this->getDimStr(); 114 115 $dates = $this->getDates(); 116 117 $info['DateTime'] = $dates['EarliestTime']; 118 $info['DateTimeStr'] = $dates['EarliestTimeStr']; 119 120 $info['HasThumbnail'] = $this->hasThumbnail(); 121 122 return $info; 123 } 124 125 126 /** 127 * Convinience function to access nearly all available Data 128 * through one function 129 * 130 * @author Andreas Gohr <andi@splitbrain.org> 131 */ 132 function getField($fields) 133 { 134 if(!is_array($fields)) $fields = array($fields); 135 $info = false; 136 foreach($fields as $field){ 137 if(strtolower(substr($field,0,5)) == 'iptc.'){ 138 $info = $this->getIPTCField(substr($field,5)); 139 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 140 $info = $this->getExifField(substr($field,5)); 141 }elseif(strtolower(substr($field,0,4)) == 'xmp.'){ 142 $info = $this->getXmpField(substr($field,4)); 143 }elseif(strtolower(substr($field,0,5)) == 'file.'){ 144 $info = $this->getFileField(substr($field,5)); 145 }elseif(strtolower(substr($field,0,5)) == 'date.'){ 146 $info = $this->getDateField(substr($field,5)); 147 }elseif(strtolower($field) == 'simple.camera'){ 148 $info = $this->getCamera(); 149 }elseif(strtolower($field) == 'simple.raw'){ 150 return $this->getRawInfo(); 151 }elseif(strtolower($field) == 'simple.title'){ 152 $info = $this->getTitle(); 153 }elseif(strtolower($field) == 'simple.shutterspeed'){ 154 $info = $this->getShutterSpeed(); 155 }else{ 156 $info = $this->getExifField($field); 157 } 158 if($info != false) break; 159 } 160 161 if($info === false) $info = $alt; 162 if(is_array($info)){ 163 if(isset($info['val'])){ 164 $info = $info['val']; 165 }else{ 166 $info = join(', ',$info); 167 } 168 } 169 return trim($info); 170 } 171 172 /** 173 * Convinience function to set nearly all available Data 174 * through one function 175 * 176 * @author Andreas Gohr <andi@splitbrain.org> 177 */ 178 function setField($field, $value) 179 { 180 if(strtolower(substr($field,0,5)) == 'iptc.'){ 181 return $this->setIPTCField(substr($field,5),$value); 182 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 183 return $this->setExifField(substr($field,5),$value); 184 }else{ 185 return $this->setExifField($field,$value); 186 } 187 } 188 189 /** 190 * Convinience function to delete nearly all available Data 191 * through one function 192 * 193 * @author Andreas Gohr <andi@splitbrain.org> 194 */ 195 function deleteField($field) 196 { 197 if(strtolower(substr($field,0,5)) == 'iptc.'){ 198 return $this->deleteIPTCField(substr($field,5)); 199 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 200 return $this->deleteExifField(substr($field,5)); 201 }else{ 202 return $this->deleteExifField($field); 203 } 204 } 205 206 /** 207 * Return a date field 208 * 209 * @author Andreas Gohr <andi@splitbrain.org> 210 */ 211 function getDateField($field) 212 { 213 if (!isset($this->_info['dates'])) { 214 $this->_info['dates'] = $this->getDates(); 215 } 216 217 if (isset($this->_info['dates'][$field])) { 218 return $this->_info['dates'][$field]; 219 } 220 221 return false; 222 } 223 224 /** 225 * Return a file info field 226 * 227 * @author Andreas Gohr <andi@splitbrain.org> 228 */ 229 function getFileField($field) 230 { 231 if (!isset($this->_info['file'])) { 232 $this->_parseFileInfo(); 233 } 234 235 if (isset($this->_info['file'][$field])) { 236 return $this->_info['file'][$field]; 237 } 238 239 return false; 240 } 241 242 /** 243 * Return the camera info (Maker and Model) 244 * 245 * @author Andreas Gohr <andi@splitbrain.org> 246 * @todo handle makernotes 247 */ 248 function getCamera(){ 249 $make = $this->getField(array('Exif.Make','Exif.TIFFMake')); 250 $model = $this->getField(array('Exif.Model','Exif.TIFFModel')); 251 $cam = trim("$make $model"); 252 if(empty($cam)) return false; 253 return $cam; 254 } 255 256 /** 257 * Return shutter speed as a ratio 258 * 259 * @author Joe Lapp <joe.lapp@pobox.com> 260 */ 261 function getShutterSpeed() 262 { 263 if (!isset($this->_info['exif'])) { 264 $this->_parseMarkerExif(); 265 } 266 if(!isset($this->_info['exif']['ExposureTime'])){ 267 return ''; 268 } 269 270 $field = $this->_info['exif']['ExposureTime']; 271 if($field['den'] == 1) return $field['num']; 272 return $field['num'].'/'.$field['den']; 273 } 274 275 /** 276 * Return an EXIF field 277 * 278 * @author Sebastian Delmont <sdelmont@zonageek.com> 279 */ 280 function getExifField($field) 281 { 282 if (!isset($this->_info['exif'])) { 283 $this->_parseMarkerExif(); 284 } 285 286 if ($this->_markers == null) { 287 return false; 288 } 289 290 if (isset($this->_info['exif'][$field])) { 291 return $this->_info['exif'][$field]; 292 } 293 294 return false; 295 } 296 297 /** 298 * Return an XMP field 299 * 300 * @author Hakan Sandell <hakan.sandell@mydata.se> 301 */ 302 function getXmpField($field) 303 { 304 if (!isset($this->_info['xmp'])) { 305 $this->_parseMarkerXmp(); 306 } 307 308 if ($this->_markers == null) { 309 return false; 310 } 311 312 if (isset($this->_info['xmp'][$field])) { 313 return $this->_info['xmp'][$field]; 314 } 315 316 return false; 317 } 318 319 /** 320 * Return an Adobe Field 321 * 322 * @author Sebastian Delmont <sdelmont@zonageek.com> 323 */ 324 function getAdobeField($field) 325 { 326 if (!isset($this->_info['adobe'])) { 327 $this->_parseMarkerAdobe(); 328 } 329 330 if ($this->_markers == null) { 331 return false; 332 } 333 334 if (isset($this->_info['adobe'][$field])) { 335 return $this->_info['adobe'][$field]; 336 } 337 338 return false; 339 } 340 341 /** 342 * Return an IPTC field 343 * 344 * @author Sebastian Delmont <sdelmont@zonageek.com> 345 */ 346 function getIPTCField($field) 347 { 348 if (!isset($this->_info['iptc'])) { 349 $this->_parseMarkerAdobe(); 350 } 351 352 if ($this->_markers == null) { 353 return false; 354 } 355 356 if (isset($this->_info['iptc'][$field])) { 357 return $this->_info['iptc'][$field]; 358 } 359 360 return false; 361 } 362 363 /** 364 * Set an EXIF field 365 * 366 * @author Sebastian Delmont <sdelmont@zonageek.com> 367 * @author Joe Lapp <joe.lapp@pobox.com> 368 */ 369 function setExifField($field, $value) 370 { 371 if (!isset($this->_info['exif'])) { 372 $this->_parseMarkerExif(); 373 } 374 375 if ($this->_markers == null) { 376 return false; 377 } 378 379 if ($this->_info['exif'] == false) { 380 $this->_info['exif'] = array(); 381 } 382 383 // make sure datetimes are in correct format 384 if(strlen($field) >= 8 && strtolower(substr($field, 0, 8)) == 'datetime') { 385 if(strlen($value) < 8 || $value{4} != ':' || $value{7} != ':') { 386 $value = date('Y:m:d H:i:s', strtotime($value)); 387 } 388 } 389 390 $this->_info['exif'][$field] = $value; 391 392 return true; 393 } 394 395 /** 396 * Set an Adobe Field 397 * 398 * @author Sebastian Delmont <sdelmont@zonageek.com> 399 */ 400 function setAdobeField($field, $value) 401 { 402 if (!isset($this->_info['adobe'])) { 403 $this->_parseMarkerAdobe(); 404 } 405 406 if ($this->_markers == null) { 407 return false; 408 } 409 410 if ($this->_info['adobe'] == false) { 411 $this->_info['adobe'] = array(); 412 } 413 414 $this->_info['adobe'][$field] = $value; 415 416 return true; 417 } 418 419 /** 420 * Calculates the multiplier needed to resize the image to the given 421 * dimensions 422 * 423 * @author Andreas Gohr <andi@splitbrain.org> 424 */ 425 function getResizeRatio($maxwidth,$maxheight=0){ 426 if(!$maxheight) $maxheight = $maxwidth; 427 428 $w = $this->getField('File.Width'); 429 $h = $this->getField('File.Height'); 430 431 $ratio = 1; 432 if($w >= $h){ 433 if($w >= $maxwidth){ 434 $ratio = $maxwidth/$w; 435 }elseif($h > $maxheight){ 436 $ratio = $maxheight/$h; 437 } 438 }else{ 439 if($h >= $maxheight){ 440 $ratio = $maxheight/$h; 441 }elseif($w > $maxwidth){ 442 $ratio = $maxwidth/$w; 443 } 444 } 445 return $ratio; 446 } 447 448 449 /** 450 * Set an IPTC field 451 * 452 * @author Sebastian Delmont <sdelmont@zonageek.com> 453 */ 454 function setIPTCField($field, $value) 455 { 456 if (!isset($this->_info['iptc'])) { 457 $this->_parseMarkerAdobe(); 458 } 459 460 if ($this->_markers == null) { 461 return false; 462 } 463 464 if ($this->_info['iptc'] == false) { 465 $this->_info['iptc'] = array(); 466 } 467 468 $this->_info['iptc'][$field] = $value; 469 470 return true; 471 } 472 473 /** 474 * Delete an EXIF field 475 * 476 * @author Sebastian Delmont <sdelmont@zonageek.com> 477 */ 478 function deleteExifField($field) 479 { 480 if (!isset($this->_info['exif'])) { 481 $this->_parseMarkerAdobe(); 482 } 483 484 if ($this->_markers == null) { 485 return false; 486 } 487 488 if ($this->_info['exif'] != false) { 489 unset($this->_info['exif'][$field]); 490 } 491 492 return true; 493 } 494 495 /** 496 * Delete an Adobe field 497 * 498 * @author Sebastian Delmont <sdelmont@zonageek.com> 499 */ 500 function deleteAdobeField($field) 501 { 502 if (!isset($this->_info['adobe'])) { 503 $this->_parseMarkerAdobe(); 504 } 505 506 if ($this->_markers == null) { 507 return false; 508 } 509 510 if ($this->_info['adobe'] != false) { 511 unset($this->_info['adobe'][$field]); 512 } 513 514 return true; 515 } 516 517 /** 518 * Delete an IPTC field 519 * 520 * @author Sebastian Delmont <sdelmont@zonageek.com> 521 */ 522 function deleteIPTCField($field) 523 { 524 if (!isset($this->_info['iptc'])) { 525 $this->_parseMarkerAdobe(); 526 } 527 528 if ($this->_markers == null) { 529 return false; 530 } 531 532 if ($this->_info['iptc'] != false) { 533 unset($this->_info['iptc'][$field]); 534 } 535 536 return true; 537 } 538 539 /** 540 * Get the image's title, tries various fields 541 * 542 * @param int $max maximum number chars (keeps words) 543 * @author Andreas Gohr <andi@splitbrain.org> 544 */ 545 function getTitle($max=80){ 546 $cap = ''; 547 548 // try various fields 549 $cap = $this->getField(array('Iptc.Headline', 550 'Iptc.Caption', 551 'Xmp.dc:title', 552 'Exif.UserComment', 553 'Exif.TIFFUserComment', 554 'Exif.TIFFImageDescription', 555 'File.Name')); 556 if (empty($cap)) return false; 557 558 if(!$max) return $cap; 559 // Shorten to 80 chars (keeping words) 560 $new = preg_replace('/\n.+$/','',wordwrap($cap, $max)); 561 if($new != $cap) $new .= '...'; 562 563 return $new; 564 } 565 566 /** 567 * Gather various date fields 568 * 569 * @author Sebastian Delmont <sdelmont@zonageek.com> 570 */ 571 function getDates() 572 { 573 $this->_parseAll(); 574 575 if ($this->_markers == null) { 576 return false; 577 } 578 579 $dates = array(); 580 581 $latestTime = 0; 582 $latestTimeSource = ""; 583 $earliestTime = time(); 584 $earliestTimeSource = ""; 585 586 if (@isset($this->_info['exif']['DateTime'])) { 587 $dates['ExifDateTime'] = $this->_info['exif']['DateTime']; 588 589 $aux = $this->_info['exif']['DateTime']; 590 $aux{4} = "-"; 591 $aux{7} = "-"; 592 $t = strtotime($aux); 593 594 if ($t > $latestTime) { 595 $latestTime = $t; 596 $latestTimeSource = "ExifDateTime"; 597 } 598 599 if ($t < $earliestTime) { 600 $earliestTime = $t; 601 $earliestTimeSource = "ExifDateTime"; 602 } 603 } 604 605 if (@isset($this->_info['exif']['DateTimeOriginal'])) { 606 $dates['ExifDateTimeOriginal'] = $this->_info['exif']['DateTime']; 607 608