Thumb component for document

Hi,

I try to generate PDF thumbnails.

I tried to extend the thumb component for it, but it does not seem to be triggered when files are documents. Can someone confirm or disprove this?

Thanks


I based my experiments on those topics:
Extend core component thumb
Create Preview from PDF-File

Hi!
Thumbs generation is a bit “magic” in Kirby3 and I am trying to explain how I think it works.

Please correct me if my steps below are wrong!

The first part of thumb generation:

  • When using $file->thumb() , you are calling the thumb method of the file class. This method is defined in the FileModifications trait.
  • Inside this method the file::version component is called (here)
  • The file::version component checks if an image with the defined specifications already exists, otherwise prepares the job file for lazy thumbnail creation (they are saved in the media/… folder in a .jobs subfolder for each page).

The second part of thumb creation:

  • Via routing Kirby realizes that you want to load an image from the media folder that has not yet been generated. You end up in the Media class static thumb method. The job file is used to get the specifications.
  • Then the App class thumb method is called
  • which calls the thumb component
  • which finally uses the Darkroom classes or your own thumbnail creation code.

Long story short: Your pdf thumb generation fails in the second step of part 1. file::version checks if a file is resizable and a pdf is not.
You’ll need to adjust the file::version component aswell or use another solution, e.g. an after upload hook.

Thank you for this!

I keep digging and I’ll post some updates if reach something worthy enough.

That’s how I create thumbs for my pdf files:

'file.create:after' => function (Kirby\Cms\File $file) {
    if ($file->template() == "flyer") {
        $im = new Imagick();
        $im->setResolution(72,72);
        $im->readimage($file->root() . '[0]');
        $im->setImageFormat('jpeg');
        $im->writeImage(dirname($file->root()) . '/' . $file->name() . '.jpg'); // temp
        $im->clear();
        $im->destroy();

        Kirby\Cms\File::create([
            'source'     => dirname($file->root()) . '/' . $file->name() . '.jpg',
            'parent'     => $file->page(),
            'filename'   => $file->name() . '-preview.jpg',
            'template'   => 'preview'
        ]);

        Kirby\Toolkit\F::remove(dirname($file->root()) . '/' . $file->name() . '.jpg'); // delete temp
    }
},

And I realize that I am not checking if it actually is a pdf. So I might should do that before creating the thumb…

I went for a field methode for now :slight_smile:

<?php

use Kirby\Cms\File;
use Kirby\Cms\Filename;
use Kirby\Cms\FileVersion;

Kirby::plugin('julien-gargot/pdf-preview', [
  'fileMethods' => [
    'preview' => function ($page = 0) {

      $extension = 'jpg';
      $mediaRoot = dirname($this->mediaRoot());
      $src       = $this->root() . '.' . $extension;
      $dst       = $mediaRoot . '/{{ name }}.{{ extension }}';
      $thumbRoot = (new Filename($src, $dst))->toString();
      $thumbName = basename($thumbRoot);

      if (!is_dir($mediaRoot)) mkdir( $mediaRoot );

      if (f::exists($thumbRoot))
      {

        if ( (f::modified($thumbRoot) > $this->modified())) {

          return new FileVersion([
              'original' => $this,
              'root'     => $thumbRoot,
              'url'      => dirname($this->mediaUrl()) . '/' . $thumbName,
          ]);

        } else {

          f::remove($thumbRoot);

        }
      }

      $im = new Imagick();
      $im->setResolution(96,96);
      $im->readImage($this->root().'['.$page.']');
      $im->transparentPaintImage("rgb(255, 255, 255)", 0, Imagick::getQuantum() * 0.2, false);
      $im = $im->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
      $im->setImageFormat($extension);
      $im->setImageCompression(Imagick::COMPRESSION_JPEG);
      $im->writeImage($thumbRoot);

      return new FileVersion([
          'original' => $this,
          'root'     => $thumbRoot,
          'url'      => dirname($this->mediaUrl()) . '/' . $thumbName,
      ]);

    }
  ]
]);
1 Like

Is it possible to chain file methods with your approach?
e.g. $file->preview()->resize(200) ?

No. It was the purpose, but I have to continue to work on it.

So, I ended up in a simple field method that saves a preview as siblings of the original file. Like this, it became a regular image that you can chain or use as a preview for the original PDF in templates or panel.
But, as this, it is also listed when calling $page->files() and displayed in the panel page files section if you use templates on other files to be able to filter this list.

I would rather save the preview in the media folder so this file cannot be listed in the $page->files() method… But, if saving the preview in the media folder returns a file to display in front end, I didn’t manage to allow it to be chained like a regular Kirby File. Actually, you can’t even make a thumbnail of it.

Here a plugin anyway: https://github.com/julien-gargot/kirby-plugin-pdf-preview.

7 Likes

Am I right to assume that with your solution, Imagick parses the whole PDF even though it makes the thumb from the first page? That could be quite a problem for larger files (got some documents with 500+ pages).

I’m currently implementing this library to circumnavigate performance issues:

I would expect that Imagick is smart enough to only parse the page I defined in $im->readimage($file->root() . '[0]');

Have you tested with big files and experienced performance issues?

I got the idea from people replying to this gist, but didn’t test it myself - but now I see you already thought about that, nice!

I actually didnt. My files are just not big enough to run into that problem.

Be that as it may, I also think IM would be clever enough to only parse input being passed to it … :wink:

Hello everyone !
I’m also interested for an automatic jpg/png thumbnail creation for my PDF’s… Anyone got a solution please ?
My programming skills are small… and I’m a “rookie” kirby user…
Thanks a lot !

You mean when the file is uploaded? Then you can use the code from the plugin code mentioned above (https://github.com/julien-gargot/kirby-plugin-pdf-preview/blob/master/index.php) with some little changes in a file.create:after hook.