123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <?php
- /**
- * PHP Exif Native Mapper
- *
- * @link http://github.com/miljar/PHPExif for the canonical source repository
- * @copyright Copyright (c) 2015 Tom Van Herreweghe <tom@theanalogguy.be>
- * @license http://github.com/miljar/PHPExif/blob/master/LICENSE MIT License
- * @category PHPExif
- * @package Mapper
- */
- namespace PHPExif\Mapper;
- use PHPExif\Exif;
- use DateTime;
- use Exception;
- /**
- * PHP Exif Native Mapper
- *
- * Maps native raw data to valid data for the \PHPExif\Exif class
- *
- * @category PHPExif
- * @package Mapper
- */
- class Native implements MapperInterface
- {
- const APERTUREFNUMBER = 'ApertureFNumber';
- const ARTIST = 'Artist';
- const CAPTION = 'caption';
- const COLORSPACE = 'ColorSpace';
- const COPYRIGHT = 'copyright';
- const DATETIMEORIGINAL = 'DateTimeOriginal';
- const CREDIT = 'credit';
- const EXPOSURETIME = 'ExposureTime';
- const FILESIZE = 'FileSize';
- const FOCALLENGTH = 'FocalLength';
- const FOCUSDISTANCE = 'FocusDistance';
- const HEADLINE = 'headline';
- const HEIGHT = 'Height';
- const ISOSPEEDRATINGS = 'ISOSpeedRatings';
- const JOBTITLE = 'jobtitle';
- const KEYWORDS = 'keywords';
- const MIMETYPE = 'MimeType';
- const MODEL = 'Model';
- const ORIENTATION = 'Orientation';
- const SOFTWARE = 'Software';
- const SOURCE = 'source';
- const TITLE = 'title';
- const WIDTH = 'Width';
- const XRESOLUTION = 'XResolution';
- const YRESOLUTION = 'YResolution';
- const GPSLATITUDE = 'GPSLatitude';
- const GPSLONGITUDE = 'GPSLongitude';
- const SECTION_FILE = 'FILE';
- const SECTION_COMPUTED = 'COMPUTED';
- const SECTION_IFD0 = 'IFD0';
- const SECTION_THUMBNAIL = 'THUMBNAIL';
- const SECTION_COMMENT = 'COMMENT';
- const SECTION_EXIF = 'EXIF';
- const SECTION_ALL = 'ANY_TAG';
- const SECTION_IPTC = 'IPTC';
- /**
- * A list of section names
- *
- * @var array
- */
- protected $sections = array(
- self::SECTION_FILE,
- self::SECTION_COMPUTED,
- self::SECTION_IFD0,
- self::SECTION_THUMBNAIL,
- self::SECTION_COMMENT,
- self::SECTION_EXIF,
- self::SECTION_ALL,
- self::SECTION_IPTC,
- );
- /**
- * Maps the ExifTool fields to the fields of
- * the \PHPExif\Exif class
- *
- * @var array
- */
- protected $map = array(
- self::APERTUREFNUMBER => Exif::APERTURE,
- self::FOCUSDISTANCE => Exif::FOCAL_DISTANCE,
- self::HEIGHT => Exif::HEIGHT,
- self::WIDTH => Exif::WIDTH,
- self::CAPTION => Exif::CAPTION,
- self::COPYRIGHT => Exif::COPYRIGHT,
- self::CREDIT => Exif::CREDIT,
- self::HEADLINE => Exif::HEADLINE,
- self::JOBTITLE => Exif::JOB_TITLE,
- self::KEYWORDS => Exif::KEYWORDS,
- self::SOURCE => Exif::SOURCE,
- self::TITLE => Exif::TITLE,
- self::ARTIST => Exif::AUTHOR,
- self::MODEL => Exif::CAMERA,
- self::COLORSPACE => Exif::COLORSPACE,
- self::DATETIMEORIGINAL => Exif::CREATION_DATE,
- self::EXPOSURETIME => Exif::EXPOSURE,
- self::FILESIZE => Exif::FILESIZE,
- self::FOCALLENGTH => Exif::FOCAL_LENGTH,
- self::ISOSPEEDRATINGS => Exif::ISO,
- self::MIMETYPE => Exif::MIMETYPE,
- self::ORIENTATION => Exif::ORIENTATION,
- self::SOFTWARE => Exif::SOFTWARE,
- self::XRESOLUTION => Exif::HORIZONTAL_RESOLUTION,
- self::YRESOLUTION => Exif::VERTICAL_RESOLUTION,
- self::GPSLATITUDE => Exif::GPS,
- self::GPSLONGITUDE => Exif::GPS,
- );
- /**
- * Maps the array of raw source data to the correct
- * fields for the \PHPExif\Exif class
- *
- * @param array $data
- * @return array
- */
- public function mapRawData(array $data)
- {
- $mappedData = array();
- $gpsData = array();
- foreach ($data as $field => $value) {
- if ($this->isSection($field) && is_array($value)) {
- $subData = $this->mapRawData($value);
- $mappedData = array_merge($mappedData, $subData);
- continue;
- }
- if (!$this->isFieldKnown($field)) {
- // silently ignore unknown fields
- continue;
- }
- $key = $this->map[$field];
- // manipulate the value if necessary
- switch ($field) {
- case self::DATETIMEORIGINAL:
- try {
- $value = new DateTime($value);
- } catch (Exception $exception) {
- continue 2;
- }
- break;
- case self::EXPOSURETIME:
- if (!is_float($value)) {
- $value = $this->normalizeComponent($value);
- }
- // Based on the source code of Exiftool (PrintExposureTime subroutine):
- // http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-9.90/lib/Image/ExifTool/Exif.pm
- if ($value < 0.25001 && $value > 0) {
- $value = sprintf('1/%d', intval(0.5 + 1 / $value));
- } else {
- $value = sprintf('%.1f', $value);
- $value = preg_replace('/.0$/', '', $value);
- }
- break;
- case self::FOCALLENGTH:
- $parts = explode('/', $value);
- // Avoid division by zero if focal length is invalid
- if (end($parts) == '0') {
- $value = 0;
- } else {
- $value = (int) reset($parts) / (int) end($parts);
- }
- break;
- case self::XRESOLUTION:
- case self::YRESOLUTION:
- $resolutionParts = explode('/', $value);
- $value = (int) reset($resolutionParts);
- break;
- case self::GPSLATITUDE:
- $gpsData['lat'] = $this->extractGPSCoordinate($value);
- break;
- case self::GPSLONGITUDE:
- $gpsData['lon'] = $this->extractGPSCoordinate($value);
- break;
- }
- // set end result
- $mappedData[$key] = $value;
- }
- // add GPS coordinates, if available
- if (count($gpsData) === 2) {
- $latitudeRef = empty($data['GPSLatitudeRef'][0]) ? 'N' : $data['GPSLatitudeRef'][0];
- $longitudeRef = empty($data['GPSLongitudeRef'][0]) ? 'E' : $data['GPSLongitudeRef'][0];
- $gpsLocation = sprintf(
- '%s,%s',
- (strtoupper($latitudeRef) === 'S' ? -1 : 1) * $gpsData['lat'],
- (strtoupper($longitudeRef) === 'W' ? -1 : 1) * $gpsData['lon']
- );
- $mappedData[Exif::GPS] = $gpsLocation;
- } else {
- unset($mappedData[Exif::GPS]);
- }
- return $mappedData;
- }
- /**
- * Determines if given field is a section
- *
- * @param string $field
- * @return bool
- */
- protected function isSection($field)
- {
- return (in_array($field, $this->sections));
- }
- /**
- * Determines if the given field is known,
- * in a case insensitive way for its first letter.
- * Also update $field to keep it valid against the known fields.
- *
- * @param string &$field
- * @return bool
- */
- protected function isFieldKnown(&$field)
- {
- $lcfField = lcfirst($field);
- if (array_key_exists($lcfField, $this->map)) {
- $field = $lcfField;
- return true;
- }
- $ucfField = ucfirst($field);
- if (array_key_exists($ucfField, $this->map)) {
- $field = $ucfField;
- return true;
- }
- return false;
- }
- /**
- * Extract GPS coordinates from components array
- *
- * @param array|string $components
- * @return float
- */
- protected function extractGPSCoordinate($components)
- {
- if (!is_array($components)) {
- $components = array($components);
- }
- $components = array_map(array($this, 'normalizeComponent'), $components);
- if (count($components) > 2) {
- return floatval($components[0]) + (floatval($components[1]) / 60) + (floatval($components[2]) / 3600);
- }
- return reset($components);
- }
- /**
- * Normalize component
- *
- * @param mixed $component
- * @return int|float
- */
- protected function normalizeComponent($component)
- {
- $parts = explode('/', $component);
- if (count($parts) > 1) {
- if ($parts[1]) {
- return intval($parts[0]) / intval($parts[1]);
- }
- return 0;
- }
- return floatval(reset($parts));
- }
- }
|