File indexing completed on 2024-12-22 05:33:13
0001 <?php 0002 ///////////////////////////////////////////////////////////////// 0003 /// getID3() by James Heinrich <info@getid3.org> // 0004 // available at http://getid3.sourceforge.net // 0005 // or http://www.getid3.org // 0006 // also https://github.com/JamesHeinrich/getID3 // 0007 ///////////////////////////////////////////////////////////////// 0008 // See readme.txt for more details // 0009 ///////////////////////////////////////////////////////////////// 0010 // // 0011 // module.graphic.jpg.php // 0012 // module for analyzing JPEG Image files // 0013 // dependencies: PHP compiled with --enable-exif (optional) // 0014 // module.tag.xmp.php (optional) // 0015 // /// 0016 ///////////////////////////////////////////////////////////////// 0017 0018 0019 class getid3_jpg extends getid3_handler 0020 { 0021 0022 0023 public function Analyze() { 0024 $info = &$this->getid3->info; 0025 0026 $info['fileformat'] = 'jpg'; 0027 $info['video']['dataformat'] = 'jpg'; 0028 $info['video']['lossless'] = false; 0029 $info['video']['bits_per_sample'] = 24; 0030 $info['video']['pixel_aspect_ratio'] = (float) 1; 0031 0032 $this->fseek($info['avdataoffset']); 0033 0034 $imageinfo = array(); 0035 //list($width, $height, $type) = getid3_lib::GetDataImageSize($this->fread($info['filesize']), $imageinfo); 0036 list($width, $height, $type) = getimagesize($info['filenamepath'], $imageinfo); // http://www.getid3.org/phpBB3/viewtopic.php?t=1474 0037 0038 0039 if (isset($imageinfo['APP13'])) { 0040 // http://php.net/iptcparse 0041 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html 0042 $iptc_parsed = iptcparse($imageinfo['APP13']); 0043 if (is_array($iptc_parsed)) { 0044 foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) { 0045 list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw); 0046 $iptc_tagkey = intval(ltrim($iptc_tagkey, '0')); 0047 foreach ($iptc_values as $key => $value) { 0048 $IPTCrecordName = $this->IPTCrecordName($iptc_record); 0049 $IPTCrecordTagName = $this->IPTCrecordTagName($iptc_record, $iptc_tagkey); 0050 if (isset($info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName])) { 0051 $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName][] = $value; 0052 } else { 0053 $info['iptc']['comments'][$IPTCrecordName][$IPTCrecordTagName] = array($value); 0054 } 0055 } 0056 } 0057 } 0058 } 0059 0060 $returnOK = false; 0061 switch ($type) { 0062 case IMG_JPG: 0063 $info['video']['resolution_x'] = $width; 0064 $info['video']['resolution_y'] = $height; 0065 0066 if (isset($imageinfo['APP1'])) { 0067 if (function_exists('exif_read_data')) { 0068 if (substr($imageinfo['APP1'], 0, 4) == 'Exif') { 0069 //$info['warning'][] = 'known issue: https://bugs.php.net/bug.php?id=62523'; 0070 //return false; 0071 $info['jpg']['exif'] = exif_read_data($info['filenamepath'], null, true, false); 0072 } else { 0073 $info['warning'][] = 'exif_read_data() cannot parse non-EXIF data in APP1 (expected "Exif", found "'.substr($imageinfo['APP1'], 0, 4).'")'; 0074 } 0075 } else { 0076 $info['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif'); 0077 } 0078 } 0079 $returnOK = true; 0080 break; 0081 0082 default: 0083 break; 0084 } 0085 0086 0087 $cast_as_appropriate_keys = array('EXIF', 'IFD0', 'THUMBNAIL'); 0088 foreach ($cast_as_appropriate_keys as $exif_key) { 0089 if (isset($info['jpg']['exif'][$exif_key])) { 0090 foreach ($info['jpg']['exif'][$exif_key] as $key => $value) { 0091 $info['jpg']['exif'][$exif_key][$key] = $this->CastAsAppropriate($value); 0092 } 0093 } 0094 } 0095 0096 0097 if (isset($info['jpg']['exif']['GPS'])) { 0098 0099 if (isset($info['jpg']['exif']['GPS']['GPSVersion'])) { 0100 for ($i = 0; $i < 4; $i++) { 0101 $version_subparts[$i] = ord(substr($info['jpg']['exif']['GPS']['GPSVersion'], $i, 1)); 0102 } 0103 $info['jpg']['exif']['GPS']['computed']['version'] = 'v'.implode('.', $version_subparts); 0104 } 0105 0106 if (isset($info['jpg']['exif']['GPS']['GPSDateStamp'])) { 0107 $explodedGPSDateStamp = explode(':', $info['jpg']['exif']['GPS']['GPSDateStamp']); 0108 $computed_time[5] = (isset($explodedGPSDateStamp[0]) ? $explodedGPSDateStamp[0] : ''); 0109 $computed_time[3] = (isset($explodedGPSDateStamp[1]) ? $explodedGPSDateStamp[1] : ''); 0110 $computed_time[4] = (isset($explodedGPSDateStamp[2]) ? $explodedGPSDateStamp[2] : ''); 0111 0112 $computed_time = array(0=>0, 1=>0, 2=>0, 3=>0, 4=>0, 5=>0); 0113 if (isset($info['jpg']['exif']['GPS']['GPSTimeStamp']) && is_array($info['jpg']['exif']['GPS']['GPSTimeStamp'])) { 0114 foreach ($info['jpg']['exif']['GPS']['GPSTimeStamp'] as $key => $value) { 0115 $computed_time[$key] = getid3_lib::DecimalizeFraction($value); 0116 } 0117 } 0118 $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]); 0119 } 0120 0121 if (isset($info['jpg']['exif']['GPS']['GPSLatitude']) && is_array($info['jpg']['exif']['GPS']['GPSLatitude'])) { 0122 $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLatitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLatitudeRef'] == 'S')) ? -1 : 1); 0123 foreach ($info['jpg']['exif']['GPS']['GPSLatitude'] as $key => $value) { 0124 $computed_latitude[$key] = getid3_lib::DecimalizeFraction($value); 0125 } 0126 $info['jpg']['exif']['GPS']['computed']['latitude'] = $direction_multiplier * ($computed_latitude[0] + ($computed_latitude[1] / 60) + ($computed_latitude[2] / 3600)); 0127 } 0128 0129 if (isset($info['jpg']['exif']['GPS']['GPSLongitude']) && is_array($info['jpg']['exif']['GPS']['GPSLongitude'])) { 0130 $direction_multiplier = ((isset($info['jpg']['exif']['GPS']['GPSLongitudeRef']) && ($info['jpg']['exif']['GPS']['GPSLongitudeRef'] == 'W')) ? -1 : 1); 0131 foreach ($info['jpg']['exif']['GPS']['GPSLongitude'] as $key => $value) { 0132 $computed_longitude[$key] = getid3_lib::DecimalizeFraction($value); 0133 } 0134 $info['jpg']['exif']['GPS']['computed']['longitude'] = $direction_multiplier * ($computed_longitude[0] + ($computed_longitude[1] / 60) + ($computed_longitude[2] / 3600)); 0135 } 0136 if (isset($info['jpg']['exif']['GPS']['GPSAltitudeRef'])) { 0137 $info['jpg']['exif']['GPS']['GPSAltitudeRef'] = ord($info['jpg']['exif']['GPS']['GPSAltitudeRef']); // 0 = above sea level; 1 = below sea level 0138 } 0139 if (isset($info['jpg']['exif']['GPS']['GPSAltitude'])) { 0140 $direction_multiplier = (!empty($info['jpg']['exif']['GPS']['GPSAltitudeRef']) ? -1 : 1); // 0 = above sea level; 1 = below sea level 0141 $info['jpg']['exif']['GPS']['computed']['altitude'] = $direction_multiplier * getid3_lib::DecimalizeFraction($info['jpg']['exif']['GPS']['GPSAltitude']); 0142 } 0143 0144 } 0145 0146 0147 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.xmp.php', __FILE__, true); 0148 if (isset($info['filenamepath'])) { 0149 $image_xmp = new Image_XMP($info['filenamepath']); 0150 $xmp_raw = $image_xmp->getAllTags(); 0151 foreach ($xmp_raw as $key => $value) { 0152 if (strpos($key, ':')) { 0153 list($subsection, $tagname) = explode(':', $key); 0154 $info['xmp'][$subsection][$tagname] = $this->CastAsAppropriate($value); 0155 } else { 0156 $info['warning'][] = 'XMP: expecting "<subsection>:<tagname>", found "'.$key.'"'; 0157 } 0158 } 0159 } 0160 0161 if (!$returnOK) { 0162 unset($info['fileformat']); 0163 return false; 0164 } 0165 return true; 0166 } 0167 0168 0169 public function CastAsAppropriate($value) { 0170 if (is_array($value)) { 0171 return $value; 0172 } elseif (preg_match('#^[0-9]+/[0-9]+$#', $value)) { 0173 return getid3_lib::DecimalizeFraction($value); 0174 } elseif (preg_match('#^[0-9]+$#', $value)) { 0175 return getid3_lib::CastAsInt($value); 0176 } elseif (preg_match('#^[0-9\.]+$#', $value)) { 0177 return (float) $value; 0178 } 0179 return $value; 0180 } 0181 0182 0183 public function IPTCrecordName($iptc_record) { 0184 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html 0185 static $IPTCrecordName = array(); 0186 if (empty($IPTCrecordName)) { 0187 $IPTCrecordName = array( 0188 1 => 'IPTCEnvelope', 0189 2 => 'IPTCApplication', 0190 3 => 'IPTCNewsPhoto', 0191 7 => 'IPTCPreObjectData', 0192 8 => 'IPTCObjectData', 0193 9 => 'IPTCPostObjectData', 0194 ); 0195 } 0196 return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : ''); 0197 } 0198 0199 0200 public function IPTCrecordTagName($iptc_record, $iptc_tagkey) { 0201 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html 0202 static $IPTCrecordTagName = array(); 0203 if (empty($IPTCrecordTagName)) { 0204 $IPTCrecordTagName = array( 0205 1 => array( // IPTC EnvelopeRecord Tags 0206 0 => 'EnvelopeRecordVersion', 0207 5 => 'Destination', 0208 20 => 'FileFormat', 0209 22 => 'FileVersion', 0210 30 => 'ServiceIdentifier', 0211 40 => 'EnvelopeNumber', 0212 50 => 'ProductID', 0213 60 => 'EnvelopePriority', 0214 70 => 'DateSent', 0215 80 => 'TimeSent', 0216 90 => 'CodedCharacterSet', 0217 100 => 'UniqueObjectName', 0218 120 => 'ARMIdentifier', 0219 122 => 'ARMVersion', 0220 ), 0221 2 => array( // IPTC ApplicationRecord Tags 0222 0 => 'ApplicationRecordVersion', 0223 3 => 'ObjectTypeReference', 0224 4 => 'ObjectAttributeReference', 0225 5 => 'ObjectName', 0226 7 => 'EditStatus', 0227 8 => 'EditorialUpdate', 0228 10 => 'Urgency', 0229 12 => 'SubjectReference', 0230 15 => 'Category', 0231 20 => 'SupplementalCategories', 0232 22 => 'FixtureIdentifier', 0233 25 => 'Keywords', 0234 26 => 'ContentLocationCode', 0235 27 => 'ContentLocationName', 0236 30 => 'ReleaseDate', 0237 35 => 'ReleaseTime', 0238 37 => 'ExpirationDate', 0239 38 => 'ExpirationTime', 0240 40 => 'SpecialInstructions', 0241 42 => 'ActionAdvised', 0242 45 => 'ReferenceService', 0243 47 => 'ReferenceDate', 0244 50 => 'ReferenceNumber', 0245 55 => 'DateCreated', 0246 60 => 'TimeCreated', 0247 62 => 'DigitalCreationDate', 0248 63 => 'DigitalCreationTime', 0249 65 => 'OriginatingProgram', 0250 70 => 'ProgramVersion', 0251 75 => 'ObjectCycle', 0252 80 => 'By-line', 0253 85 => 'By-lineTitle', 0254 90 => 'City', 0255 92 => 'Sub-location', 0256 95 => 'Province-State', 0257 100 => 'Country-PrimaryLocationCode', 0258 101 => 'Country-PrimaryLocationName', 0259 103 => 'OriginalTransmissionReference', 0260 105 => 'Headline', 0261 110 => 'Credit', 0262 115 => 'Source', 0263 116 => 'CopyrightNotice', 0264 118 => 'Contact', 0265 120 => 'Caption-Abstract', 0266 121 => 'LocalCaption', 0267 122 => 'Writer-Editor', 0268 125 => 'RasterizedCaption', 0269 130 => 'ImageType', 0270 131 => 'ImageOrientation', 0271 135 => 'LanguageIdentifier', 0272 150 => 'AudioType', 0273 151 => 'AudioSamplingRate', 0274 152 => 'AudioSamplingResolution', 0275 153 => 'AudioDuration', 0276 154 => 'AudioOutcue', 0277 184 => 'JobID', 0278 185 => 'MasterDocumentID', 0279 186 => 'ShortDocumentID', 0280 187 => 'UniqueDocumentID', 0281 188 => 'OwnerID', 0282 200 => 'ObjectPreviewFileFormat', 0283 201 => 'ObjectPreviewFileVersion', 0284 202 => 'ObjectPreviewData', 0285 221 => 'Prefs', 0286 225 => 'ClassifyState', 0287 228 => 'SimilarityIndex', 0288 230 => 'DocumentNotes', 0289 231 => 'DocumentHistory', 0290 232 => 'ExifCameraInfo', 0291 ), 0292 3 => array( // IPTC NewsPhoto Tags 0293 0 => 'NewsPhotoVersion', 0294 10 => 'IPTCPictureNumber', 0295 20 => 'IPTCImageWidth', 0296 30 => 'IPTCImageHeight', 0297 40 => 'IPTCPixelWidth', 0298 50 => 'IPTCPixelHeight', 0299 55 => 'SupplementalType', 0300 60 => 'ColorRepresentation', 0301 64 => 'InterchangeColorSpace', 0302 65 => 'ColorSequence', 0303 66 => 'ICC_Profile', 0304 70 => 'ColorCalibrationMatrix', 0305 80 => 'LookupTable', 0306 84 => 'NumIndexEntries', 0307 85 => 'ColorPalette', 0308 86 => 'IPTCBitsPerSample', 0309 90 => 'SampleStructure', 0310 100 => 'ScanningDirection', 0311 102 => 'IPTCImageRotation', 0312 110 => 'DataCompressionMethod', 0313 120 => 'QuantizationMethod', 0314 125 => 'EndPoints', 0315 130 => 'ExcursionTolerance', 0316 135 => 'BitsPerComponent', 0317 140 => 'MaximumDensityRange', 0318 145 => 'GammaCompensatedValue', 0319 ), 0320 7 => array( // IPTC PreObjectData Tags 0321 10 => 'SizeMode', 0322 20 => 'MaxSubfileSize', 0323 90 => 'ObjectSizeAnnounced', 0324 95 => 'MaximumObjectSize', 0325 ), 0326 8 => array( // IPTC ObjectData Tags 0327 10 => 'SubFile', 0328 ), 0329 9 => array( // IPTC PostObjectData Tags 0330 10 => 'ConfirmedObjectSize', 0331 ), 0332 ); 0333 0334 } 0335 return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey); 0336 } 0337 0338 }