_contents = $contents; $png_signature = pack("C8", 137, 80, 78, 71, 13, 10, 26, 10); // Read 8 bytes of PNG header and verify. $header = substr($this->_contents, 0, 8); if ($header != $png_signature) { echo 'This is not a valid PNG image'; } $this->_size = strlen($this->_contents); $this->_chunks = []; // Skip 8 bytes of IHDR image header. $position = 8; do { $chunk = @unpack('Nsize/a4type', substr($this->_contents, $position, 8)); $this->_chunks[$chunk['type']][] = substr($this->_contents, $position + 8, $chunk['size']); // Skip 12 bytes chunk overhead. $position += $chunk['size'] + 12; } while ($position < $this->_size); } /** * Checks if a key already exists in the chunk of said type. * We need to avoid writing same keyword into file chunks. * * @param string $type chunk type, like iTXt, tEXt, etc * @param string $check keyword that needs to be checked * * @return bool (true|false) True if file is safe to write this keyword, false otherwise */ public function checkChunks($type, $check) { if (array_key_exists($type, $this->_chunks)) { foreach (array_keys($this->_chunks[$type]) as $typekey) { list($key, $data) = explode("\0", $this->_chunks[$type][$typekey]); if (strcmp($key, $check) == 0) { echo 'Key "'.$check.'" already exists in "'.$type.'" chunk.'; return false; } } } return true; } /** * Add a chunk by type with given key and text. * * @param string $chunkType chunk type, like iTXt, tEXt, etc * @param string $key keyword that needs to be added * @param string $value currently an assertion URL that is added to an image metadata * * @return string $result file content with a new chunk as a string */ public function addChunk($chunkType, $key, $value) { $chunkData = $key."\0".$value; $crc = pack("N", crc32($chunkType.$chunkData)); $len = pack("N", strlen($chunkData)); $newChunk = $len.$chunkType.$chunkData.$crc; $result = substr($this->_contents, 0, $this->_size - 12) .$newChunk .substr($this->_contents, $this->_size - 12, 12); return $result; } /** * removes a chunk by type with given key and text. * * @param string $chunkType chunk type, like iTXt, tEXt, etc * @param string $key keyword that needs to be deleted * @param string $png the png image * * @return string $result new File content */ public function removeChunks($chunkType, $key, $png) { // Read the magic bytes and verify $retval = substr($png, 0, 8); $ipos = 8; if ($retval != "\x89PNG\x0d\x0a\x1a\x0a") { throw new Exception('Is not a valid PNG image'); } // Loop through the chunks. Byte 0-3 is length, Byte 4-7 is type $chunkHeader = substr($png, $ipos, 8); $ipos = $ipos + 8; while ($chunkHeader) { // Extract length and type from binary data $chunk = @unpack('Nsize/a4type', $chunkHeader); $skip = false; if ($chunk['type'] == $chunkType) { $data = substr($png, $ipos, $chunk['size']); $sections = explode("\0", $data); print_r($sections); if ($sections[0] == $key) { $skip = true; } } // Extract the data and the CRC $data = substr($png, $ipos, $chunk['size'] + 4); $ipos = $ipos + $chunk['size'] + 4; // Add in the header, data, and CRC if (!$skip) { $retval = $retval.$chunkHeader.$data; } // Read next chunk header $chunkHeader = substr($png, $ipos, 8); $ipos = $ipos + 8; } return $retval; } /** * Extracts the baked PNG info by the Key. * * @param string $png the png image * @param string $key keyword that needs to be searched * * @return mixed - If there is an error - boolean false is returned * If there is PNG information that matches the key an array is returned */ public function extractBadgeInfo($png, $key = 'openbadges') { // Read the magic bytes and verify $retval = substr($png, 0, 8); $ipos = 8; if ($retval != "\x89PNG\x0d\x0a\x1a\x0a") { return false; } // Loop through the chunks. Byte 0-3 is length, Byte 4-7 is type $chunkHeader = substr($png, $ipos, 8); $ipos = $ipos + 8; while ($chunkHeader) { // Extract length and type from binary data $chunk = @unpack('Nsize/a4type', $chunkHeader); $skip = false; if ($chunk['type'] == 'tEXt') { $data = substr($png, $ipos, $chunk['size']); $sections = explode("\0", $data); if ($sections[0] == $key) { return $sections; } } // Extract the data and the CRC $data = substr($png, $ipos, $chunk['size'] + 4); $ipos = $ipos + $chunk['size'] + 4; // Read next chunk header $chunkHeader = substr($png, $ipos, 8); $ipos = $ipos + 8; } } }