hasComponentAttribute(self::BACKGROUNDS)) {
PluginUtility::getSnippetManager()->attachCssInternalStyleSheet(self::CANONICAL);
$backgrounds = $tagAttributes->getValueAndRemove(self::BACKGROUNDS);
switch (sizeof($backgrounds)) {
case 1:
// Only one background was specified
$background = $backgrounds[0];
if (
/**
* We need to create a background node
* if we transform or
* use a CSS pattern (because it use the text color as one of painting color)
*/
!isset($background[TagAttributes::TRANSFORM]) &&
!isset($background[self::PATTERN_ATTRIBUTE])
) {
/**
* For readability,
* we put the background on the parent node
* because there is only one background
*/
$backgroundImage = $background[self::BACKGROUND_IMAGE] ?? null;
if ($backgroundImage !== null) {
$tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_IMAGE, $backgroundImage);
}
$backgroundColor = $background[self::BACKGROUND_COLOR] ?? null;
if ($backgroundColor !== null) {
$tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_COLOR, $backgroundColor);
}
$opacityAttribute = $background[Opacity::OPACITY_ATTRIBUTE] ?? null;
if ($opacityAttribute !== null) {
$tagAttributes->addComponentAttributeValueIfNotEmpty(Opacity::OPACITY_ATTRIBUTE, $opacityAttribute);
}
$backgroundPosition = $background[self::BACKGROUND_POSITION] ?? null;
if ($backgroundPosition !== null) {
$tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_POSITION, $backgroundPosition);
}
$backgroundFill = $background[self::BACKGROUND_FILL] ?? null;
if ($backgroundFill !== null) {
$tagAttributes->addComponentAttributeValueIfNotEmpty(self::BACKGROUND_FILL, $backgroundFill);
}
} else {
$backgroundTagAttribute = TagAttributes::createFromCallStackArray($background);
$backgroundTagAttribute->addClassName(self::CANONICAL);
$backgroundHTML = "
" .
$backgroundTagAttribute->toHtmlEnterTag("div") .
"
" .
"";
$tagAttributes->addHtmlAfterEnterTag($backgroundHTML);
}
break;
default:
/**
* More than one background
* This backgrounds should have been set on the backgrounds
*/
$backgroundHTML = "";
foreach ($backgrounds as $background) {
$backgroundTagAttribute = TagAttributes::createFromCallStackArray($background);
$backgroundTagAttribute->addClassName(self::CANONICAL);
$backgroundHTMLEnter = $backgroundTagAttribute->toHtmlEnterTag("div");
$backgroundHTML .= $backgroundHTMLEnter . "";
}
$tagAttributes->addHtmlAfterEnterTag($backgroundHTML);
break;
}
}
/**
* Background-image attribute
*/
$backgroundImageStyleValue = "";
if ($tagAttributes->hasComponentAttribute(self::BACKGROUND_IMAGE)) {
$backgroundImageValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_IMAGE);
if (is_string($backgroundImageValue)) {
/**
* Image background is set by the user
*/
$backgroundImageStyleValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_IMAGE);
} else {
if (is_array($backgroundImageValue)) {
/**
* Background-fill for background image
*/
$backgroundFill = $tagAttributes->getValueAndRemove(self::BACKGROUND_FILL, "cover");
switch ($backgroundFill) {
case "cover":
// it makes the background responsive
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_SIZE, $backgroundFill);
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_REPEAT, "no-repeat");
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_POSITION, "center center");
/**
* The type of image is important for the processing of SVG
*/
$backgroundImageValue[TagAttributes::TYPE_KEY] = FetcherSvg::ILLUSTRATION_TYPE;
break;
case "tile":
// background size is then "auto" (ie repeat), the default
// background position is not needed (the tile start on the left top corner)
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_REPEAT, "repeat");
/**
* The type of image is important for the processing of SVG
* A tile needs to have a width and a height
*/
$backgroundImageValue[TagAttributes::TYPE_KEY] = FetcherSvg::TILE_TYPE;
break;
case "css":
// custom, set by the user in own css stylesheet, nothing to do
break;
default:
LogUtility::msg("The background `fill` attribute ($backgroundFill) is unknown. If you want to take over the filling via css, set the `fill` value to `css`.", self::CANONICAL);
break;
}
try {
$mediaMarkup = MediaMarkup::createFromCallStackArray($backgroundImageValue)
->setLinking(MediaMarkup::LINKING_NOLINK_VALUE);
} catch (ExceptionCompile $e) {
LogUtility::error("We could not create a background image. Error: {$e->getMessage()}");
return;
}
try {
$imageFetcher = $mediaMarkup->getFetcher();
} catch (ExceptionBadArgument|ExceptionInternal|ExceptionNotFound $e) {
LogUtility::internalError("The fetcher for the background image ($mediaMarkup) returns an error", self::CANONICAL, $e);
return;
}
$mime = $imageFetcher->getMime();
if (!$mime->isImage()) {
LogUtility::error("The background image ($mediaMarkup) is not an image but a $mime", self::CANONICAL);
return;
}
$backgroundImageStyleValue = "url(" . $imageFetcher->getFetchUrl()->toAbsoluteUrl()->toCssString() . ")";
} else {
LogUtility::msg("Internal Error: The background image value ($backgroundImageValue) is not a string nor an array", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
}
}
}
if (!empty($backgroundImageStyleValue)) {
if ($tagAttributes->hasComponentAttribute(Opacity::OPACITY_ATTRIBUTE)) {
$opacity = $tagAttributes->getValueAndRemove(Opacity::OPACITY_ATTRIBUTE);
$finalOpacity = 1 - $opacity;
/**
* In the argument of linear-gradient, we don't use `0 100%` to apply the
* same image everywhere
*
* because the validator https://validator.w3.org/ would complain with
* `
* CSS: background-image: too few values for the property linear-gradient.
* `
*/
$backgroundImageStyleValue = "linear-gradient(to right, rgba(255,255,255, $finalOpacity) 0 50%, rgba(255,255,255, $finalOpacity) 50% 100%)," . $backgroundImageStyleValue;
}
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_IMAGE, $backgroundImageStyleValue);
}
/**
* Process the pattern css
* https://bansal.io/pattern-css
* This call should be before the processing of the background color
* because it will set one if it's not available
*/
self::processPatternAttribute($tagAttributes);
/**
* Background color
*/
if ($tagAttributes->hasComponentAttribute(self::BACKGROUND_COLOR)) {
$colorValue = $tagAttributes->getValueAndRemove(self::BACKGROUND_COLOR);
$gradientPrefix = 'gradient-';
if (strpos($colorValue, $gradientPrefix) === 0) {
/**
* A gradient is an image
* Check that there is no image
*/
if (!empty($backgroundImageStyleValue)) {
LogUtility::msg("An image and a linear gradient color are exclusive because a linear gradient color creates an image. You can't use the linear color (" . $colorValue . ") and the image (" . $backgroundImageStyleValue . ")", LogUtility::LVL_MSG_WARNING, self::CANONICAL);
} else {
$mainColorValue = substr($colorValue, strlen($gradientPrefix));
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_IMAGE, 'linear-gradient(to top,#fff 0,' . ColorRgb::createFromString($mainColorValue)->toCssValue() . ' 100%)');
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_COLOR, 'unset!important');
}
} else {
$colorValue = ColorRgb::createFromString($colorValue)->toCssValue();
$tagAttributes->addStyleDeclarationIfNotSet(self::BACKGROUND_COLOR, $colorValue);
}
}
}
/**
* Return a background array with background properties
* from a media {@link MediaLink::toCallStackArray()}
* @param array $mediaCallStackArray
* @return array
*/
public static function fromMediaToBackgroundImageStackArray(array $mediaCallStackArray): array
{
/**
* This attributes should no be taken
*/
$mediaCallStackArray[MediaMarkup::LINKING_KEY] = LazyLoad::LAZY_LOAD_METHOD_NONE_VALUE;
$mediaCallStackArray[Align::ALIGN_ATTRIBUTE] = null;
$mediaCallStackArray[TagAttributes::TITLE_KEY] = null; // not sure why
return $mediaCallStackArray;
}
/**
* @param TagAttributes $tagAttributes
* Process the `pattern` attribute
* that implements
* https://bansal.io/pattern-css
*/
private static function processPatternAttribute(TagAttributes &$tagAttributes)
{
/**
* Css Pattern
*/
if ($tagAttributes->hasComponentAttribute(self::PATTERN_ATTRIBUTE)) {
/**
* Attach the stylesheet
*/
PluginUtility::getSnippetManager()->attachRemoteCssStyleSheet(
self::PATTERN_CSS_SNIPPET_ID,
"https://cdn.jsdelivr.net/npm/pattern.css@1.0.0/dist/pattern.min.css",
"sha256-Vwich3JPJa27TO9g6q+TxJGE7DNEigBaHNPm+KkMR6o=")
->setCritical(false); // not blocking for rendering
$patternValue = strtolower($tagAttributes->getValueAndRemove(self::PATTERN_ATTRIBUTE));
$lastIndexOfMinus = StringUtility::lastIndexOf($patternValue, "-");
$lastMinusPart = substr($patternValue, $lastIndexOfMinus);
/**
* Do we have the size as last part
*/
if (!in_array($lastMinusPart, self::PATTERN_CSS_SIZE)) {
/**
* We don't have a size
*/
$pattern = $patternValue;
$size = "md";
} else {
$pattern = substr($patternValue, 0, $lastIndexOfMinus);
$size = $lastMinusPart;
}
/**
* Does this pattern is a known pattern
*/
if (!in_array($pattern, self::PATTERN_NAMES)) {
LogUtility::msg("The pattern (" . $pattern . ") is not a known CSS pattern and was ignored.", LogUtility::LVL_MSG_WARNING, self::CANONICAL);
return;
} else {
$tagAttributes->addClassName(self::PATTERN_CSS_CLASS_PREFIX . "-" . $pattern . "-" . $size);
}
if (!$tagAttributes->hasComponentAttribute(self::BACKGROUND_COLOR)) {
LogUtility::msg("The background color was not set for the background with the (" . $pattern . "). It was set to the default color.", LogUtility::LVL_MSG_INFO, self::CANONICAL);
$tagAttributes->addComponentAttributeValue(self::BACKGROUND_COLOR, "steelblue");
}
/**
* Color
*/
if ($tagAttributes->hasComponentAttribute(self::PATTERN_COLOR_ATTRIBUTE)) {
$patternColor = $tagAttributes->getValueAndRemove(self::PATTERN_COLOR_ATTRIBUTE);
} else {
LogUtility::msg("The pattern color was not set for the background with the (" . $pattern . "). It was set to the default color.", LogUtility::LVL_MSG_INFO, self::CANONICAL);
$patternColor = "#FDE482";
}
$tagAttributes->addStyleDeclarationIfNotSet(ColorRgb::COLOR, $patternColor);
}
}
}