Search code examples
phppdfpdf-generationimagick

Why is the quality on generated PDFs so low with this code?


I have the following code. It's used to combine various image attachments (and pdfs) into one PDF. For some reason, when I take even a single PDF and put it through the code, the end result comes out looking very bad compared to the original PDF. In addition, I can select text in the source PDF, but in the generated one I cannot.

Any help would be greatly appreciated.

// PDF object
$pdf = new Imagick();
$max_resolution = array('x' => 100, 'y' => 100);

foreach($attachment_ids as $attachment_id) {
    $attachment = DAO_Attachment::get($attachment_id);
    $file = Storage_Attachments::get($attachment);
    // Temporarily store our attachment
    $im = new Imagick();
    $im->readImageBlob($file);
    // We need to reset the iterator otherwise only one page will be rotated
    $im->resetIterator();

    // Get the resolution
    $resolution = $im->getImageResolution();
    if($resolution['x'] > $max_resolution['x']) {
        $max_resolution['x'] = $resolution['x'];
    }
    if($resolution['y'] > $max_resolution['y']) {
        $max_resolution['y'] = $resolution['y'];
    }

    $num_pages = $im->getNumberImages();

    $rotation = array_shift($rotations);
    $degrees = $rotation > 0 ? 360 - $rotation : 0;
    $pages = array();

    if($degrees > 0) {
        // Rotate each page
        for($i = 1; $i <= $num_pages; $i++) {
            $im->nextImage();
            $im->rotateImage(new ImagickPixel(), $degrees);
        }
    }

    // We need to reset the iterator again so all of our pages will be added to the pdf
    $im->resetIterator();

    // If the image format isn't a pdf, convert it to a png
    if($im->getImageFormat !== 'pdf') {
        $im->setImageFormat('png');
        // Opacity
        if(method_exists($im, 'setImageOpacity'))
            $im->setImageOpacity(1.0);
    }

    $im->setImageCompression(imagick::COMPRESSION_LOSSLESSJPEG); 
    $im->setImageCompressionQuality(100);
    $im->stripImage();

    // Add the rotated attachment to the PDF
    $pdf->addImage($im);

    // Free
    $im->destroy();
}

// Create a composite
$pdf->setImageFormat('pdf');

// Compress output
$pdf->setImageCompression(imagick::COMPRESSION_LOSSLESSJPEG); 
$pdf->setImageCompressionQuality(100);
$pdf->stripImage();

// Set resolution
$pdf->setImageResolution($max_resolution['x'], $max_resolution['y']);

Solution

  • It turns out the answer to this is to set the DPI using setResolution(). We do this before using readImageBlob() to get read the file containing our image, as it will change the DPI of the image based on the current resolution (so setting it afterwards won't work).

    You could also use some math and use resampleImage() to do it after the fact, but setResolution() seems to be working perfectly for us.