0

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']);
sluther
  • 1,694
  • 20
  • 29
  • Define "looking very bad" - ideally, show some screenshots – Pekka Jun 02 '11 at 19:38
  • Sounds like you're rasterizing the PDF, so it's not longer text-embedded-in-a-page, but rather a picture-of-some-text. if you're rendering the PDF at display resolutions (72-100dpi), it's going to look awful when you print it at even cheap laserprinter resolution (300dpi). – Marc B Jun 02 '11 at 19:50
  • I am not able to show any screenshots, unfortunately. Suffice it to say that the quality is just very poor. We're not going to be printing this, but regardless it sounds like it might be getting rasterized....any ideas how to stop that behavior? – sluther Jun 02 '11 at 20:03
  • 1
    Use public domain PDFs and show us before/after. Surely you can do this. – Michael B Jun 02 '11 at 20:09

3 Answers3

1

This may be obvious to you already but a low quality image will not result in a high quality pdf. I don't know how good Imagick's pdf generation capabilities are, but it seems from your code you are converting images? You could compare by doing the same thing with TcPDF, though if the image is low quality I doubt you will get better results.

Also, if you have access to higher DPI resolution images than the usual web-optimised format, I recommend you use those to build your PDF instead. The quality will be a lot better.

Stephane Gosselin
  • 9,030
  • 5
  • 42
  • 65
  • Images actually look fine going through. It's PDFs that get screwed up. The code specifically doesn't convert PDFs (since they are already PDFs they dont need a conversion) though it applies everything else if needed - rotations, quality, etc. – sluther Jun 02 '11 at 20:04
  • [As noted from their website](http://www.imagemagick.org/Usage/formats/#vector): IM is a 'raster image processor', and while it can read or write images stored in one of the vector formats it does so by converting the image to and from a internal raster image. Consequently if you are trying to convert a image from a vector format, to another vector format, IM will essentially rasterize this image at the currently defined resolution or density which will hopefully (but unlikely) be suitable for the output device you intend to use it on. Any output from IM will never be a true vector format. – Stephane Gosselin Jun 02 '11 at 20:11
  • I recommend you [try out TcPDF to do this](http://www.tcpdf.org/), it is well documented and quite easy to use. – Stephane Gosselin Jun 02 '11 at 20:13
1

ImageMagick uses GhostScript to convert PDFs to various raster image formats. GhostScript is quite good at this, but you're hand-cuffing it by scaling the page down to a max of 100x100.

An 8.5x11 (inches) page at 72 dpi, is 612x792 pixels.

Perhaps you meant to restrict DPI rather than resolution? The output still won't scale all that well (vector formats vs pixel formats), but I suspect it would be a big improvement.

Mark Storer
  • 15,672
  • 3
  • 42
  • 80
  • According to [this post](http://stackoverflow.com/questions/4076936/get-set-dpi-with-php-gd-imagick), getImageResolution/setImageResolution actually changes the DPI. – sluther Jun 06 '11 at 22:15
  • 1
    So it is. Huh. The ImageMagick docs are quite revealing. Someone spells out the differences between setImageResolution(x y) and setResolution( x y ). Great naming scheme there. :[ – Mark Storer Jun 06 '11 at 22:26
  • Turns out it's setResolution() that sets the DPI - not setImageResolution(). Using setResolution(144,144) allows us to bump the quality up. – sluther Jul 18 '11 at 08:40
1

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.

sluther
  • 1,694
  • 20
  • 29