cObj->checkIf($conf['if.'])) { return ''; } $theValue = $this->cImage($conf['file'] ?? '', is_array($conf) ? $conf : []); if (isset($conf['stdWrap.'])) { $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']); } return $theValue; } /** * Returns a tag with the image file defined by $file and processed according to the properties in the TypoScript array. * Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript. * * @param string|File|FileReference|null $file File TypoScript resource * @param array $conf TypoScript configuration properties * @return string HTML tag, (possibly wrapped in links and other HTML) if any image found. */ protected function cImage($file, array $conf): string { $imageResource = $this->cObj->getImgResource($file, $conf['file.'] ?? []); if ($imageResource === null) { return ''; } // $info['originalFile'] will be set, when the file is processed by FAL. // In that case the URL is final and we must not add a prefix if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) { $absRefPrefix = GeneralUtility::makeInstance(FrontendUrlPrefix::class)->getUrlPrefix($this->request); $source = $absRefPrefix . str_replace('%2F', '/', rawurlencode($imageResource->getPublicUrl())); } else { $source = $imageResource->getPublicUrl(); } // A file whose physical resource is gone (sys_file.missing=1) resolves to // an image resource without public URL. Render nothing in this case, just // like for an image resource that could not be resolved at all. if ($source === null) { $identifier = $imageResource->getOriginalFile()?->getIdentifier() ?: $imageResource->getFullPath(); $this->logger->warning('The image "{file}" has no public URL, the file is probably missing, and won\'t be included in frontend output', [ 'file' => $identifier, ]); $this->timeTracker->setTSlogMessage( 'The image "' . $identifier . '" has no public URL, the file is probably missing. It is not rendered.', LogLevel::WARNING ); return ''; } GeneralUtility::makeInstance(AssetCollector::class)->addMedia( $source, $imageResource->getLegacyImageResourceInformation() ); $layoutKey = (string)$this->cObj->stdWrapValue('layoutKey', $conf); $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf); $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file); $altParam = $this->getAltParam($conf); $params = $this->cObj->stdWrapValue('params', $conf); if ($params !== '' && $params[0] !== ' ') { $params = ' ' . $params; } $imageTagValues = [ 'width' => $imageResource->getWidth(), 'height' => $imageResource->getHeight(), 'src' => htmlspecialchars($source), 'params' => $params, 'altParams' => $altParam, 'sourceCollection' => $sourceCollection, 'selfClosingTagSlash' => DocType::createFromRequest($this->request)->isXmlCompliant() ? ' /' : '', ]; $theValue = $this->markerTemplateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true); $linkWrap = (string)$this->cObj->stdWrapValue('linkWrap', $conf); if ($linkWrap !== '') { $theValue = $this->linkWrap($theValue, $linkWrap); } elseif ($conf['imageLinkWrap'] ?? false) { $originalFile = urldecode($imageResource->getFullPath()); $theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']); } $wrap = $this->cObj->stdWrapValue('wrap', $conf); if ((string)$wrap !== '') { $theValue = $this->cObj->wrap($theValue, $conf['wrap']); } return $theValue; } /** * Returns the html-template for rendering the image-Tag if no template is defined via typoscript the * default tag template is returned * * @param string $layoutKey rendering key * @param array $conf TypoScript configuration properties */ protected function getImageTagTemplate($layoutKey, $conf): string { if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) { return $this->cObj->stdWrapValue('element', $conf['layout.'][$layoutKey . '.']); } return ''; } /** * Render alternate sources for the image tag. If no source collection is given an empty string is returned. * * @param string $layoutKey rendering key * @param array $conf TypoScript configuration properties * @param string|File|FileReference|null $file * @return string */ protected function getImageSourceCollection(string $layoutKey, array $conf, $file) { $sourceCollection = ''; if ($layoutKey && isset($conf['sourceCollection.']) && $conf['sourceCollection.'] && ( isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source'] || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.'] ) ) { // find active sourceCollection $activeSourceCollections = []; foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) { if (str_ends_with($sourceCollectionKey, '.')) { if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) { $activeSourceCollections[] = $sourceCollectionConfiguration; } } } // apply option split to configurations $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections)); $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); $isXmlCompliant = DocType::createFromRequest($this->request)->isXmlCompliant(); // render sources foreach ($activeSourceCollections as $key => $sourceConfiguration) { $sourceLayout = $this->cObj->stdWrapValue('source', $srcLayoutOptionSplitted[$key] ?? []); $sourceRenderConfiguration = [ 'file' => $file, 'file.' => $conf['file.'] ?? null, ]; $imageQuality = $this->cObj->stdWrapValue('quality', $sourceConfiguration ?? []); if ($imageQuality) { $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality; } $pixelDensity = (int)$this->cObj->stdWrapValue('pixelDensity', $sourceConfiguration, 1); $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY']; foreach ($dimensionKeys as $dimensionKey) { $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $sourceConfiguration); if ($dimension === '') { $dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $conf['file.'] ?? []); } if ($dimension !== '') { if (str_contains($dimension, 'c') && ($dimensionKey === 'width' || $dimensionKey === 'height')) { $dimensionParts = explode('c', $dimension, 2); $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c'; if ($dimensionParts[1]) { $dimension .= $dimensionParts[1]; } } elseif ($dimensionKey === 'XY') { $dimensionParts = GeneralUtility::intExplode(',', $dimension); $dimension = $dimensionParts[0] * $pixelDensity; if ($dimensionParts[1]) { $dimension .= ',' . $dimensionParts[1] * $pixelDensity; } } else { $dimension = (int)$dimension * $pixelDensity; } $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension; // Remove the stdWrap properties for dimension as they have been processed already above. unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']); } } $imageResource = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']); if ($imageResource !== null) { $sourceConfiguration['width'] = $imageResource->getWidth(); $sourceConfiguration['height'] = $imageResource->getHeight(); $urlPrefix = ''; // Prepend 'absRefPrefix' to file path only if file was not processed by FAL, e.g. GIFBUILDER if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) { $urlPrefix = GeneralUtility::makeInstance(FrontendUrlPrefix::class)->getUrlPrefix($this->request); } $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $imageResource->getPublicUrl()); $sourceConfiguration['selfClosingTagSlash'] = $isXmlCompliant ? ' /' : ''; $oneSourceCollection = $this->markerTemplateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true); $sourceCollection .= $eventDispatcher->dispatch( new ModifyImageSourceCollectionEvent($oneSourceCollection, $sourceCollection, (array)$sourceConfiguration, $sourceRenderConfiguration, $this->cObj) )->getSourceCollection(); } } } return $sourceCollection; } /** * Wraps the input string by the $wrap value and implements the "linkWrap" data type as well. * * The "linkWrap" data type means that this function will find any integer encapsulated * in {} (curly braces) in the first wrap part and substitute it with the corresponding page * uid from the rootline where the found integer is pointing to the key in the rootline. * * @param string $content Input string * @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string */ protected function linkWrap(string $content, string $wrap): string { $wrapArr = explode('|', $wrap); if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) { $localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine(); $uid = $localRootLine[$reg[1]]['uid'] ?? null; if ($uid) { $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]); } } return trim($wrapArr[0]) . $content . trim($wrapArr[1] ?? ''); } /** * An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element. * From the $conf array it implements the properties "altText" and "titleText" * * @param array $conf TypoScript configuration properties * @return string Parameter string containing alt and title parameters (if any) */ protected function getAltParam(array $conf): string { $altText = trim((string)$this->cObj->stdWrapValue('altText', $conf)); $titleText = trim((string)$this->cObj->stdWrapValue('titleText', $conf)); // "alt": $altParam = ' alt="' . htmlspecialchars($altText) . '"'; // "title": $emptyTitleHandling = $this->cObj->stdWrapValue('emptyTitleHandling', $conf); // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr' if ($titleText || $emptyTitleHandling === 'keepEmpty') { $altParam .= ' title="' . htmlspecialchars($titleText) . '"'; } elseif ($emptyTitleHandling === 'useAlt') { $altParam .= ' title="' . htmlspecialchars($altText) . '"'; } return $altParam; } }