xref: /plugin/combo/ComboStrap/SvgImageLink.php (revision c3437056399326d621a01da73b649707fbb0ae69)
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     */
50    public function __construct($imageSvg)
51    {
52        parent::__construct($imageSvg);
53        $imageSvg->getAttributes()->setLogicalTag(self::CANONICAL);
54
55    }
56
57
58    private function createImgHTMLTag(): string
59    {
60
61
62        $lazyLoad = $this->getLazyLoad();
63
64        $svgInjection = PluginUtility::getConfValue(self::CONF_SVG_INJECTION_ENABLE, 1);
65        /**
66         * Snippet
67         */
68        if ($svgInjection) {
69            $snippetManager = PluginUtility::getSnippetManager();
70
71            // Based on https://github.com/iconic/SVGInjector/
72            // See also: https://github.com/iconfu/svg-inject
73            // !! There is a fork: https://github.com/tanem/svg-injector !!
74            // Fallback ? : https://github.com/iconic/SVGInjector/#per-element-png-fallback
75            $snippetManager->upsertTagsForBar("svg-injector",
76                array(
77                    'script' => [
78                        array(
79                            "src" => "https://cdn.jsdelivr.net/npm/svg-injector@1.1.3/svg-injector.min.js",
80                            // "integrity" => "sha256-CjBlJvxqLCU2HMzFunTelZLFHCJdqgDoHi/qGJWdRJk=",
81                            "crossorigin" => "anonymous"
82                        )
83                    ]
84                )
85            );
86        }
87
88        // Add lazy load snippet
89        if ($lazyLoad) {
90            LazyLoad::addLozadSnippet();
91        }
92
93        /**
94         * Remove the cache attribute
95         * (no cache for the img tag)
96         * @var ImageSvg $image
97         */
98        $image = $this->getDefaultImage();
99        $responseAttributes = TagAttributes::createFromTagAttributes($image->getAttributes());
100        $responseAttributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY);
101
102        /**
103         * Remove linking (not yet implemented)
104         */
105        $responseAttributes->removeComponentAttributeIfPresent(MediaLink::LINKING_KEY);
106
107
108        /**
109         * Adaptive Image
110         * It adds a `height: auto` that avoid a layout shift when
111         * using the img tag
112         */
113        $responseAttributes->addClassName(RasterImageLink::RESPONSIVE_CLASS);
114
115
116        /**
117         * Alt is mandatory
118         */
119        $responseAttributes->addHtmlAttributeValue("alt", $image->getAltNotEmpty());
120
121
122        /**
123         * Class management
124         *
125         * functionalClass is the class used in Javascript
126         * that should be in the class attribute
127         * When injected, the other class should come in a `data-class` attribute
128         */
129        $svgFunctionalClass = "";
130        if ($svgInjection && $lazyLoad) {
131            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg-injection");
132            $svgFunctionalClass = "lazy-svg-injection-combo";
133        } else if ($lazyLoad && !$svgInjection) {
134            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg");
135            $svgFunctionalClass = "lazy-svg-combo";
136        } else if ($svgInjection && !$lazyLoad) {
137            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("svg-injector");
138            $svgFunctionalClass = "svg-injection-combo";
139        }
140        if ($lazyLoad) {
141            // A class to all component lazy loaded to download them before print
142            $svgFunctionalClass .= " " . LazyLoad::LAZY_CLASS;
143        }
144        $responseAttributes->addClassName($svgFunctionalClass);
145
146        /**
147         * Dimension are mandatory
148         * to avoid layout shift (CLS)
149         */
150        $responseAttributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth());
151        $responseAttributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight());
152
153        /**
154         * Src call
155         */
156        $srcValue = $image->getUrl(DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML);
157        if ($lazyLoad) {
158
159            /**
160             * Note: Responsive image srcset is not needed for svg
161             */
162            $responseAttributes->addHtmlAttributeValue("data-src", $srcValue);
163            $responseAttributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder(
164                $image->getTargetWidth(),
165                $image->getTargetHeight()
166            ));
167
168        } else {
169
170            $responseAttributes->addHtmlAttributeValue("src", $srcValue);
171
172        }
173
174        /**
175         * Old model where dokuwiki parses the src in handle
176         */
177        $responseAttributes->removeAttributeIfPresent(PagePath::PROPERTY_NAME);
178
179        /**
180         * Return the image
181         */
182        return '<img ' . $responseAttributes->toHTMLAttributeString() . '/>';
183
184    }
185
186
187    /**
188     * Render a link
189     * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()}
190     * A media can be a video also
191     * @return string
192     */
193    public function renderMediaTag(): string
194    {
195
196        /**
197         * @var ImageSvg $image
198         */
199        $image = $this->getDefaultImage();
200        if ($image->exists()) {
201
202            /**
203             * This attributes should not be in the render
204             */
205            $attributes = $this->getDefaultImage()->getAttributes();
206            $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE);
207            $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC);
208            /**
209             * TODO: Title should be a node just below SVG
210             */
211            $attributes->removeComponentAttributeIfPresent(PageTitle::PROPERTY_NAME);
212
213            $imageSize = FileSystems::getSize($image->getPath());
214            if (
215                $imageSize > $this->getMaxInlineSize()
216            ) {
217
218                /**
219                 * Img tag
220                 */
221                $imgHTML = $this->createImgHTMLTag();
222
223            } else {
224
225                /**
226                 * Svg tag
227                 */
228                $imgHTML = FileSystems::getContent($image->getSvgFile());
229
230            }
231
232
233        } else {
234
235            $imgHTML = "<span class=\"text-danger\">The svg ($this) does not exist</span>";
236
237        }
238        return $imgHTML;
239    }
240
241    private function getMaxInlineSize()
242    {
243        return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024;
244    }
245
246
247    public function getLazyLoad()
248    {
249        $lazyLoad = parent::getLazyLoad();
250        if ($lazyLoad !== null) {
251            return $lazyLoad;
252        } else {
253            return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE);
254        }
255    }
256
257
258}
259