The TYPO3 ViewHelper are a powerful tool. The Fluid-ViewHelper f:image brings numerous functionalities to output images. But unfortunately it does not know any GIFBUILDER effects. We can change that.

GIFBUILDERS

One of the functionalities TYPO3 was able to score with very early on was the possibility of image manipulation. Fortunately, you don't have to render graphical menus anymore; but every now and then watermarks should be rendered on images or a certain effect should be applied.

The GIFBUILDER never disappeared from the TYPO3 feature set despite its antiquated name and is still part of TypoScript. Contrary to what one might expect from the name, it can also handle all kinds of images and works with JPG and PNG files.

Fluid-ViewHelper f:image

The ViewHelper f:image supplied with Fluid is a standard tool in the developer toolbox. It can render images in the appropriate size, supports crop variants and much more. But he can not handle the GIFBUILDER. We want to change that.

An own Image-ViewHelper

First we create a custom ViewHelper in our sitepackage (or the extension we want to use). Since we do not want to reinvent the wheel, we inherit from the standard class and only make changes where necessary.

 

<?php
namespace MarcWillmann\Sitepackage\ViewHelpers;

use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Imaging\GifBuilder;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Extbase\Exception;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;

class ImageViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper
{


    /**
     * Initialize arguments.
     */
    public function initializeArguments()
    {
        parent::initializeArguments();
        $this->registerArgument('gifBuilderEffect', 'string', 'e.g. blur=20 | gamma=1.3', false, '');

    }
}

 

Hereby we register an additional argument, which can be used to pass the desired effect in the template later.

The method render is almost 1:1 taken from the original file

 

public function render()
    {
        if (($this->arguments['src'] === null && $this->arguments['image'] === null) || ($this->arguments['src'] !== null && $this->arguments['image'] !== null)) {
            throw new Exception('You must either specify a string src or a File object.', 1382284106);
        }

        try {
            $gifBuilderEffect = $this->arguments['gifBuilderEffect'];

            $image = $this->imageService->getImage($this->arguments['src'], $this->arguments['image'], $this->arguments['treatIdAsReference']);
            $cropString = $this->arguments['crop'];
            if ($cropString === null && $image->hasProperty('crop') && $image->getProperty('crop')) {
                $cropString = $image->getProperty('crop');
            }
            $cropVariantCollection = CropVariantCollection::create((string)$cropString);
            $cropVariant = $this->arguments['cropVariant'] ?: 'default';
            $cropArea = $cropVariantCollection->getCropArea($cropVariant);
            $processingInstructions = [
                'width' => $this->arguments['width'],
                'height' => $this->arguments['height'],
                'minWidth' => $this->arguments['minWidth'],
                'minHeight' => $this->arguments['minHeight'],
                'maxWidth' => $this->arguments['maxWidth'],
                'maxHeight' => $this->arguments['maxHeight'],
                'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
            ];
            $processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);

            if ($gifBuilderEffect) {
                $conf = [
                    1 => 'IMAGE',
                    '1.' => [
                        'file' => $processedImage->getForLocalProcessing(false)
                    ],
                    20 => 'EFFECT',
                    '20.' => [
                        'value' => $gifBuilderEffect
                    ]
                ];
                $conf['XY'] = '[1.w],[1.h]';
                /** @var GifBuilder $gifCreator */
                $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
                $gifCreator->init();
                $gifCreator->start($conf, []);
                $imageUri = $gifCreator->gifBuild();
            }
            else {
                $imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
            }

            if (!$this->tag->hasAttribute('data-focus-area')) {
                $focusArea = $cropVariantCollection->getFocusArea($cropVariant);
                if (!$focusArea->isEmpty()) {
                    $this->tag->addAttribute('data-focus-area', $focusArea->makeAbsoluteBasedOnFile($image));
                }
            }
            $this->tag->addAttribute('src', $imageUri);
            $this->tag->addAttribute('width', $processedImage->getProperty('width'));
            $this->tag->addAttribute('height', $processedImage->getProperty('height'));

            $alt = $image->getProperty('alternative');
            $title = $image->getProperty('title');

            // The alt-attribute is mandatory to have valid html-code, therefore add it even if it is empty
            if (empty($this->arguments['alt'])) {
                $this->tag->addAttribute('alt', $alt);
            }
            if (empty($this->arguments['title']) && $title) {
                $this->tag->addAttribute('title', $title);
            }
        } catch (ResourceDoesNotExistException $e) {
            // thrown if file does not exist
            throw new Exception($e->getMessage(), 1509741911, $e);
        } catch (\UnexpectedValueException $e) {
            // thrown if a file has been replaced with a folder
            throw new Exception($e->getMessage(), 1509741912, $e);
        } catch (\RuntimeException $e) {
            // RuntimeException thrown if a file is outside of a storage
            throw new Exception($e->getMessage(), 1509741913, $e);
        } catch (\InvalidArgumentException $e) {
            // thrown if file storage does not exist
            throw new Exception($e->getMessage(), 1509741914, $e);
        }

        return $this->tag->render();
    }

 

The interesting part is this one, the better overview taken out of the snippet above:

 

[...]
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);

if ($gifBuilderEffect) {
    $conf = [
        1 => 'IMAGE',
        '1.' => [
            'file' => $processedImage->getForLocalProcessing(false)
        ],
        20 => 'EFFECT',
        '20.' => [
            'value' => $gifBuilderEffect
        ]
    ];
    $conf['XY'] = '[1.w],[1.h]';
    /** @var GifBuilder $gifCreator */
    $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
    $gifCreator->init();
    $gifCreator->start($conf, []);
    $imageUri = $gifCreator->gifBuild();
}
else {
    $imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
}

 

In case a GIFBUILDER statement is passed to ViewHelper with the newly introduced parameter, we apply it after the normal image manipulations are finished.

Using the new ViewHelper

We can now use this Image-ViewHelper as usual in our template.

 

{namespace mw=MarcWillmann\Sitepackage\ViewHelpers}

[...]
<mw:image 
  image="{file}" 
  alt="{file.alternative}" 
  title="{file.title}"
  cropVariant="default" 
  width="1200"
  gifBuilderEffect="blur=50 | gray" 
/>

 

That gives us everything we need. The result can look like this, for example:

With the f:uri.image-ViewHelper this is of course possible in the same way. Instead of (or in addition to) the GIFBUILDER effects, you could also use the GIFBUILDER text object to render a text onto the image or similar.

Documentation

An overview of all available effects can be found in the documentation (TSRef)https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Gifbuilder/ObjectNames/Index.html#effect

 

Comments (0)

No comments found!

Write new comment