137748cd8SNickeau<?php 237748cd8SNickeau/** 337748cd8SNickeau * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 437748cd8SNickeau * 537748cd8SNickeau * This source code is licensed under the GPL license found in the 637748cd8SNickeau * COPYING file in the root directory of this source tree. 737748cd8SNickeau * 837748cd8SNickeau * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 937748cd8SNickeau * @author ComboStrap <support@combostrap.com> 1037748cd8SNickeau * 1137748cd8SNickeau */ 1237748cd8SNickeau 1337748cd8SNickeaunamespace ComboStrap; 1437748cd8SNickeau 151fa8c418SNickeau 1637748cd8SNickeaurequire_once(__DIR__ . '/PluginUtility.php'); 171fa8c418SNickeau 1837748cd8SNickeau 1937748cd8SNickeau/** 2037748cd8SNickeau * Image 2137748cd8SNickeau * This is the class that handles the 2237748cd8SNickeau * svg link type 2337748cd8SNickeau */ 241fa8c418SNickeauclass SvgImageLink extends ImageLink 2537748cd8SNickeau{ 2637748cd8SNickeau 271fa8c418SNickeau const CANONICAL = ImageSvg::CANONICAL; 2837748cd8SNickeau 2937748cd8SNickeau /** 3037748cd8SNickeau * The maximum size to be embedded 3137748cd8SNickeau * Above this size limit they are fetched 3237748cd8SNickeau */ 3337748cd8SNickeau const CONF_MAX_KB_SIZE_FOR_INLINE_SVG = "svgMaxInlineSizeKb"; 3437748cd8SNickeau 3537748cd8SNickeau /** 3637748cd8SNickeau * Lazy Load 3737748cd8SNickeau */ 3837748cd8SNickeau const CONF_LAZY_LOAD_ENABLE = "svgLazyLoadEnable"; 3937748cd8SNickeau 4037748cd8SNickeau /** 4137748cd8SNickeau * Svg Injection 4237748cd8SNickeau */ 4337748cd8SNickeau const CONF_SVG_INJECTION_ENABLE = "svgInjectionEnable"; 4437748cd8SNickeau 4537748cd8SNickeau 4637748cd8SNickeau /** 4737748cd8SNickeau * SvgImageLink constructor. 481fa8c418SNickeau * @param ImageSvg $imageSvg 4937748cd8SNickeau */ 501fa8c418SNickeau public function __construct($imageSvg) 5137748cd8SNickeau { 521fa8c418SNickeau parent::__construct($imageSvg); 531fa8c418SNickeau $imageSvg->getAttributes()->setLogicalTag(self::CANONICAL); 541fa8c418SNickeau 5537748cd8SNickeau } 5637748cd8SNickeau 5737748cd8SNickeau 581fa8c418SNickeau private function createImgHTMLTag(): string 5937748cd8SNickeau { 6037748cd8SNickeau 6137748cd8SNickeau 6237748cd8SNickeau $lazyLoad = $this->getLazyLoad(); 6337748cd8SNickeau 6437748cd8SNickeau $svgInjection = PluginUtility::getConfValue(self::CONF_SVG_INJECTION_ENABLE, 1); 6537748cd8SNickeau /** 6637748cd8SNickeau * Snippet 6737748cd8SNickeau */ 6837748cd8SNickeau if ($svgInjection) { 6937748cd8SNickeau $snippetManager = PluginUtility::getSnippetManager(); 7037748cd8SNickeau 7137748cd8SNickeau // Based on https://github.com/iconic/SVGInjector/ 7237748cd8SNickeau // See also: https://github.com/iconfu/svg-inject 7337748cd8SNickeau // !! There is a fork: https://github.com/tanem/svg-injector !! 7437748cd8SNickeau // Fallback ? : https://github.com/iconic/SVGInjector/#per-element-png-fallback 7537748cd8SNickeau $snippetManager->upsertTagsForBar("svg-injector", 7637748cd8SNickeau array( 7737748cd8SNickeau 'script' => [ 7837748cd8SNickeau array( 7937748cd8SNickeau "src" => "https://cdn.jsdelivr.net/npm/svg-injector@1.1.3/svg-injector.min.js", 8037748cd8SNickeau // "integrity" => "sha256-CjBlJvxqLCU2HMzFunTelZLFHCJdqgDoHi/qGJWdRJk=", 8137748cd8SNickeau "crossorigin" => "anonymous" 8237748cd8SNickeau ) 8337748cd8SNickeau ] 8437748cd8SNickeau ) 8537748cd8SNickeau ); 8637748cd8SNickeau } 8737748cd8SNickeau 8837748cd8SNickeau // Add lazy load snippet 8937748cd8SNickeau if ($lazyLoad) { 9037748cd8SNickeau LazyLoad::addLozadSnippet(); 9137748cd8SNickeau } 9237748cd8SNickeau 9337748cd8SNickeau /** 9437748cd8SNickeau * Remove the cache attribute 9537748cd8SNickeau * (no cache for the img tag) 96*c3437056SNickeau * @var ImageSvg $image 9737748cd8SNickeau */ 981fa8c418SNickeau $image = $this->getDefaultImage(); 99*c3437056SNickeau $responseAttributes = TagAttributes::createFromTagAttributes($image->getAttributes()); 100*c3437056SNickeau $responseAttributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY); 10137748cd8SNickeau 10237748cd8SNickeau /** 10337748cd8SNickeau * Remove linking (not yet implemented) 10437748cd8SNickeau */ 105*c3437056SNickeau $responseAttributes->removeComponentAttributeIfPresent(MediaLink::LINKING_KEY); 10637748cd8SNickeau 10737748cd8SNickeau 10837748cd8SNickeau /** 10937748cd8SNickeau * Adaptive Image 11037748cd8SNickeau * It adds a `height: auto` that avoid a layout shift when 11137748cd8SNickeau * using the img tag 11237748cd8SNickeau */ 113*c3437056SNickeau $responseAttributes->addClassName(RasterImageLink::RESPONSIVE_CLASS); 11437748cd8SNickeau 11537748cd8SNickeau 11637748cd8SNickeau /** 1171fa8c418SNickeau * Alt is mandatory 11837748cd8SNickeau */ 119*c3437056SNickeau $responseAttributes->addHtmlAttributeValue("alt", $image->getAltNotEmpty()); 12037748cd8SNickeau 12137748cd8SNickeau 12237748cd8SNickeau /** 12337748cd8SNickeau * Class management 12437748cd8SNickeau * 12537748cd8SNickeau * functionalClass is the class used in Javascript 12637748cd8SNickeau * that should be in the class attribute 12737748cd8SNickeau * When injected, the other class should come in a `data-class` attribute 12837748cd8SNickeau */ 12937748cd8SNickeau $svgFunctionalClass = ""; 13037748cd8SNickeau if ($svgInjection && $lazyLoad) { 13137748cd8SNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg-injection"); 13237748cd8SNickeau $svgFunctionalClass = "lazy-svg-injection-combo"; 13337748cd8SNickeau } else if ($lazyLoad && !$svgInjection) { 13437748cd8SNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg"); 13537748cd8SNickeau $svgFunctionalClass = "lazy-svg-combo"; 13637748cd8SNickeau } else if ($svgInjection && !$lazyLoad) { 13737748cd8SNickeau PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("svg-injector"); 13837748cd8SNickeau $svgFunctionalClass = "svg-injection-combo"; 13937748cd8SNickeau } 14037748cd8SNickeau if ($lazyLoad) { 14137748cd8SNickeau // A class to all component lazy loaded to download them before print 14237748cd8SNickeau $svgFunctionalClass .= " " . LazyLoad::LAZY_CLASS; 14337748cd8SNickeau } 144*c3437056SNickeau $responseAttributes->addClassName($svgFunctionalClass); 14537748cd8SNickeau 14637748cd8SNickeau /** 14737748cd8SNickeau * Dimension are mandatory 14837748cd8SNickeau * to avoid layout shift (CLS) 14937748cd8SNickeau */ 150*c3437056SNickeau $responseAttributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth()); 151*c3437056SNickeau $responseAttributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight()); 152*c3437056SNickeau 153*c3437056SNickeau /** 154*c3437056SNickeau * Src call 155*c3437056SNickeau */ 156*c3437056SNickeau $srcValue = $image->getUrl(DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML); 157*c3437056SNickeau if ($lazyLoad) { 158*c3437056SNickeau 159*c3437056SNickeau /** 160*c3437056SNickeau * Note: Responsive image srcset is not needed for svg 161*c3437056SNickeau */ 162*c3437056SNickeau $responseAttributes->addHtmlAttributeValue("data-src", $srcValue); 163*c3437056SNickeau $responseAttributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder( 164*c3437056SNickeau $image->getTargetWidth(), 165*c3437056SNickeau $image->getTargetHeight() 166*c3437056SNickeau )); 167*c3437056SNickeau 168*c3437056SNickeau } else { 169*c3437056SNickeau 170*c3437056SNickeau $responseAttributes->addHtmlAttributeValue("src", $srcValue); 171*c3437056SNickeau 172*c3437056SNickeau } 173*c3437056SNickeau 174*c3437056SNickeau /** 175*c3437056SNickeau * Old model where dokuwiki parses the src in handle 176*c3437056SNickeau */ 177*c3437056SNickeau $responseAttributes->removeAttributeIfPresent(PagePath::PROPERTY_NAME); 17837748cd8SNickeau 17937748cd8SNickeau /** 18037748cd8SNickeau * Return the image 18137748cd8SNickeau */ 182*c3437056SNickeau return '<img ' . $responseAttributes->toHTMLAttributeString() . '/>'; 18337748cd8SNickeau 18437748cd8SNickeau } 18537748cd8SNickeau 18637748cd8SNickeau 18737748cd8SNickeau /** 18837748cd8SNickeau * Render a link 18937748cd8SNickeau * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()} 19037748cd8SNickeau * A media can be a video also 19137748cd8SNickeau * @return string 19237748cd8SNickeau */ 1931fa8c418SNickeau public function renderMediaTag(): string 19437748cd8SNickeau { 19537748cd8SNickeau 1961fa8c418SNickeau /** 1971fa8c418SNickeau * @var ImageSvg $image 1981fa8c418SNickeau */ 1991fa8c418SNickeau $image = $this->getDefaultImage(); 2001fa8c418SNickeau if ($image->exists()) { 20137748cd8SNickeau 20237748cd8SNickeau /** 20337748cd8SNickeau * This attributes should not be in the render 20437748cd8SNickeau */ 2051fa8c418SNickeau $attributes = $this->getDefaultImage()->getAttributes(); 2061fa8c418SNickeau $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE); 2071fa8c418SNickeau $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC); 20837748cd8SNickeau /** 20937748cd8SNickeau * TODO: Title should be a node just below SVG 21037748cd8SNickeau */ 211*c3437056SNickeau $attributes->removeComponentAttributeIfPresent(PageTitle::PROPERTY_NAME); 21237748cd8SNickeau 213*c3437056SNickeau $imageSize = FileSystems::getSize($image->getPath()); 21437748cd8SNickeau if ( 215*c3437056SNickeau $imageSize > $this->getMaxInlineSize() 21637748cd8SNickeau ) { 21737748cd8SNickeau 21837748cd8SNickeau /** 21937748cd8SNickeau * Img tag 22037748cd8SNickeau */ 22137748cd8SNickeau $imgHTML = $this->createImgHTMLTag(); 22237748cd8SNickeau 22337748cd8SNickeau } else { 22437748cd8SNickeau 22537748cd8SNickeau /** 22637748cd8SNickeau * Svg tag 22737748cd8SNickeau */ 228*c3437056SNickeau $imgHTML = FileSystems::getContent($image->getSvgFile()); 22937748cd8SNickeau 23037748cd8SNickeau } 23137748cd8SNickeau 23237748cd8SNickeau 23337748cd8SNickeau } else { 23437748cd8SNickeau 23537748cd8SNickeau $imgHTML = "<span class=\"text-danger\">The svg ($this) does not exist</span>"; 23637748cd8SNickeau 23737748cd8SNickeau } 23837748cd8SNickeau return $imgHTML; 23937748cd8SNickeau } 24037748cd8SNickeau 24137748cd8SNickeau private function getMaxInlineSize() 24237748cd8SNickeau { 24337748cd8SNickeau return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024; 24437748cd8SNickeau } 24537748cd8SNickeau 24637748cd8SNickeau 24737748cd8SNickeau public function getLazyLoad() 24837748cd8SNickeau { 24937748cd8SNickeau $lazyLoad = parent::getLazyLoad(); 25037748cd8SNickeau if ($lazyLoad !== null) { 25137748cd8SNickeau return $lazyLoad; 25237748cd8SNickeau } else { 25337748cd8SNickeau return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE); 25437748cd8SNickeau } 25537748cd8SNickeau } 25637748cd8SNickeau 25737748cd8SNickeau 25837748cd8SNickeau} 259