// // available at http://getid3.sourceforge.net // // or http://www.getid3.org // // also https://github.com/JamesHeinrich/getID3 // ///////////////////////////////////////////////////////////////// // See readme.txt for more details // ///////////////////////////////////////////////////////////////// // // // module.archive.tiff.php // // module for analyzing TIFF files // // dependencies: NONE // // /// ///////////////////////////////////////////////////////////////// class getid3_tiff extends getid3_handler { public function Analyze() { $info = &$this->getid3->info; $this->fseek($info['avdataoffset']); $TIFFheader = $this->fread(4); switch (substr($TIFFheader, 0, 2)) { case 'II': $info['tiff']['byte_order'] = 'Intel'; break; case 'MM': $info['tiff']['byte_order'] = 'Motorola'; break; default: $this->error('Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset']); return false; break; } $info['fileformat'] = 'tiff'; $info['video']['dataformat'] = 'tiff'; $info['video']['lossless'] = true; $info['tiff']['ifd'] = array(); $CurrentIFD = array(); $FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8); $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); while ($nextIFDoffset > 0) { $CurrentIFD['offset'] = $nextIFDoffset; $this->fseek($info['avdataoffset'] + $nextIFDoffset); $CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) { $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); $CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4); switch ($CurrentIFD['fields'][$i]['raw']['type']) { case 1: // BYTE An 8-bit unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']); } else { $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) { $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3); } else { $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 3: // SHORT A 16-bit (2-byte) unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) { $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']); } else { $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 4: // LONG A 32-bit (4-byte) unsigned integer. if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) { $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } else { $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']); } break; case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator. break; } } $info['tiff']['ifd'][] = $CurrentIFD; $CurrentIFD = array(); $nextIFDoffset = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']); } foreach ($info['tiff']['ifd'] as $IFDid => $IFDarray) { foreach ($IFDarray['fields'] as $key => $fieldarray) { switch ($fieldarray['raw']['tag']) { case 256: // ImageWidth case 257: // ImageLength case 258: // BitsPerSample case 259: // Compression if (!isset($fieldarray['value'])) { $this->fseek($fieldarray['offset']); $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); } break; case 270: // ImageDescription case 271: // Make case 272: // Model case 305: // Software case 306: // DateTime case 315: // Artist case 316: // HostComputer if (isset($fieldarray['value'])) { $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value']; } else { $this->fseek($fieldarray['offset']); $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $this->fread($fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]); } break; } switch ($fieldarray['raw']['tag']) { case 256: // ImageWidth $info['video']['resolution_x'] = $fieldarray['value']; break; case 257: // ImageLength $info['video']['resolution_y'] = $fieldarray['value']; break; case 258: // BitsPerSample if (isset($fieldarray['value'])) { $info['video']['bits_per_sample'] = $fieldarray['value']; } else { $info['video']['bits_per_sample'] = 0; for ($i = 0; $i < $fieldarray['raw']['length']; $i++) { $info['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $info['tiff']['byte_order']); } } break; case 259: // Compression $info['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']); break; case 270: // ImageDescription case 271: // Make case 272: // Model case 305: // Software case 306: // DateTime case 315: // Artist case 316: // HostComputer $TIFFcommentName = $this->TIFFcommentName($fieldarray['raw']['tag']); if (isset($info['tiff']['comments'][$TIFFcommentName])) { $info['tiff']['comments'][$TIFFcommentName][] = $info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']; } else { $info['tiff']['comments'][$TIFFcommentName] = array($info['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data']); } break; default: break; } } } return true; } public function TIFFendian2Int($bytestring, $byteorder) { if ($byteorder == 'Intel') { return getid3_lib::LittleEndian2Int($bytestring); } elseif ($byteorder == 'Motorola') { return getid3_lib::BigEndian2Int($bytestring); } return false; } public function TIFFcompressionMethod($id) { static $TIFFcompressionMethod = array(); if (empty($TIFFcompressionMethod)) { $TIFFcompressionMethod = array( 1 => 'Uncompressed', 2 => 'Huffman', 3 => 'Fax - CCITT 3', 5 => 'LZW', 32773 => 'PackBits', ); } return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')'); } public function TIFFcommentName($id) { static $TIFFcommentName = array(); if (empty($TIFFcommentName)) { $TIFFcommentName = array( 270 => 'imagedescription', 271 => 'make', 272 => 'model', 305 => 'software', 306 => 'datetime', 315 => 'artist', 316 => 'hostcomputer', ); } return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')'); } }