phppngquant

pngquant with PHP shell_exec output to variable


I'm trying to use pngquant with PHP using the following code (source):

<?php 


function compress_png($path_to_png_file, $max_quality = 90)
{
    if (!file_exists($path_to_png_file)) {
        throw new Exception("File does not exist: $path_to_png_file");
    }

    // guarantee that quality won't be worse than that.
    $min_quality = 60;

    // '-' makes it use stdout, required to save to $compressed_png_content variable
    // '<' makes it read from the given file path
    // escapeshellarg() makes this safe to use with any path

    // maybe with more memory ?
    ini_set("memory_limit", "128M");

    // The command should look like: pngquant --quality=60-90 - < "image-original.png"
    $comm = "pngquant --quality=$min_quality-$max_quality - < ".escapeshellarg(    $path_to_png_file);

    $compressed_png_content = shell_exec($comm);

    var_dump($compressed_png_content);

    if (!$compressed_png_content) {
        throw new Exception("Conversion to compressed PNG failed. Is pngquant 1.8+ installed on the server?");
    }

    return $compressed_png_content;
}

echo compress_png("image-original.png");

The function is supposed to retrieve the output of the shell_exec function. With the output i should be able to create a new png file, however the output of the shell_exec in the browser is corrupt: �PNG.

Note: the execution of the command is succesfully executed in the console without PHP (pngquant --quality=60-90 - < "image-original.png")

If I execute the php code from the console, i get the following message:

error: failed writing image to stdout (16)

I've searched everywhere without any solution, can someone help me or have any idea of what could be causing the problem ?


Solution

  • The php-pngquant wrapper allow you to retrieve the content from the generated image by PNGQuant directly into a variable using the getRawOutput method:

    <?php 
    
    use ourcodeworld\PNGQuant\PNGQuant;
    
    $instance = new PNGQuant();
    
    $result = $instance
        ->setImage("/image/to-compress.png")
        ->setQuality(50,80)
        ->getRawOutput();
    
    // Result is an array with the following structure
    // $result = array(
    //    'statusCode' => 0,
    //    'tempFile' => "/tmp/example-temporal.png",
    //    'imageData' => [String] (use the imagecreatefromstring function to get the binary data)
    //)
    
    // Get the binary data of the image
    $imageData = imagecreatefromstring($result["imageData"]);
    
    // Save the PNG Image from the raw data into a file or do whatever you want.
    imagepng($imageData , '/result_image.png');
    

    Under the hood, the wrapper provides as the output argument in PNGQuant a temporary file, then pngquant will write the compressed image into that file and will retrieve its content in the result array. You can still verify the exit code of PNGQuant with the statusCode index of the result array.