<?php
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <getid3@users.sourceforge.net>  //
//        available at http://getid3.sourceforge.net          ///
/////////////////////////////////////////////////////////////////
//                                                             //
// getid3.png.php - part of getID3()                           //
// See getid3.readme.txt for more details                      //
//                                                             //
/////////////////////////////////////////////////////////////////

function getPNGHeaderFilepointer(&$fd, &$ThisFileInfo) {
	$ThisFileInfo['fileformat']          = 'png';
	$ThisFileInfo['video']['dataformat'] = 'png';
	$ThisFileInfo['video']['lossless']   = false;

	fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
	$PNGfiledata = fread($fd, FREAD_BUFFER_SIZE);
	$offset = 0;

	$PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A
	$offset += 8;
	if ($PNGidentifier != chr(0x89).chr(0x50).chr(0x4E).chr(0x47).chr(0x0D).chr(0x0A).chr(0x1A).chr(0x0A)) {
		$ThisFileInfo['error'] .= "\n".'First 8 bytes of file ('.PrintHexBytes($PNGidentifier).') did not match expected PNG identifier';
		unset($ThisFileInfo['fileformat']);
		return false;
	}

	while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) {
		$chunk['data_length'] = BigEndian2Int(substr($PNGfiledata, $offset, 4));
		$offset += 4;
		while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) {
			$PNGfiledata .= fread($fd, FREAD_BUFFER_SIZE);
		}
		$chunk['type_text']   =               substr($PNGfiledata, $offset, 4);
		$offset += 4;
		$chunk['type_raw']    = BigEndian2Int($chunk['type_text']);
		$chunk['data']        =               substr($PNGfiledata, $offset, $chunk['data_length']);
		$offset += $chunk['data_length'];
		$chunk['crc']         = BigEndian2Int(substr($PNGfiledata, $offset, 4));
		$offset += 4;

		$chunk['flags']['ancilliary']   = (bool) ($chunk['type_raw'] & 0x20000000);
		$chunk['flags']['private']      = (bool) ($chunk['type_raw'] & 0x00200000);
		$chunk['flags']['reserved']     = (bool) ($chunk['type_raw'] & 0x00002000);
		$chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020);

		switch ($chunk['type_text']) {

			case 'IHDR': // Image Header
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['width']                     = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  0, 4));
				$ThisFileInfo['png'][$chunk['type_text']]['height']                    = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  4, 4));
				$ThisFileInfo['png'][$chunk['type_text']]['raw']['bit_depth']          = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  8, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type']         = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  9, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['raw']['compression_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 10, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['raw']['filter_method']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 11, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['raw']['interlace_method']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 12, 1));

				$ThisFileInfo['png'][$chunk['type_text']]['compression_method_text']   = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['raw']['compression_method']);
				$ThisFileInfo['png'][$chunk['type_text']]['color_type']['palette']     = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x01);
				$ThisFileInfo['png'][$chunk['type_text']]['color_type']['true_color']  = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x02);
				$ThisFileInfo['png'][$chunk['type_text']]['color_type']['alpha']       = (bool) ($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'] & 0x04);

				$ThisFileInfo['video']['resolution_x']    = $ThisFileInfo['png'][$chunk['type_text']]['width'];
				$ThisFileInfo['video']['resolution_y']    = $ThisFileInfo['png'][$chunk['type_text']]['height'];

				$ThisFileInfo['video']['bits_per_sample'] = IHDRcalculateBitsPerSample($ThisFileInfo['png'][$chunk['type_text']]['raw']['color_type'], $ThisFileInfo['png'][$chunk['type_text']]['raw']['bit_depth']);
				break;


			case 'PLTE': // Palette
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$paletteoffset = 0;
				for ($i = 0; $i <= 255; $i++) {
					//$ThisFileInfo['png'][$chunk['type_text']]['red'][$i]   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					//$ThisFileInfo['png'][$chunk['type_text']]['green'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					//$ThisFileInfo['png'][$chunk['type_text']]['blue'][$i]  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					$red   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					$green = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					$blue  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $paletteoffset++, 1));
					$ThisFileInfo['png'][$chunk['type_text']][$i] = (($red << 16) | ($green << 8) | ($blue));
				}
				break;


			case 'tRNS': // Transparency
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) {
					case 0:
						$ThisFileInfo['png'][$chunk['type_text']]['transparent_color_gray']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2));
						break;

					case 2:
						$ThisFileInfo['png'][$chunk['type_text']]['transparent_color_red']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2));
						$ThisFileInfo['png'][$chunk['type_text']]['transparent_color_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2));
						$ThisFileInfo['png'][$chunk['type_text']]['transparent_color_blue']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2));
						break;

					case 3:
						for ($i = 0; $i < strlen($ThisFileInfo['png'][$chunk['type_text']]['header']['data']); $i++) {
							$ThisFileInfo['png'][$chunk['type_text']]['palette_opacity'][$i] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $i, 1));
						}
						break;

					case 4:
					case 6:
						$ThisFileInfo['error'] .= "\n".'Invalid color_type in tRNS chunk: '.$ThisFileInfo['png']['IHDR']['raw']['color_type'];

					default:
						$ThisFileInfo['warning'] .= "\n".'Unhandled color_type in tRNS chunk: '.$ThisFileInfo['png']['IHDR']['raw']['color_type'];
						break;
				}
				break;


			case 'gAMA': // Image Gamma
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['gamma']  = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']) / 100000;
				break;


			case 'cHRM': // Primary Chromaticities
				$ThisFileInfo['png'][$chunk['type_text']]['header']  = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['white_x'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  0, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['white_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  4, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['red_y']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  8, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['red_y']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 12, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['green_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 16, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['green_y'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 20, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['blue_y']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 24, 4)) / 100000;
				$ThisFileInfo['png'][$chunk['type_text']]['blue_y']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 28, 4)) / 100000;
				break;


			case 'sRGB': // Standard RGB Color Space
				$ThisFileInfo['png'][$chunk['type_text']]['header']                 = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['reindering_intent']      = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']);
				$ThisFileInfo['png'][$chunk['type_text']]['reindering_intent_text'] = PNGsRGBintentLookup($ThisFileInfo['png'][$chunk['type_text']]['reindering_intent']);
				break;


			case 'iCCP': // Embedded ICC Profile
				$ThisFileInfo['png'][$chunk['type_text']]['header']                  = $chunk;
				list($profilename, $compressiondata)                                = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['profile_name']            = $profilename;
				$ThisFileInfo['png'][$chunk['type_text']]['compression_method']      = BigEndian2Int(substr($compressiondata, 0, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['compression_profile']     = substr($compressiondata, 1);

				$ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']);
				break;


			case 'tEXt': // Textual Data
				$ThisFileInfo['png'][$chunk['type_text']]['header']  = $chunk;
				list($keyword, $text)                               = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['keyword'] = $keyword;
				$ThisFileInfo['png'][$chunk['type_text']]['text']    = $text;

				$ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text'];
				break;


			case 'zTXt': // Compressed Textual Data
				$ThisFileInfo['png'][$chunk['type_text']]['header']                  = $chunk;
				list($keyword, $otherdata)                                          = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['keyword']                 = $keyword;
				$ThisFileInfo['png'][$chunk['type_text']]['compression_method']      = BigEndian2Int(substr($otherdata, 0, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['compressed_text']         = substr($otherdata, 1);
				$ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']);
				switch ($ThisFileInfo['png'][$chunk['type_text']]['compression_method']) {
					case 0:
						$ThisFileInfo['png'][$chunk['type_text']]['text']            = gzuncompress($ThisFileInfo['png'][$chunk['type_text']]['compressed_text']);
						break;

					default:
						// unknown compression method
						break;
				}

				if (isset($ThisFileInfo['png'][$chunk['type_text']]['text'])) {
					$ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text'];
				}
				break;


			case 'iTXt': // International Textual Data
				$ThisFileInfo['png'][$chunk['type_text']]['header']                  = $chunk;
				list($keyword, $otherdata)                                          = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['keyword']                 = $keyword;
				$ThisFileInfo['png'][$chunk['type_text']]['compression']             = (bool) BigEndian2Int(substr($otherdata, 0, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['compression_method']      = BigEndian2Int(substr($otherdata, 1, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['compression_method_text'] = PNGcompressionMethodLookup($ThisFileInfo['png'][$chunk['type_text']]['compression_method']);
				list($languagetag, $translatedkeyword, $text)                       = explode(chr(0x00), substr($otherdata, 2), 3);
				$ThisFileInfo['png'][$chunk['type_text']]['language_tag']            = $languagetag;
				$ThisFileInfo['png'][$chunk['type_text']]['translated_keyword']      = utf8_decode($translatedkeyword);

				if ($ThisFileInfo['png'][$chunk['type_text']]['compression']) {

					switch ($ThisFileInfo['png'][$chunk['type_text']]['compression_method']) {
						case 0:
							$ThisFileInfo['png'][$chunk['type_text']]['text']        = utf8_decode(gzuncompress($text));
							break;

						default:
							// unknown compression method
							break;
					}

				} else {

					$ThisFileInfo['png'][$chunk['type_text']]['text']                = utf8_decode($text);

				}

				if (isset($ThisFileInfo['png'][$chunk['type_text']]['text'])) {
					$ThisFileInfo['png']['comments'][$ThisFileInfo['png'][$chunk['type_text']]['keyword']][] = $ThisFileInfo['png'][$chunk['type_text']]['text'];
				}
				break;


			case 'bKGD': // Background Color
				$ThisFileInfo['png'][$chunk['type_text']]['header']                   = $chunk;
				switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) {
					case 0:
					case 4:
						$ThisFileInfo['png'][$chunk['type_text']]['background_gray']  = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']);
						break;

					case 2:
					case 6:
						$ThisFileInfo['png'][$chunk['type_text']]['background_red']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth']));
						$ThisFileInfo['png'][$chunk['type_text']]['background_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth']));
						$ThisFileInfo['png'][$chunk['type_text']]['background_blue']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2 * $ThisFileInfo['png']['IHDR']['raw']['bit_depth'], $ThisFileInfo['png']['IHDR']['raw']['bit_depth']));
						break;

					case 3:
						$ThisFileInfo['png'][$chunk['type_text']]['background_index'] = BigEndian2Int($ThisFileInfo['png'][$chunk['type_text']]['header']['data']);
						break;

					default:
						break;
				}
				break;


			case 'pHYs': // Physical Pixel Dimensions
				$ThisFileInfo['png'][$chunk['type_text']]['header']                 = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['pixels_per_unit_x']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4));
				$ThisFileInfo['png'][$chunk['type_text']]['pixels_per_unit_y']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4));
				$ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']         = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['unit']                   = PNGpHYsUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']);
				break;


			case 'sBIT': // Significant Bits
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				switch ($ThisFileInfo['png']['IHDR']['raw']['color_type']) {
					case 0:
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_gray']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
						break;

					case 2:
					case 3:
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_red']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_blue']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1));
						break;

					case 4:
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_gray']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_alpha'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1));
						break;

					case 6:
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_red']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_green'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_blue']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1));
						$ThisFileInfo['png'][$chunk['type_text']]['significant_bits_alpha'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 3, 1));
						break;

					default:
						break;
				}
				break;


			case 'sPLT': // Suggested Palette
				$ThisFileInfo['png'][$chunk['type_text']]['header']                           = $chunk;
				list($palettename, $otherdata)                                               = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['palette_name']                     = $palettename;
				$sPLToffset = 0;
				$ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bits']                = BigEndian2Int(substr($otherdata, $sPLToffset, 1));
				$sPLToffset += 1;
				$ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']               = $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bits'] / 8;
				$paletteCounter = 0;
				while ($sPLToffset < strlen($otherdata)) {
					$ThisFileInfo['png'][$chunk['type_text']]['red'][$paletteCounter]       = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']));
					$sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'];
					$ThisFileInfo['png'][$chunk['type_text']]['green'][$paletteCounter]     = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']));
					$sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'];
					$ThisFileInfo['png'][$chunk['type_text']]['blue'][$paletteCounter]      = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']));
					$sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'];
					$ThisFileInfo['png'][$chunk['type_text']]['alpha'][$paletteCounter]     = BigEndian2Int(substr($otherdata, $sPLToffset, $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes']));
					$sPLToffset += $ThisFileInfo['png'][$chunk['type_text']]['sample_depth_bytes'];
					$ThisFileInfo['png'][$chunk['type_text']]['frequency'][$paletteCounter] = BigEndian2Int(substr($otherdata, $sPLToffset, 2));
					$sPLToffset += 2;
					$paletteCounter++;
				}
				break;


			case 'hIST': // Palette Histogram
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$hISTcounter = 0;
				while ($hISTcounter < strlen($ThisFileInfo['png'][$chunk['type_text']]['header']['data'])) {
					$ThisFileInfo['png'][$chunk['type_text']][$hISTcounter] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $hISTcounter / 2, 2));
					$hISTcounter += 2;
				}
				break;


			case 'tIME': // Image Last-Modification Time
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['year']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 2));
				$ThisFileInfo['png'][$chunk['type_text']]['month']  = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['day']    = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 3, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['hour']   = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['minute'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 5, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['second'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 6, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['unix']   = gmmktime($ThisFileInfo['png'][$chunk['type_text']]['hour'], $ThisFileInfo['png'][$chunk['type_text']]['minute'], $ThisFileInfo['png'][$chunk['type_text']]['second'], $ThisFileInfo['png'][$chunk['type_text']]['month'], $ThisFileInfo['png'][$chunk['type_text']]['day'], $ThisFileInfo['png'][$chunk['type_text']]['year']);
				break;


			case 'oFFs': // Image Offset
				$ThisFileInfo['png'][$chunk['type_text']]['header']         = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['position_x']     = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 4), false, true);
				$ThisFileInfo['png'][$chunk['type_text']]['position_y']     = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 4, 4), false, true);
				$ThisFileInfo['png'][$chunk['type_text']]['unit_specifier'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 8, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['unit']           = PNGoFFsUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']);
				break;


			case 'pCAL': // Calibration Of Pixel Values
				$ThisFileInfo['png'][$chunk['type_text']]['header']             = $chunk;
				list($calibrationname, $otherdata)                             = explode(chr(0x00), $ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2);
				$ThisFileInfo['png'][$chunk['type_text']]['calibration_name']   = $calibrationname;
				$pCALoffset = 0;
				$ThisFileInfo['png'][$chunk['type_text']]['original_zero']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 4), false, true);
				$pCALoffset += 4;
				$ThisFileInfo['png'][$chunk['type_text']]['original_max']       = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 4), false, true);
				$pCALoffset += 4;
				$ThisFileInfo['png'][$chunk['type_text']]['equation_type']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 1));
				$pCALoffset += 1;
				$ThisFileInfo['png'][$chunk['type_text']]['equation_type_text'] = PNGpCALequationTypeLookup($ThisFileInfo['png'][$chunk['type_text']]['equation_type']);
				$ThisFileInfo['png'][$chunk['type_text']]['parameter_count']    = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset, 1));
				$pCALoffset += 1;
				$ThisFileInfo['png'][$chunk['type_text']]['parameters']         = explode(chr(0x00), substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], $pCALoffset));
				break;


			case 'sCAL': // Physical Scale Of Image Subject
				$ThisFileInfo['png'][$chunk['type_text']]['header']         = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']]['unit_specifier'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
				$ThisFileInfo['png'][$chunk['type_text']]['unit']           = PNGsCALUnitLookup($ThisFileInfo['png'][$chunk['type_text']]['unit_specifier']);
				list($pixelwidth, $pixelheight)                            = explode(chr(0x00), substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1));
				$ThisFileInfo['png'][$chunk['type_text']]['pixel_width']    = $pixelwidth;
				$ThisFileInfo['png'][$chunk['type_text']]['pixel_height']   = $pixelheight;
				break;


			case 'gIFg': // GIF Graphic Control Extension
				$gIFgCounter = 0;
				if (isset($ThisFileInfo['png'][$chunk['type_text']]) && is_array($ThisFileInfo['png'][$chunk['type_text']])) {
					$gIFgCounter = count($ThisFileInfo['png'][$chunk['type_text']]);
				}
				$ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['header']          = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['disposal_method'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 0, 1));
				$ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['user_input_flag'] = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 1, 1));
				$ThisFileInfo['png'][$chunk['type_text']][$gIFgCounter]['delay_time']      = BigEndian2Int(substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 2, 2));
				break;


			case 'gIFx': // GIF Application Extension
				$gIFxCounter = 0;
				if (isset($ThisFileInfo['png'][$chunk['type_text']]) && is_array($ThisFileInfo['png'][$chunk['type_text']])) {
					$gIFxCounter = count($ThisFileInfo['png'][$chunk['type_text']]);
				}
				$ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['header']                 = $chunk;
				$ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['application_identifier'] = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  0, 8);
				$ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['authentication_code']    = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'],  8, 3);
				$ThisFileInfo['png'][$chunk['type_text']][$gIFxCounter]['application_data']       = substr($ThisFileInfo['png'][$chunk['type_text']]['header']['data'], 11);
				break;


			case 'IDAT': // Image Data
				$idatinformationfieldindex = 0;
				if (isset($ThisFileInfo['png']['IDAT']) && is_array($ThisFileInfo['png']['IDAT'])) {
					$idatinformationfieldindex = count($ThisFileInfo['png']['IDAT']);
				}
				unset($chunk['data']);
				$ThisFileInfo['png'][$chunk['type_text']][$idatinformationfieldindex]['header'] = $chunk;
				break;


			case 'IEND': // Image Trailer
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				break;


			default:
				//unset($chunk['data']);
				$ThisFileInfo['png'][$chunk['type_text']]['header'] = $chunk;
				$ThisFileInfo['warning'] .= "\n".'Unhandled chunk type: '.$chunk['type_text'];
				break;
		}
	}

	// PNG tags have highest priority
	if (!empty($ThisFileInfo['png']['comments'])) {
		CopyFormatCommentsToRootComments($ThisFileInfo['png']['comments'], $ThisFileInfo, true, true, true);

		// add tag to array of tags
		$ThisFileInfo['tags'][] = 'png';
	}

	return true;
}

