Commit d05ea9f7 authored by Benni Mack's avatar Benni Mack Committed by Anja Leichsenring
Browse files

[FEATURE] Define target file extension in Image-related ViewHelpers

When rendering custom formats with the <source> tag (as an example),
it currently is not possible to specificy a target file extension (e.g. webp
as alternative).

A new argument "fileExtension" for the <f:image>, <f:media> and
<f:uri.media> is introduced to specify a custom output format for having
variants on image variants.

Resolves: #90416
Releases: master
Change-Id: I5fe2a60b968578e504cda851d2d5dd0f0eefe7e0
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63289


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
parent 66ac21e6
.. include:: ../../Includes.txt
=============================================================================
Feature: #90416 - Specific target file extension in Image-related ViewHelpers
=============================================================================
See :issue:`90416`
Description
===========
TYPO3 Core's shipped Fluid ViewHelpers now allow to optionally
specify a target file extension via the new attribute `fileExtension`.
This affects the following ViewHelpers:
- :html:`<f:image>`
- :html:`<f:media>`
- :html:`<f:uri.image>`
This is rather important for specific scenarios where a :html:`<picture>` tag with multiple images are requested, allowing
to e.g. customize rendering for `webp` support, if the servers' ImageMagick version supports `webp` conversion.
In other regard, this might become useful to specify the output
for preview images of `pdf` files which can be converted via `GhostScript` if installed.
Impact
======
TYPO3 Integrators can now use the additional attribute
in their custom Fluid Templates for specific use cases.
Example:
.. code-block:: html
<picture>
<source srcset="{f:uri.image(image: fileObject, treatIdAsReference: true, fileExtension: 'webp')}" type="image/webp">
<source srcset="{f:uri.image(image: fileObject, treatIdAsReference: true, fileExtension: 'jpg')}" type="image/jpeg">
<f:image image="{fileObject}" treatIdAsReference="true" alt="{fileObject.alternative}">
<picture>
.. index:: Fluid, ext:fluid
\ No newline at end of file
......@@ -120,6 +120,7 @@ class ImageViewHelper extends AbstractTagBasedViewHelper
$this->registerArgument('image', 'object', 'a FAL object');
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
$this->registerArgument('fileExtension', 'string', 'Custom file extension to use');
$this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
......@@ -162,6 +163,9 @@ class ImageViewHelper extends AbstractTagBasedViewHelper
'maxHeight' => $this->arguments['maxHeight'],
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
if (!empty($this->arguments['fileExtension'] ?? '')) {
$processingInstructions['fileExtension'] = $this->arguments['fileExtension'];
}
$processedImage = $this->imageService->applyProcessingInstructions($image, $processingInstructions);
$imageUri = $this->imageService->getImageUri($processedImage, $this->arguments['absolute']);
......
......@@ -85,6 +85,7 @@ class MediaViewHelper extends AbstractTagBasedViewHelper
$this->registerArgument('width', 'string', 'This can be a numeric value representing the fixed width of in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'This can be a numeric value representing the fixed height in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
$this->registerArgument('fileExtension', 'string', 'Custom file extension to use for images');
}
/**
......@@ -114,7 +115,7 @@ class MediaViewHelper extends AbstractTagBasedViewHelper
// Fallback to image when no renderer is found
if ($fileRenderer === null) {
return $this->renderImage($file, $width, $height);
return $this->renderImage($file, $width, $height, $this->arguments['fileExtension'] ?? null);
}
$additionalConfig = array_merge_recursive($this->arguments, $additionalConfig);
return $fileRenderer->render($file, $width, $height, $additionalConfig);
......@@ -126,9 +127,10 @@ class MediaViewHelper extends AbstractTagBasedViewHelper
* @param FileInterface $image
* @param string $width
* @param string $height
* @param string|null $fileExtension
* @return string Rendered img tag
*/
protected function renderImage(FileInterface $image, $width, $height)
protected function renderImage(FileInterface $image, $width, $height, ?string $fileExtension)
{
$cropVariant = $this->arguments['cropVariant'] ?: 'default';
$cropString = $image instanceof FileReference ? $image->getProperty('crop') : '';
......@@ -139,6 +141,9 @@ class MediaViewHelper extends AbstractTagBasedViewHelper
'height' => $height,
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
if (!empty($fileExtension)) {
$processingInstructions['fileExtension'] = $fileExtension;
}
$imageService = $this->getImageService();
$processedImage = $imageService->applyProcessingInstructions($image, $processingInstructions);
$imageUri = $imageService->getImageUri($processedImage);
......
......@@ -94,6 +94,7 @@ class ImageViewHelper extends AbstractViewHelper
$this->registerArgument('image', 'object', 'image');
$this->registerArgument('crop', 'string|bool', 'overrule cropping of image (setting to FALSE disables the cropping set in FileReference)');
$this->registerArgument('cropVariant', 'string', 'select a cropping variant, in case multiple croppings have been specified or stored in FileReference', false, 'default');
$this->registerArgument('fileExtension', 'string', 'Custom file extension to use');
$this->registerArgument('width', 'string', 'width of the image. This can be a numeric value representing the fixed width of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
$this->registerArgument('height', 'string', 'height of the image. This can be a numeric value representing the fixed height of the image in pixels. But you can also perform simple calculations by adding "m" or "c" to the value. See imgResource.width for possible options.');
......@@ -145,6 +146,9 @@ class ImageViewHelper extends AbstractViewHelper
'maxHeight' => $arguments['maxHeight'],
'crop' => $cropArea->isEmpty() ? null : $cropArea->makeAbsoluteBasedOnFile($image),
];
if (!empty($arguments['fileExtension'])) {
$processingInstructions['fileExtension'] = $arguments['fileExtension'];
}
$processedImage = $imageService->applyProcessingInstructions($image, $processingInstructions);
return $imageService->getImageUri($processedImage, $absolute);
......
......@@ -116,6 +116,26 @@ class ImageViewHelperTest extends ViewHelperBaseTestcase
'title' => 'title'
]
],
[
[
'src' => 'test',
'width' => 100,
'height' => 200,
'minWidth' => 300,
'maxWidth' => 400,
'minHeight' => 500,
'maxHeight' => 600,
'crop' => null,
'fileExtension' => 'jpg'
],
[
'src' => 'test.jpg',
'width' => '100',
'height' => '200',
'alt' => 'alternative',
'title' => 'title'
]
],
];
}
......@@ -157,7 +177,7 @@ class ImageViewHelperTest extends ViewHelperBaseTestcase
$imageService = $this->createMock(ImageService::class);
$imageService->expects(self::once())->method('getImage')->willReturn($image);
$imageService->expects(self::once())->method('applyProcessingInstructions')->with($image, self::anything())->willReturn($processedFile);
$imageService->expects(self::once())->method('getImageUri')->with($processedFile)->willReturn('test.png');
$imageService->expects(self::once())->method('getImageUri')->with($processedFile)->willReturn($expected['src']);
$this->viewHelper->injectImageService($imageService);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment