* */ namespace ComboStrap; require_once(__DIR__ . '/PluginUtility.php'); /** * Image * This is the class that handles the * svg link type */ class SvgImageLink extends ImageLink { const CANONICAL = ImageSvg::CANONICAL; /** * The maximum size to be embedded * Above this size limit they are fetched */ const CONF_MAX_KB_SIZE_FOR_INLINE_SVG = "svgMaxInlineSizeKb"; /** * Lazy Load */ const CONF_LAZY_LOAD_ENABLE = "svgLazyLoadEnable"; /** * Svg Injection */ const CONF_SVG_INJECTION_ENABLE = "svgInjectionEnable"; /** * SvgImageLink constructor. * @param ImageSvg $imageSvg */ public function __construct($imageSvg) { parent::__construct($imageSvg); $imageSvg->getAttributes()->setLogicalTag(self::CANONICAL); } private function createImgHTMLTag(): string { $lazyLoad = $this->getLazyLoad(); $svgInjection = PluginUtility::getConfValue(self::CONF_SVG_INJECTION_ENABLE, 1); /** * Snippet */ if ($svgInjection) { $snippetManager = PluginUtility::getSnippetManager(); // Based on https://github.com/iconic/SVGInjector/ // See also: https://github.com/iconfu/svg-inject // !! There is a fork: https://github.com/tanem/svg-injector !! // Fallback ? : https://github.com/iconic/SVGInjector/#per-element-png-fallback $snippetManager->upsertTagsForBar("svg-injector", array( 'script' => [ array( "src" => "https://cdn.jsdelivr.net/npm/svg-injector@1.1.3/svg-injector.min.js", // "integrity" => "sha256-CjBlJvxqLCU2HMzFunTelZLFHCJdqgDoHi/qGJWdRJk=", "crossorigin" => "anonymous" ) ] ) ); } // Add lazy load snippet if ($lazyLoad) { LazyLoad::addLozadSnippet(); } /** * Remove the cache attribute * (no cache for the img tag) * @var ImageSvg $image */ $image = $this->getDefaultImage(); $responseAttributes = TagAttributes::createFromTagAttributes($image->getAttributes()); $responseAttributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY); /** * Remove linking (not yet implemented) */ $responseAttributes->removeComponentAttributeIfPresent(MediaLink::LINKING_KEY); /** * Adaptive Image * It adds a `height: auto` that avoid a layout shift when * using the img tag */ $responseAttributes->addClassName(RasterImageLink::RESPONSIVE_CLASS); /** * Alt is mandatory */ $responseAttributes->addHtmlAttributeValue("alt", $image->getAltNotEmpty()); /** * Class management * * functionalClass is the class used in Javascript * that should be in the class attribute * When injected, the other class should come in a `data-class` attribute */ $svgFunctionalClass = ""; if ($svgInjection && $lazyLoad) { PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg-injection"); $svgFunctionalClass = "lazy-svg-injection-combo"; } else if ($lazyLoad && !$svgInjection) { PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg"); $svgFunctionalClass = "lazy-svg-combo"; } else if ($svgInjection && !$lazyLoad) { PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("svg-injector"); $svgFunctionalClass = "svg-injection-combo"; } if ($lazyLoad) { // A class to all component lazy loaded to download them before print $svgFunctionalClass .= " " . LazyLoad::LAZY_CLASS; } $responseAttributes->addClassName($svgFunctionalClass); /** * Dimension are mandatory * to avoid layout shift (CLS) */ $responseAttributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth()); $responseAttributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight()); /** * Src call */ $srcValue = $image->getUrl(DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML); if ($lazyLoad) { /** * Note: Responsive image srcset is not needed for svg */ $responseAttributes->addHtmlAttributeValue("data-src", $srcValue); $responseAttributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder( $image->getTargetWidth(), $image->getTargetHeight() )); } else { $responseAttributes->addHtmlAttributeValue("src", $srcValue); } /** * Old model where dokuwiki parses the src in handle */ $responseAttributes->removeAttributeIfPresent(PagePath::PROPERTY_NAME); /** * Return the image */ return 'toHTMLAttributeString() . '/>'; } /** * Render a link * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()} * A media can be a video also * @return string */ public function renderMediaTag(): string { /** * @var ImageSvg $image */ $image = $this->getDefaultImage(); if ($image->exists()) { /** * This attributes should not be in the render */ $attributes = $this->getDefaultImage()->getAttributes(); $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE); $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC); /** * TODO: Title should be a node just below SVG */ $attributes->removeComponentAttributeIfPresent(PageTitle::PROPERTY_NAME); $imageSize = FileSystems::getSize($image->getPath()); if ( $imageSize > $this->getMaxInlineSize() ) { /** * Img tag */ $imgHTML = $this->createImgHTMLTag(); } else { /** * Svg tag */ $imgHTML = FileSystems::getContent($image->getSvgFile()); } } else { $imgHTML = "The svg ($this) does not exist"; } return $imgHTML; } private function getMaxInlineSize() { return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024; } public function getLazyLoad() { $lazyLoad = parent::getLazyLoad(); if ($lazyLoad !== null) { return $lazyLoad; } else { return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE); } } }