Bernd Wilke πφ
Bernd Wilke πφ

Reputation: 10791

How can I enhance the quality of images generated from PDF?

We use TYPO3 9.5.13, GraphicsMagick 1.3.29, Ghostscript 9.27, BK2K\\BootstrapPackage 11.0.1

Using PDFs as normal images is no problem.
But now I want a 'preview' of the PDFs in full column width (~1000px). And although the PDF has a high resolution, the generated Image has a width of 595px only and any text is nearly unreadable.

The problem occurs with Image-CEs like in the uploads CE, which I want to enhance:
each time I want the image using the full column width it renders in a bad resolution and the image seems distorted.

here a small area from the generated image:
enter image description here

and the same area from the PDF as shown in PDF-reader:
enter image description here

The fluid part:

<img loading="lazy" 
     src="{f:uri.image(image: file, cropVariant: 'default', maxWidth: 1100)}" 
     width="{bk2k:lastImageInfo(property: 'width')}" 
     height="{bk2k:lastImageInfo(property: 'height')}" 
     intrinsicsize="{bk2k:lastImageInfo(property: 'width')}x{bk2k:lastImageInfo(property: 'height')}" 
     title="{file.properties.title}" 
     alt="{file.properties.alternative}">

which results in something like:

<img loading="lazy" 
     src="/fileadmin/_processed_/3/2/csm_Warum_D-Arzt_6afd8ad8d4.png"
     intrinsicsize="595x842" 
     title="" 
     alt="" 
     width="595" 
     height="842">

Edit:

In case of using this FLUID:

<img loading="lazy" 
     src="{f:uri.image(image: file, cropVariant: 'default', width: 1100)}" 
     width="{bk2k:lastImageInfo(property: 'width')}" 
     height="{bk2k:lastImageInfo(property: 'height')}" 
     intrinsicsize="{bk2k:lastImageInfo(property: 'width')}x{bk2k:lastImageInfo(property: 'height')}" 
     title="{file.properties.title}" 
     alt="{file.properties.alternative}">

I get:

<img loading="lazy" 
     src="/fileadmin/_processed_/3/2/csm_Warum_D-Arzt_2ffb63b15f.png" 
     intrinsicsize="1100x1557" 
     title="" 
     alt="" 
     width="1100" 
     height="1557">

the image is bigger (and overflow the container) but the quality is worse the same, notice the bigger pixels:

enter image description here

Upvotes: 1

Views: 1257

Answers (1)

Mikel Wohlschlegel
Mikel Wohlschlegel

Reputation: 1476

Actually, a PDF is NOT an image. It is a container format which can contain vectors and images with different colorspaces and dimensions. A bitmap image has fixed dimensions width, height, density, a PDF not. Originally, it was created and optimized to work for printers, not for screens.

TYPO3 reflects that with a message in the backend: enter image description here

IMHO, there is no perfect way of handling PDFs to behave like images, as you know the output format, but not the input format (properly). Two ways to get acceptable results:

  1. Extend content elements or create new ones and add a second image slot for PDF preview images. Create the preview images yourself with a graphical program.
  2. Write your own viewhelper and create your own thumbnails

Solution 1 will lead to more work for editors. Would be no best practise for me.


I would go with an own viewhelper.

Add your own render type for PDFs:

<f:switch expression="{file.type}">
  <f:case value="5">
    <f:render partial="Media/Type/Pdf" arguments="{file: file, dimensions: dimensions, data: data, settings: settings}" />
  </f:case>
  <f:defaultCase>
    <f:render partial="Media/Type/Image" arguments="{file: file, dimensions: dimensions, data: data, settings: settings}" />
  </f:defaultCase>
</f:switch>

Partial Media/Type/Pdf

{namespace cv=Conversion\HelperUtils\ViewHelpers}
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:ce="http://typo3.org/ns/TYPO3/CMS/FluidStyledContent/ViewHelpers" data-namespace-typo3-fluid="true">
  <cv:forEachPdfThumbnail document="{file}" pages="1" as="pdfPreviewPage">
    <f:image src="{pdfPreviewPage}" alt="" />
  </cv:forEachPdfThumbnail>
</html>

ViewHelper:

This viewhelper convert multiple pages from a PDF, using the CommandUtility::imageMagickCommand. You can raise the density to a higher value to improve quality. As mentioned, this viewhelper was developed a few years ago and could be improved (e.g. saving to fileadmin/processed instead of typo3temp. Feel free to clone and improve: https://github.com/conversion1/t3-pdfthumbnailviewhelper/blob/master/ForEachPdfThumbnailViewHelper.php

public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{

    $templateVariableContainer = $renderingContext->getVariableProvider();

    /** @var \TYPO3\CMS\Core\Resource\FileReference $document */
    $document = $arguments['document'];
    $pages = explode(',', str_replace(' ', '', $arguments['pages']));

    $colorspace = TRUE === isset($GLOBALS['TYPO3_CONF_VARS']['GFX']['colorspace']) ? $GLOBALS['TYPO3_CONF_VARS']['GFX']['colorspace'] : 'RGB';
    $absFilePath = GeneralUtility::getFileAbsFileName($document->getOriginalFile()->getPublicUrl());
    $destinationPath = 'typo3temp/';
    $destinationFilePrefix = 'pdf-prev_' . $document->getOriginalFile()->getNameWithoutExtension();
    $destinationFileExtension = 'png';

    $output = '';

    foreach ($pages as $pageNumber) {

        if($pageNumber > 0) {
            $pageNumber = intval($pageNumber);
        } else {
            $pageNumber = 1;
        }

        $destinationFileSuffix =  '_page-' . $pageNumber;
        $absDestinationFilePath = GeneralUtility::getFileAbsFileName($destinationPath . $destinationFilePrefix . $destinationFileSuffix . '.' . $destinationFileExtension);

        $imgArguments = '-colorspace ' . $colorspace;
        $imgArguments .= ' -density 300';
        $imgArguments .= ' -sharpen 0x.6';
        $imgArguments .= ' "' . $absFilePath . '"';
        $imgArguments .= '['. intval($pageNumber - 1) .']';
        $imgArguments .= ' "' . $absDestinationFilePath . '"';

        if(!file_exists($absDestinationFilePath)) {
            $command = CommandUtility::imageMagickCommand('convert', $imgArguments);
            CommandUtility::exec($command);
        }

        $thumbnail = substr($absDestinationFilePath, strlen(Environment::getPublicPath()));
        $templateVariableContainer->add($arguments['as'], $thumbnail);
        $output .= $renderChildrenClosure();
        $templateVariableContainer->remove($arguments['as']);

    }

    return $output;
}

Edit:

A third way: You can use a JavaScript library to generate thumbnails on the fly. E.g. https://github.com/mozilla/pdf.js

Upvotes: 1

Related Questions