How can I read PNG Metadata from PHP?

2019-01-05 03:31发布

This is what I have so far:

<?php

$file = "18201010338AM16390621000846.png";

$test = file_get_contents($file, FILE_BINARY);

echo str_replace("\n","<br>",$test);

?>

The output is sorta what I want, but I really only need lines 3-7 (inclusively). This is what the output looks like now: http://silentnoobs.com/pbss/collector/test.php. I am trying to get the data from "PunkBuster Screenshot (±) AAO Bridge Crossing" to "Resulting: w=394 X h=196 sample=2". I think it'd be fairly straight forward to read through the file, and store each line in an array, line[0] would need to be "PunkBuster Screenshot (±) AAO Bridge Crossing", and so on. All those lines are subject to change, so I can't just search for something finite.

I've tried for a few days now, and it doesn't help much that I'm poor at php.

标签: php png metadata
3条回答
小情绪 Triste *
3楼-- · 2019-01-05 04:02
<?php
  $fp = fopen('18201010338AM16390621000846.png', 'rb');
  $sig = fread($fp, 8);
  if ($sig != "\x89PNG\x0d\x0a\x1a\x0a")
  {
    print "Not a PNG image";
    fclose($fp);
    die();
  }

  while (!feof($fp))
  {
    $data = unpack('Nlength/a4type', fread($fp, 8));
    if ($data['type'] == 'IEND') break;

    if ($data['type'] == 'tEXt')
    {
       list($key, $val) = explode("\0", fread($fp, $data['length']));
       echo "<h1>$key</h1>";
       echo nl2br($val);

       fseek($fp, 4, SEEK_CUR);
    }
    else
    {
       fseek($fp, $data['length'] + 4, SEEK_CUR);
    }
  }


  fclose($fp);
?>

It assumes a basically well formed PNG file.

查看更多
SAY GOODBYE
4楼-- · 2019-01-05 04:05

The PNG file format defines that a PNG document is split up into multiple chunks of data. You must therefore navigate your way to the chunk you desire.

The data you want to extract seem to be defined in a tEXt chunk. I've written the following class to allow you to extract chunks from PNG files.

class PNG_Reader
{
    private $_chunks;
    private $_fp;

    function __construct($file) {
        if (!file_exists($file)) {
            throw new Exception('File does not exist');
        }

        $this->_chunks = array ();

        // Open the file
        $this->_fp = fopen($file, 'r');

        if (!$this->_fp)
            throw new Exception('Unable to open file');

        // Read the magic bytes and verify
        $header = fread($this->_fp, 8);

        if ($header != "\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 = fread($this->_fp, 8);

        while ($chunkHeader) {
            // Extract length and type from binary data
            $chunk = @unpack('Nsize/a4type', $chunkHeader);

            // Store position into internal array
            if ($this->_chunks[$chunk['type']] === null)
                $this->_chunks[$chunk['type']] = array ();
            $this->_chunks[$chunk['type']][] = array (
                'offset' => ftell($this->_fp),
                'size' => $chunk['size']
            );

            // Skip to next chunk (over body and CRC)
            fseek($this->_fp, $chunk['size'] + 4, SEEK_CUR);

            // Read next chunk header
            $chunkHeader = fread($this->_fp, 8);
        }
    }

    function __destruct() { fclose($this->_fp); }

    // Returns all chunks of said type
    public function get_chunks($type) {
        if ($this->_chunks[$type] === null)
            return null;

        $chunks = array ();

        foreach ($this->_chunks[$type] as $chunk) {
            if ($chunk['size'] > 0) {
                fseek($this->_fp, $chunk['offset'], SEEK_SET);
                $chunks[] = fread($this->_fp, $chunk['size']);
            } else {
                $chunks[] = '';
            }
        }

        return $chunks;
    }
}

You may use it as such to extract your desired tEXt chunk as such:

$file = '18201010338AM16390621000846.png';
$png = new PNG_Reader($file);

$rawTextData = $png->get_chunks('tEXt');

$metadata = array();

foreach($rawTextData as $data) {
   $sections = explode("\0", $data);

   if($sections > 1) {
       $key = array_shift($sections);
       $metadata[$key] = implode("\0", $sections);
   } else {
       $metadata[] = $data;
   }
}
查看更多
登录 后发表回答