\IMAGETYPE_GIF, 'jpg' => \IMAGETYPE_JPEG, 'jpeg' => \IMAGETYPE_JPEG, 'png' => \IMAGETYPE_PNG ); /** * Supported mime types * * @var array * */ protected $supportedMimeTypes = array( \IMAGETYPE_GIF => 'image/gif', \IMAGETYPE_JPEG => 'image/jpeg', \IMAGETYPE_PNG => 'image/png' ); /** * Construct * * @param string $filename * */ public function __construct($filename = '') { parent::__construct(); if($filename && is_readable($filename)) { $this->filename = $filename; } } /** * parse Image and return information * * @param string $filename the file we want to inspect * @param bool $parseAppmarker (IPTC), default is FALSE * @return null|false|array * */ public function inspect($filename = '', $parseAppmarker = false) { if($filename) $this->filename = $filename; if(!$this->filename || !is_readable($this->filename)) return null; if(!$this->extension) $this->extension = pathinfo($this->filename, \PATHINFO_EXTENSION); $this->extension = strtolower($this->extension); $additionalInfo = array(); $info = @getimagesize($filename, $additionalInfo); if($info === false) return false; // read basic data if(isset($info[2])) { $imageType = $info[2]; } else if(function_exists("exif_imagetype")) { $imageType = exif_imagetype($this->filename); } else if(isset($this->supportedImageTypes[$this->extension])) { $imageType = $this->supportedImageTypes[$this->extension]; } else { // default fallback so that $imageType is defined $imageType = \IMAGETYPE_JPEG; } $this->info['width'] = $info[0]; $this->info['height'] = $info[1]; $this->info['imageType'] = $imageType; $this->info['mime'] = isset($this->supportedMimeTypes[$imageType]) ? $this->supportedMimeTypes[$imageType] : 'unsupported'; // Infos about Orientation and optional corrections for rotate and flip $tmp = $this->checkOrientation($this->filename); $this->info['orientation'] = $tmp['orientation']; // Exif-Orientation value :: integer range 1 - 8, 0 on failure $this->info['rotate'] = $tmp['rotate']; // empty or 0 or degrees :: 0 | 90 | 180 | 270 $this->info['flip'] = $tmp['flip']; // empty | horizontal | vertical :: 0 | 1 | 2 // additional, more indepth data $this->info['channels'] = isset($info['channels']) ? $info['channels'] : -1; $this->info['bits'] = isset($info['bits']) ? $info['bits'] : -1; switch($imageType) { case \IMAGETYPE_GIF: $this->loadImageInfoGif(); break; case \IMAGETYPE_JPEG: $this->loadImageInfoJpg(); break; case \IMAGETYPE_PNG: $this->loadImageInfoPng(); break; } // read appmarker metadata if present $this->info['appmarker'] = $iptcRaw = null; if(is_array($additionalInfo) && $parseAppmarker) { $appmarker = array(); foreach($additionalInfo as $k => $v) { $appmarker[$k] = substr($v, 0, strpos($v, chr(0))); } $this->info['appmarker'] = $appmarker; if(isset($additionalInfo['APP13'])) { $iptc = iptcparse($additionalInfo['APP13']); if(is_array($iptc)) $iptcRaw = $iptc; } } // return the result return array( 'filename' => $this->filename, 'extension' => $this->extension, 'imageType' => $imageType, 'info' => $this->info, 'iptcRaw' => $iptcRaw ); } /** * Check orientation (@horst) * * @param array * @return array * @todo there is already a checkOrientation method in ImageSizerEngine - do we need both? * */ protected function checkOrientation($filename) { // first value is rotation-degree and second value is flip-mode: 0=NONE | 1=HORIZONTAL | 2=VERTICAL $corrections = array( '1' => array(0, 0), '2' => array(0, 1), '3' => array(180, 0), '4' => array(0, 2), '5' => array(270, 1), '6' => array(270, 0), '7' => array(90, 1), '8' => array(90, 0) ); $result = array('orientation' => 0, 'rotate' => 0, 'flip' => 0); $supportedExifMimeTypes = array('image/jpeg', 'image/tiff'); // hardcoded by PHP $mime = isset($this->info['mime']) ? $this->info['mime'] : 'no'; if(!function_exists('exif_read_data') || !in_array($mime, $supportedExifMimeTypes)) { return $result; } $exif = @exif_read_data($filename, 'IFD0'); if(!is_array($exif) || !isset($exif['Orientation']) || !in_array(strval($exif['Orientation']), array_keys($corrections)) ) { return $result; } $correctionArray = $corrections[strval($exif['Orientation'])]; $result = array( 'orientation' => $exif['Orientation'], 'rotate' => $correctionArray[0], 'flip' => $correctionArray[1] ); return $result; } /** * parse PNG Image and collect information into $this->info * * @return bool * */ protected function loadImageInfoPng() { $png = new PWPNG(); if(!$png->loadFile($this->filename)) { return false; } $this->info = array_merge($this->info, $png->info); unset($png); return true; } /** * parse GIF Image and collect information into $this->info * * @return bool * */ protected function loadImageInfoGif() { $gif = new PWGIF(false); // passing true also loads BitmapData $iIndex = 0; if(!$gif->loadFile($this->filename, $iIndex)) { return false; } $gi = $gif->m_img; // = CGIFIMAGE $gfh = $gif->m_gfh; // = CGIFFILEHEADER $gih = $gif->m_img->m_gih; // = CGIFIMAGEHEADER $i = $this->info; $i['width'] = $gfh->m_nWidth; $i['height'] = $gfh->m_nHeight; $i['gifversion'] = $gfh->m_lpVer; $i['animated'] = $gfh->m_bAnimated; $i['delay'] = isset($gi->m_nDelay) ? $gi->m_nDelay : ''; $i['trans'] = isset($gi->m_bTrans) ? $gi->m_bTrans : false; $i['transcolor'] = isset($gi->m_nTrans) ? $gi->m_nTrans : ''; $i['bgcolor'] = $gfh->m_nBgColor; $i['numcolors'] = isset($gfh->m_colorTable->m_nColors) ? $gfh->m_colorTable->m_nColors : 0; $i['interlace'] = $gih->m_bInterlace; $this->info = $i; unset($gif, $gih, $gfh, $gi, $i); return true; // CGIFFILEHEADER // numColors = m_colorTable->m_nColors // m_lpVer // m_nWidth // m_nHeight // m_bGlobalClr // m_nColorRes // m_bSorted // m_nTableSize // m_nBgColor // m_nPixelRatio // m_bAnimated // // CGIFIMAGEHEADER // m_nLeft // m_nTop // m_nWidth // m_nHeight // m_bLocalClr // m_bInterlace // m_bSorted // m_nTableSize // m_colorTable // // CGIFIMAGE // m_disp // m_bUser // m_bTrans // m_nDelay // m_nTrans // m_lpComm } /** * parse JPEG Image and collect information into $this->info * */ protected function loadImageInfoJpg() { } }