1<?php 2/** 3 * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14 15 16require_once(__DIR__ . '/PluginUtility.php'); 17 18 19/** 20 * Image 21 * This is the class that handles the 22 * svg link type 23 */ 24class SvgImageLink extends ImageLink 25{ 26 27 const CANONICAL = ImageSvg::CANONICAL; 28 29 /** 30 * The maximum size to be embedded 31 * Above this size limit they are fetched 32 */ 33 const CONF_MAX_KB_SIZE_FOR_INLINE_SVG = "svgMaxInlineSizeKb"; 34 35 /** 36 * Lazy Load 37 */ 38 const CONF_LAZY_LOAD_ENABLE = "svgLazyLoadEnable"; 39 40 /** 41 * Svg Injection 42 */ 43 const CONF_SVG_INJECTION_ENABLE = "svgInjectionEnable"; 44 45 46 /** 47 * SvgImageLink constructor. 48 * @param ImageSvg $imageSvg 49 * @param TagAttributes $tagAttributes 50 */ 51 public function __construct($imageSvg) 52 { 53 parent::__construct($imageSvg); 54 $imageSvg->getAttributes()->setLogicalTag(self::CANONICAL); 55 56 } 57 58 59 private function createImgHTMLTag(): string 60 { 61 62 63 $lazyLoad = $this->getLazyLoad(); 64 65 $svgInjection = PluginUtility::getConfValue(self::CONF_SVG_INJECTION_ENABLE, 1); 66 /** 67 * Snippet 68 */ 69 if ($svgInjection) { 70 $snippetManager = PluginUtility::getSnippetManager(); 71 72 // Based on https://github.com/iconic/SVGInjector/ 73 // See also: https://github.com/iconfu/svg-inject 74 // !! There is a fork: https://github.com/tanem/svg-injector !! 75 // Fallback ? : https://github.com/iconic/SVGInjector/#per-element-png-fallback 76 $snippetManager->upsertTagsForBar("svg-injector", 77 array( 78 'script' => [ 79 array( 80 "src" => "https://cdn.jsdelivr.net/npm/svg-injector@1.1.3/svg-injector.min.js", 81 // "integrity" => "sha256-CjBlJvxqLCU2HMzFunTelZLFHCJdqgDoHi/qGJWdRJk=", 82 "crossorigin" => "anonymous" 83 ) 84 ] 85 ) 86 ); 87 } 88 89 // Add lazy load snippet 90 if ($lazyLoad) { 91 LazyLoad::addLozadSnippet(); 92 } 93 94 /** 95 * Remove the cache attribute 96 * (no cache for the img tag) 97 */ 98 $image = $this->getDefaultImage(); 99 $attributes = $image->getAttributes(); 100 $attributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY); 101 102 /** 103 * Remove linking (not yet implemented) 104 */ 105 $attributes->removeComponentAttributeIfPresent(MediaLink::LINKING_KEY); 106 107 108 /** 109 * Src 110 */ 111 $srcValue = $image->getUrl(DokuwikiUrl::URL_ENCODED_AND); 112 if ($lazyLoad) { 113 114 /** 115 * Note: Responsive image srcset is not needed for svg 116 */ 117 $attributes->addHtmlAttributeValue("data-src", $srcValue); 118 $attributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder( 119 $image->getTargetWidth(), 120 $image->getTargetHeight() 121 )); 122 123 } else { 124 125 $attributes->addHtmlAttributeValue("src", $srcValue); 126 127 } 128 129 /** 130 * Adaptive Image 131 * It adds a `height: auto` that avoid a layout shift when 132 * using the img tag 133 */ 134 $attributes->addClassName(RasterImageLink::RESPONSIVE_CLASS); 135 136 137 /** 138 * Alt is mandatory 139 */ 140 $attributes->addHtmlAttributeValue("alt", $image->getAltNotEmpty()); 141 142 143 /** 144 * Class management 145 * 146 * functionalClass is the class used in Javascript 147 * that should be in the class attribute 148 * When injected, the other class should come in a `data-class` attribute 149 */ 150 $svgFunctionalClass = ""; 151 if ($svgInjection && $lazyLoad) { 152 PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg-injection"); 153 $svgFunctionalClass = "lazy-svg-injection-combo"; 154 } else if ($lazyLoad && !$svgInjection) { 155 PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg"); 156 $svgFunctionalClass = "lazy-svg-combo"; 157 } else if ($svgInjection && !$lazyLoad) { 158 PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("svg-injector"); 159 $svgFunctionalClass = "svg-injection-combo"; 160 } 161 if ($lazyLoad) { 162 // A class to all component lazy loaded to download them before print 163 $svgFunctionalClass .= " " . LazyLoad::LAZY_CLASS; 164 } 165 $attributes->addClassName($svgFunctionalClass); 166 167 /** 168 * Dimension are mandatory 169 * to avoid layout shift (CLS) 170 */ 171 $attributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth()); 172 $attributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight()); 173 174 /** 175 * Return the image 176 */ 177 return '<img ' . $attributes->toHTMLAttributeString() . '/>'; 178 179 } 180 181 182 /** 183 * Render a link 184 * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()} 185 * A media can be a video also 186 * @return string 187 */ 188 public function renderMediaTag(): string 189 { 190 191 /** 192 * @var ImageSvg $image 193 */ 194 $image = $this->getDefaultImage(); 195 if ($image->exists()) { 196 197 /** 198 * This attributes should not be in the render 199 */ 200 $attributes = $this->getDefaultImage()->getAttributes(); 201 $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE); 202 $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC); 203 /** 204 * TODO: Title should be a node just below SVG 205 */ 206 $attributes->removeComponentAttributeIfPresent(Page::TITLE_META_PROPERTY); 207 208 if ( 209 $image->getSize() > $this->getMaxInlineSize() 210 ) { 211 212 /** 213 * Img tag 214 */ 215 $imgHTML = $this->createImgHTMLTag(); 216 217 } else { 218 219 /** 220 * Svg tag 221 */ 222 $imgHTML = file_get_contents($image->getSvgFile()); 223 224 } 225 226 227 } else { 228 229 $imgHTML = "<span class=\"text-danger\">The svg ($this) does not exist</span>"; 230 231 } 232 return $imgHTML; 233 } 234 235 private function getMaxInlineSize() 236 { 237 return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024; 238 } 239 240 241 public function getLazyLoad() 242 { 243 $lazyLoad = parent::getLazyLoad(); 244 if ($lazyLoad !== null) { 245 return $lazyLoad; 246 } else { 247 return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE); 248 } 249 } 250 251 252} 253