function PNGsRGBintentLookup($sRGB) {
	static $PNGsRGBintentLookup = array();
	if (empty($PNGsRGBintentLookup)) {
		$PNGsRGBintentLookup[0] = 'Perceptual';
		$PNGsRGBintentLookup[1] = 'Relative colorimetric';
		$PNGsRGBintentLookup[2] = 'Saturation';
		$PNGsRGBintentLookup[3] = 'Absolute colorimetric';
	}
	return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid');
}

function PNGcompressionMethodLookup($compressionmethod) {
	static $PNGcompressionMethodLookup = array();
	if (empty($PNGcompressionMethodLookup)) {
		$PNGcompressionMethodLookup[0] = 'deflate/inflate';
	}
	return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid');
}

function PNGpHYsUnitLookup($unitid) {
	static $PNGpHYsUnitLookup = array();
	if (empty($PNGpHYsUnitLookup)) {
		$PNGpHYsUnitLookup[0] = 'unknown';
		$PNGpHYsUnitLookup[1] = 'meter';
	}
	return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid');
}

function PNGoFFsUnitLookup($unitid) {
	static $PNGoFFsUnitLookup = array();
	if (empty($PNGoFFsUnitLookup)) {
		$PNGoFFsUnitLookup[0] = 'pixel';
		$PNGoFFsUnitLookup[1] = 'micrometer';
	}
	return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid');
}

