618 lines
26 KiB
PHP
618 lines
26 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2003-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file LICENSE for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @author Michael J. Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*/
|
|
|
|
/**
|
|
* General class for fetching and parsing EXIF information from images.
|
|
*
|
|
* Works equally well with either the built in php exif functions (if PHP
|
|
* compiled with exif support), the Exiftool package (more complete but
|
|
* slower), or the bundled exif library.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @author Michael J. Rubinsky <mrubinsk@horde.org>
|
|
* @category Horde
|
|
* @copyright 2003-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
|
|
* @package Image
|
|
*/
|
|
class Horde_Image_Exif
|
|
{
|
|
protected static $_titleFields = array(
|
|
'IPTC' => array('ObjectName'),
|
|
'XMP' => array('Title'),
|
|
'EXIF' => array(),
|
|
'COMPOSITE' => array()
|
|
);
|
|
|
|
protected static $_descriptionFields = array(
|
|
'IPTC' => array('Caption-Abstract'),
|
|
'XMP' => array('Description'),
|
|
'EXIF' => array('ImageDescription'),
|
|
'COMPOSITE' => array()
|
|
);
|
|
|
|
/**
|
|
* Factory method for instantiating a Horde_Image_Exif object.
|
|
*
|
|
* @param string $driver
|
|
* @param array $params
|
|
*
|
|
* @return Horde_Image_Exif
|
|
*/
|
|
public static function factory($driver = null, $params = array())
|
|
{
|
|
if (empty($driver) && function_exists('exif_read_data')) {
|
|
$driver = 'Php';
|
|
} elseif (empty($driver)) {
|
|
$driver = 'Bundled';
|
|
} else {
|
|
$driver = basename($driver);
|
|
}
|
|
|
|
$class = 'Horde_Image_Exif_' . $driver;
|
|
|
|
return new $class($params);
|
|
}
|
|
|
|
/**
|
|
* Converts from Intel to Motorola endien.
|
|
*
|
|
* Just reverses the bytes (assumes hex is passed in)
|
|
*
|
|
* @param string $intel
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function intel2Moto($intel)
|
|
{
|
|
$len = strlen($intel);
|
|
$moto = '';
|
|
for ($i = 0; $i <= $len; $i += 2) {
|
|
$moto .= substr($intel, $len - $i, 2);
|
|
}
|
|
return $moto;
|
|
}
|
|
|
|
/**
|
|
* Obtains an array of supported meta data fields.
|
|
*
|
|
* @TODO: This should probably be extended by the subclass?
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getCategories()
|
|
{
|
|
return array(
|
|
'IPTC' => array(
|
|
'Keywords' => array('description' => Horde_Image_Translation::t("Image keywords"), 'type' => 'array'),
|
|
'ObjectName' => array('description' => Horde_Image_Translation::t("Image Title"), 'type' => 'text'),
|
|
'By-line' => array('description' => Horde_Image_Translation::t("By"), 'type' => 'text'),
|
|
'CopyrightNotice' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
|
|
'Caption-Abstract' => array('description' => Horde_Image_Translation::t("Caption"), 'type' => 'text'),
|
|
),
|
|
|
|
'XMP' => array(
|
|
'Creator' => array('description' => Horde_Image_Translation::t("Image Creator"), 'type' => 'text'),
|
|
'Rights' => array('description' => Horde_Image_Translation::t("Rights"), 'type' => 'text'),
|
|
'UsageTerms' => array('description' => Horde_Image_Translation::t("Usage Terms"), 'type' => 'text'),
|
|
'Title' => array('description' => Horde_Image_Translation::t("Title"), 'type' => 'text'),
|
|
'Description' => array('description' => Horde_Image_Translation::t("Description"), 'type' => 'text'),
|
|
),
|
|
|
|
'EXIF' => array(
|
|
'DateTime' => array('description' => Horde_Image_Translation::t("Date Photo Modified"), 'type' => 'date'),
|
|
'DateTimeOriginal' => array('description' => Horde_Image_Translation::t("Date Photo Taken"), 'type' => 'date'),
|
|
'DateTimeDigitized' => array('description' => Horde_Image_Translation::t("Date Photo Digitized"), 'type' => 'date'),
|
|
'GPSLatitude' => array('description' => Horde_Image_Translation::t("Latitude"), 'type' => 'gps'),
|
|
'GPSLongitude' => array('description' => Horde_Image_Translation::t("Longitude"), 'type' => 'gps'),
|
|
'Make' => array('description' => Horde_Image_Translation::t("Camera Make"), 'type' => 'text'),
|
|
'Model' => array('description' => Horde_Image_Translation::t("Camera Model"), 'type' => 'text'),
|
|
'Software' => array('description' => Horde_Image_Translation::t("Software Version"), 'type' => 'text'),
|
|
'ImageType' => array('description' => Horde_Image_Translation::t("Photo Type"), 'type' => 'text'),
|
|
'ImageDescription' => array('description' => Horde_Image_Translation::t("Photo Description"), 'type' => 'text'),
|
|
'FileSize' => array('description' => Horde_Image_Translation::t("File Size"), 'type' => 'number'),
|
|
'ExifImageWidth' => array('description' => Horde_Image_Translation::t("Width"), 'type' => 'number'),
|
|
'ExifImageLength' => array('description' => Horde_Image_Translation::t("Height"), 'type' => 'number'),
|
|
'XResolution' => array('description' => Horde_Image_Translation::t("X Resolution"), 'type' => 'number'),
|
|
'YResolution' => array('description' => Horde_Image_Translation::t("Y Resolution"), 'type' => 'number'),
|
|
'ResolutionUnit' => array('description' => Horde_Image_Translation::t("Resolution Unit"), 'type' => 'text'),
|
|
'ShutterSpeedValue' => array('description' => Horde_Image_Translation::t("Shutter Speed"), 'type' => 'number'),
|
|
'ExposureTime' => array('description' => Horde_Image_Translation::t("Exposure"), 'type' => 'number'),
|
|
'FocalLength' => array('description' => Horde_Image_Translation::t("Focal Length"), 'type' => 'number'),
|
|
'FocalLengthIn35mmFilm' => array('description' => Horde_Image_Translation::t("Focal Length (35mm equiv)"), 'type' => 'number'),
|
|
'ApertureValue' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'number'),
|
|
'FNumber' => array('description' => Horde_Image_Translation::t("F-Number"), 'type' => 'number'),
|
|
'ISOSpeedRatings' => array('description' => Horde_Image_Translation::t("ISO Setting"), 'type' => 'number'),
|
|
'ExposureBiasValue' => array('description' => Horde_Image_Translation::t("Exposure Bias"), 'type' => 'number'),
|
|
'ExposureMode' => array('description' => Horde_Image_Translation::t("Exposure Mode"), 'type' => 'number'),
|
|
'ExposureProgram' => array('description' => Horde_Image_Translation::t("Exposure Program"), 'type' => 'number'),
|
|
'MeteringMode' => array('description' => Horde_Image_Translation::t("Metering Mode"), 'type' => 'number'),
|
|
'Flash' => array('description' => Horde_Image_Translation::t("Flash Setting"), 'type' => 'number'),
|
|
'UserComment' => array('description' => Horde_Image_Translation::t("User Comment"), 'type' => 'text'),
|
|
'ColorSpace' => array('description' => Horde_Image_Translation::t("Color Space"), 'type' => 'number'),
|
|
'SensingMethod' => array('description' => Horde_Image_Translation::t("Sensing Method"), 'type' => 'number'),
|
|
'WhiteBalance' => array('description' => Horde_Image_Translation::t("White Balance"), 'type' => 'number'),
|
|
'Orientation' => array('description' => Horde_Image_Translation::t("Camera Orientation"), 'type' => 'number'),
|
|
'Copyright' => array('description' => Horde_Image_Translation::t("Copyright"), 'type' => 'text'),
|
|
'Artist' => array('description' => Horde_Image_Translation::t("Artist"), 'type' => 'text'),
|
|
'LightSource' => array('description' => Horde_Image_Translation::t("Light source"), 'type' => 'number'),
|
|
'ImageStabalization' => array('description' => Horde_Image_Translation::t("Image Stabilization"), 'type' => 'text'),
|
|
'SceneCaptureType' => array('description' => Horde_Image_Translation::t("Scene Type"), 'type' => 'number'),
|
|
),
|
|
|
|
'COMPOSITE' => array(
|
|
'LensID' => array('description' => Horde_Image_Translation::t("Lens Id"), 'type' => 'text'),
|
|
'Lens' => array('description' => 'Lens', 'type' => 'text'),
|
|
'Aperture' => array('description' => Horde_Image_Translation::t("Aperture"), 'type' => 'text'),
|
|
'DOF' => array('description' => Horde_Image_Translation::t("Depth of Field"), 'type' => 'text'),
|
|
'FOV' => array('description' => Horde_Image_Translation::t("Field of View"), 'type' => 'text')
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of metadata fields that can by used for image titles.
|
|
*
|
|
* @param mixed $driver A Horde_Image_Exif_Base instance or a string
|
|
* specifying the driver in use.
|
|
*
|
|
* @return array An array of metadata field name hashes.
|
|
* @since 2.1.0
|
|
*/
|
|
public static function getTitleFields($driver = null)
|
|
{
|
|
if (!is_null($driver) && is_array($driver)) {
|
|
$driver = self::factory($driver[0], $driver[1]);
|
|
}
|
|
if ($driver instanceof Horde_Image_Exif_Base) {
|
|
$supported = $driver->supportedCategories();
|
|
} else {
|
|
$supported = array('XMP', 'IPTC', 'EXIF');
|
|
}
|
|
|
|
$fields = array();
|
|
foreach ($supported as $category) {
|
|
$fields = array_merge($fields, self::$_titleFields[$category]);
|
|
}
|
|
$return = array();
|
|
$all = self::getFields($driver, true);
|
|
foreach ($fields as $field) {
|
|
$return[$field] = $all[$field];
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of metadata fields that can by used for image
|
|
* descriptions.
|
|
*
|
|
* @param mixed $driver A Horde_Image_Exif_Base instance or a string
|
|
* specifying the driver in use.
|
|
*
|
|
* @return array An array of metadata field hashes.
|
|
* @since 2.1.0
|
|
*/
|
|
public static function getDescriptionFields($driver = null)
|
|
{
|
|
$map = self::getCategories();
|
|
if (!is_null($driver) && is_array($driver)) {
|
|
$driver = self::factory($driver[0], $driver[1]);
|
|
}
|
|
if ($driver instanceof Horde_Image_Exif_Base) {
|
|
$supported = $driver->supportedCategories();
|
|
} else {
|
|
$supported = array('XMP', 'IPTC', 'EXIF');
|
|
}
|
|
|
|
$fields = array();
|
|
foreach ($supported as $category) {
|
|
$fields = array_merge($fields, self::$_descriptionFields[$category]);
|
|
}
|
|
$return = array();
|
|
$all = self::getFields($driver, true);
|
|
foreach ($fields as $field) {
|
|
$return[$field] = $all[$field];
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
/**
|
|
* Returns a flattened array of supported metadata fields.
|
|
*
|
|
* @param mixed $driver A Horde_Image_Exif_Base instance or a
|
|
* string specifying the driver in use.
|
|
* @param boolean $description_only Only return the field descriptions.
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function getFields($driver = null, $description_only = false)
|
|
{
|
|
if (!is_null($driver) && is_array($driver)) {
|
|
$driver = self::factory($driver[0], $driver[1]);
|
|
}
|
|
|
|
if ($driver instanceof Horde_Image_Exif_Base) {
|
|
$supported = $driver->supportedCategories();
|
|
} else {
|
|
$supported = array('XMP', 'IPTC', 'EXIF' );
|
|
}
|
|
$categories = self::getCategories();
|
|
$flattened = array();
|
|
foreach ($supported as $category) {
|
|
$flattened = array_merge($flattened, $categories[$category]);
|
|
}
|
|
|
|
if ($description_only) {
|
|
foreach ($flattened as $key => $data) {
|
|
$return[$key] = $data['description'];
|
|
}
|
|
return $return;
|
|
}
|
|
|
|
return $flattened;
|
|
}
|
|
|
|
/**
|
|
* More human friendly exposure formatting.
|
|
*/
|
|
protected static function _formatExposure($data)
|
|
{
|
|
if ($data > 0) {
|
|
if ($data > 1) {
|
|
return sprintf(
|
|
Horde_Image_Translation::t("%d sec"),
|
|
round($data, 2)
|
|
);
|
|
} else {
|
|
$n = $d = 0;
|
|
self::_convertToFraction($data, $n, $d);
|
|
if ($n <> 1) {
|
|
return sprintf(
|
|
Horde_Image_Translation::t("%4f sec"), $n / $d
|
|
);
|
|
}
|
|
return sprintf(
|
|
Horde_Image_Translation::t("%s / %s sec"), $n, $d
|
|
);
|
|
}
|
|
} else {
|
|
return Horde_Image_Translation::t("Bulb");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a floating point number into a fraction.
|
|
*
|
|
* Many thanks to Matthieu Froment for this code.
|
|
*
|
|
* (Ported from the Exifer library).
|
|
*/
|
|
protected static function _convertToFraction($v, &$n, &$d)
|
|
{
|
|
$MaxTerms = 15; // Limit to prevent infinite loop
|
|
$MinDivisor = 0.000001; // Limit to prevent divide by zero
|
|
$MaxError = 0.00000001; // How close is enough
|
|
|
|
// Initialize fraction being converted
|
|
$f = $v;
|
|
|
|
// Initialize fractions with 1/0, 0/1
|
|
$n_un = 1;
|
|
$d_un = 0;
|
|
$n_deux = 0;
|
|
$d_deux = 1;
|
|
|
|
for ($i = 0; $i < $MaxTerms; $i++) {
|
|
$a = floor($f); // Get next term
|
|
$f = $f - $a; // Get new divisor
|
|
$n = $n_un * $a + $n_deux; // Calculate new fraction
|
|
$d = $d_un * $a + $d_deux;
|
|
$n_deux = $n_un; // Save last two fractions
|
|
$d_deux = $d_un;
|
|
$n_un = $n;
|
|
$d_un = $d;
|
|
|
|
// Quit if dividing by zero
|
|
if ($f < $MinDivisor) {
|
|
break;
|
|
}
|
|
if (abs($v - $n / $d) < $MaxError) {
|
|
break;
|
|
}
|
|
|
|
// reciprocal
|
|
$f = 1 / $f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts an exif field into human-readable form.
|
|
*
|
|
* Some of these cases are ported from the Exifer library, others were
|
|
* changed from their implementation where the EXIF standard dictated
|
|
* different behaviour.
|
|
*
|
|
* @param string $field The name of the field to translate.
|
|
* @param string $data The data value to translate.
|
|
*
|
|
* @return string The converted data.
|
|
*/
|
|
public static function getHumanReadable($field, $data)
|
|
{
|
|
switch ($field) {
|
|
case 'ExposureMode':
|
|
switch ($data) {
|
|
case 0: return Horde_Image_Translation::t("Auto exposure");
|
|
case 1: return Horde_Image_Translation::t("Manual exposure");
|
|
case 2: return Horde_Image_Translation::t("Auto bracket");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'ExposureProgram':
|
|
switch ($data) {
|
|
case 1: return Horde_Image_Translation::t("Manual");
|
|
case 2: return Horde_Image_Translation::t("Normal Program");
|
|
case 3: return Horde_Image_Translation::t("Aperture Priority");
|
|
case 4: return Horde_Image_Translation::t("Shutter Priority");
|
|
case 5: return Horde_Image_Translation::t("Creative");
|
|
case 6: return Horde_Image_Translation::t("Action");
|
|
case 7: return Horde_Image_Translation::t("Portrait");
|
|
case 8: return Horde_Image_Translation::t("Landscape");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'XResolution':
|
|
case 'YResolution':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
return sprintf(Horde_Image_Translation::t("%d dots per unit"), $n);
|
|
}
|
|
return sprintf(Horde_Image_Translation::t("%d per unit"), $data);
|
|
|
|
case 'ResolutionUnit':
|
|
switch ($data) {
|
|
case 1: return Horde_Image_Translation::t("Pixels");
|
|
case 2: return Horde_Image_Translation::t("Inch");
|
|
case 3: return Horde_Image_Translation::t("Centimeter");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'ExifImageWidth':
|
|
case 'ExifImageLength':
|
|
return sprintf(Horde_Image_Translation::t("%d pixels"), $data);
|
|
|
|
case 'Orientation':
|
|
switch ($data) {
|
|
case 1:
|
|
return sprintf(Horde_Image_Translation::t("Normal (O deg)"));
|
|
case 2:
|
|
return sprintf(Horde_Image_Translation::t("Mirrored"));
|
|
case 3:
|
|
return sprintf(Horde_Image_Translation::t("Upsidedown"));
|
|
case 4:
|
|
return sprintf(Horde_Image_Translation::t("Upsidedown Mirrored"));
|
|
case 5:
|
|
return sprintf(Horde_Image_Translation::t("90 deg CW Mirrored"));
|
|
case 6:
|
|
return sprintf(Horde_Image_Translation::t("90 deg CCW"));
|
|
case 7:
|
|
return sprintf(Horde_Image_Translation::t("90 deg CCW Mirrored"));
|
|
case 8:
|
|
return sprintf(Horde_Image_Translation::t("90 deg CW"));
|
|
}
|
|
break;
|
|
|
|
case 'ExposureTime':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($d == 0) {
|
|
return;
|
|
}
|
|
$data = $n / $d;
|
|
}
|
|
return self::_formatExposure($data);
|
|
|
|
case 'ShutterSpeedValue':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($d == 0) {
|
|
return;
|
|
}
|
|
$data = $n / $d;
|
|
}
|
|
$data = exp($data * log(2));
|
|
if ($data > 0) {
|
|
$data = 1 / $data;
|
|
}
|
|
return self::_formatExposure($data);
|
|
|
|
case 'ApertureValue':
|
|
case 'MaxApertureValue':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($d == 0) {
|
|
return;
|
|
}
|
|
$data = $n / $d;
|
|
$data = exp(($data * log(2)) / 2);
|
|
|
|
// Precision is 1 digit.
|
|
$data = round($data, 1);
|
|
}
|
|
return 'f/' . $data;
|
|
|
|
case 'FocalLength':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($d == 0) {
|
|
return;
|
|
}
|
|
return sprintf(Horde_Image_Translation::t("%d mm"), round($n / $d));
|
|
}
|
|
return sprintf(Horde_Image_Translation::t("%d mm"), $data);
|
|
|
|
case 'FNumber':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($d != 0) {
|
|
return 'f/' . round($n / $d, 1);
|
|
}
|
|
}
|
|
return 'f/' . $data;
|
|
|
|
case 'ExposureBiasValue':
|
|
if (strpos($data, '/') !== false) {
|
|
list($n, $d) = explode('/', $data, 2);
|
|
if ($n == 0) {
|
|
return '0 EV';
|
|
}
|
|
}
|
|
return $data . ' EV';
|
|
|
|
case 'MeteringMode':
|
|
switch ($data) {
|
|
case 0: return Horde_Image_Translation::t("Unknown");
|
|
case 1: return Horde_Image_Translation::t("Average");
|
|
case 2: return Horde_Image_Translation::t("Center Weighted Average");
|
|
case 3: return Horde_Image_Translation::t("Spot");
|
|
case 4: return Horde_Image_Translation::t("Multi-Spot");
|
|
case 5: return Horde_Image_Translation::t("Multi-Segment");
|
|
case 6: return Horde_Image_Translation::t("Partial");
|
|
case 255: return Horde_Image_Translation::t("Other");
|
|
default: return sprintf(Horde_Image_Translation::t("Unknown: %s"), $data);
|
|
}
|
|
break;
|
|
|
|
case 'LightSource':
|
|
switch ($data) {
|
|
case 1: return Horde_Image_Translation::t("Daylight");
|
|
case 2: return Horde_Image_Translation::t("Fluorescent");
|
|
case 3: return Horde_Image_Translation::t("Tungsten");
|
|
case 4: return Horde_Image_Translation::t("Flash");
|
|
case 9: return Horde_Image_Translation::t("Fine weather");
|
|
case 10: return Horde_Image_Translation::t("Cloudy weather");
|
|
case 11: return Horde_Image_Translation::t("Shade");
|
|
case 12: return Horde_Image_Translation::t("Daylight fluorescent");
|
|
case 13: return Horde_Image_Translation::t("Day white fluorescent");
|
|
case 14: return Horde_Image_Translation::t("Cool white fluorescent");
|
|
case 15: return Horde_Image_Translation::t("White fluorescent");
|
|
case 17: return Horde_Image_Translation::t("Standard light A");
|
|
case 18: return Horde_Image_Translation::t("Standard light B");
|
|
case 19: return Horde_Image_Translation::t("Standard light C");
|
|
case 20: return 'D55';
|
|
case 21: return 'D65';
|
|
case 22: return 'D75';
|
|
case 23: return 'D50';
|
|
case 24: return Horde_Image_Translation::t("ISO studio tungsten");
|
|
case 255: return Horde_Image_Translation::t("other light source");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'WhiteBalance':
|
|
switch ($data) {
|
|
case 0: return Horde_Image_Translation::t("Auto");
|
|
case 1: return Horde_Image_Translation::t("Manual");
|
|
default: Horde_Image_Translation::t("Unknown");
|
|
}
|
|
break;
|
|
|
|
case 'FocalLengthIn35mmFilm':
|
|
return $data . ' mm';
|
|
|
|
case 'Flash':
|
|
switch ($data) {
|
|
case 0: return Horde_Image_Translation::t("No Flash");
|
|
case 1: return Horde_Image_Translation::t("Flash");
|
|
case 5: return Horde_Image_Translation::t("Flash, strobe return light not detected");
|
|
case 7: return Horde_Image_Translation::t("Flash, strobe return light detected");
|
|
case 9: return Horde_Image_Translation::t("Compulsory Flash");
|
|
case 13: return Horde_Image_Translation::t("Compulsory Flash, Return light not detected");
|
|
case 15: return Horde_Image_Translation::t("Compulsory Flash, Return light detected");
|
|
case 16: return Horde_Image_Translation::t("No Flash");
|
|
case 24: return Horde_Image_Translation::t("No Flash");
|
|
case 25: return Horde_Image_Translation::t("Flash, Auto-Mode");
|
|
case 29: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light not detected");
|
|
case 31: return Horde_Image_Translation::t("Flash, Auto-Mode, Return light detected");
|
|
case 32: return Horde_Image_Translation::t("No Flash");
|
|
case 65: return Horde_Image_Translation::t("Red Eye");
|
|
case 69: return Horde_Image_Translation::t("Red Eye, Return light not detected");
|
|
case 71: return Horde_Image_Translation::t("Red Eye, Return light detected");
|
|
case 73: return Horde_Image_Translation::t("Red Eye, Compulsory Flash");
|
|
case 77: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light not detected");
|
|
case 79: return Horde_Image_Translation::t("Red Eye, Compulsory Flash, Return light detected");
|
|
case 89: return Horde_Image_Translation::t("Red Eye, Auto-Mode");
|
|
case 93: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light not detected");
|
|
case 95: return Horde_Image_Translation::t("Red Eye, Auto-Mode, Return light detected");
|
|
}
|
|
break;
|
|
|
|
case 'FileSize':
|
|
if ($data <= 0) {
|
|
return '0 Bytes';
|
|
}
|
|
$s = array('B', 'kB', 'MB', 'GB');
|
|
$e = floor(log($data, 1024));
|
|
return round($data/pow(1024, $e), 2) . ' ' . $s[$e];
|
|
|
|
case 'SensingMethod':
|
|
switch ($data) {
|
|
case 1: return Horde_Image_Translation::t("Not defined");
|
|
case 2: return Horde_Image_Translation::t("One Chip Color Area Sensor");
|
|
case 3: return Horde_Image_Translation::t("Two Chip Color Area Sensor");
|
|
case 4: return Horde_Image_Translation::t("Three Chip Color Area Sensor");
|
|
case 5: return Horde_Image_Translation::t("Color Sequential Area Sensor");
|
|
case 7: return Horde_Image_Translation::t("Trilinear Sensor");
|
|
case 8: return Horde_Image_Translation::t("Color Sequential Linear Sensor");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'ColorSpace':
|
|
switch ($data) {
|
|
case 1: return Horde_Image_Translation::t("sRGB");
|
|
default: return Horde_Image_Translation::t("Uncalibrated");
|
|
}
|
|
|
|
case 'SceneCaptureType':
|
|
switch ($data) {
|
|
case 0: return Horde_Image_Translation::t("Standard");
|
|
case 1: return Horde_Image_Translation::t("Landscape");
|
|
case 2: return Horde_Image_Translation::t("Portrait");
|
|
case 3: return Horde_Image_Translation::t("Night Scene");
|
|
default: return Horde_Image_Translation::t("Unknown");
|
|
}
|
|
|
|
case 'DateTime':
|
|
case 'DateTimeOriginal':
|
|
case 'DateTimeDigitized':
|
|
return date('m/d/Y H:i:s O', $data);
|
|
|
|
case 'UserComment':
|
|
//@TODO: the first 8 bytes of this field contain the charset used
|
|
// to encode the comment. Either ASCII, JIS, UNICODE, or
|
|
// UNDEFINED. Should probably either convert to a known charset
|
|
// here and let the calling code deal with it, or allow this
|
|
// method to take an optional charset to convert to (would
|
|
// introduce a dependency on Horde_String to do the conversion).
|
|
$data = trim(substr($data, 7)) ;
|
|
|
|
|
|
default:
|
|
return !empty($data) ? $data : '---';
|
|
}
|
|
}
|
|
|
|
}
|