function PNGpCALequationTypeLookup($equationtype) {
	static $PNGpCALequationTypeLookup = array();
	if (empty($PNGpCALequationTypeLookup)) {
		$PNGpCALequationTypeLookup[0] = 'Linear mapping';
		$PNGpCALequationTypeLookup[1] = 'Base-e exponential mapping';
		$PNGpCALequationTypeLookup[2] = 'Arbitrary-base exponential mapping';
		$PNGpCALequationTypeLookup[3] = 'Hyperbolic mapping';
	}
	return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid');
}

function PNGsCALUnitLookup($unitid) {
	static $PNGsCALUnitLookup = array();
	if (empty($PNGsCALUnitLookup)) {
		$PNGsCALUnitLookup[0] = 'meter';
		$PNGsCALUnitLookup[1] = 'radian';
	}
	return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid');
}

function IHDRcalculateBitsPerSample($color_type, $bit_depth) {
	switch ($color_type) {
		case 0: // Each pixel is a grayscale sample.
			return $bit_depth;
			break;

		case 2: // Each pixel is an R,G,B triple
			return 3 * $bit_depth;
			break;

		case 3: // Each pixel is a palette index; a PLTE chunk must appear.
			return $bit_depth;
			break;

		case 4: // Each pixel is a grayscale sample, followed by an alpha sample.
			return 2 * $bit_depth;
			break;

		case 6: // Each pixel is an R,G,B triple, followed by an alpha sample.
			return 4 * $bit_depth;
			break;
	}
	return false;
}

?>