xref: /template/strap/ComboStrap/SvgImageLink.php (revision 1fa8c418ed5809db58049141be41b7738471dd32)
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
15*1fa8c418SNickeau
1637748cd8SNickeaurequire_once(__DIR__ . '/PluginUtility.php');
17*1fa8c418SNickeau
1837748cd8SNickeau
1937748cd8SNickeau/**
2037748cd8SNickeau * Image
2137748cd8SNickeau * This is the class that handles the
2237748cd8SNickeau * svg link type
2337748cd8SNickeau */
24*1fa8c418SNickeauclass SvgImageLink extends ImageLink
2537748cd8SNickeau{
2637748cd8SNickeau
27*1fa8c418SNickeau    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.
48*1fa8c418SNickeau     * @param ImageSvg $imageSvg
4937748cd8SNickeau     * @param TagAttributes $tagAttributes
5037748cd8SNickeau     */
51*1fa8c418SNickeau    public function __construct($imageSvg)
5237748cd8SNickeau    {
53*1fa8c418SNickeau        parent::__construct($imageSvg);
54*1fa8c418SNickeau        $imageSvg->getAttributes()->setLogicalTag(self::CANONICAL);
55*1fa8c418SNickeau
5637748cd8SNickeau    }
5737748cd8SNickeau
5837748cd8SNickeau
59*1fa8c418SNickeau    private function createImgHTMLTag(): string
6037748cd8SNickeau    {
6137748cd8SNickeau
6237748cd8SNickeau
6337748cd8SNickeau        $lazyLoad = $this->getLazyLoad();
6437748cd8SNickeau
6537748cd8SNickeau        $svgInjection = PluginUtility::getConfValue(self::CONF_SVG_INJECTION_ENABLE, 1);
6637748cd8SNickeau        /**
6737748cd8SNickeau         * Snippet
6837748cd8SNickeau         */
6937748cd8SNickeau        if ($svgInjection) {
7037748cd8SNickeau            $snippetManager = PluginUtility::getSnippetManager();
7137748cd8SNickeau
7237748cd8SNickeau            // Based on https://github.com/iconic/SVGInjector/
7337748cd8SNickeau            // See also: https://github.com/iconfu/svg-inject
7437748cd8SNickeau            // !! There is a fork: https://github.com/tanem/svg-injector !!
7537748cd8SNickeau            // Fallback ? : https://github.com/iconic/SVGInjector/#per-element-png-fallback
7637748cd8SNickeau            $snippetManager->upsertTagsForBar("svg-injector",
7737748cd8SNickeau                array(
7837748cd8SNickeau                    'script' => [
7937748cd8SNickeau                        array(
8037748cd8SNickeau                            "src" => "https://cdn.jsdelivr.net/npm/svg-injector@1.1.3/svg-injector.min.js",
8137748cd8SNickeau                            // "integrity" => "sha256-CjBlJvxqLCU2HMzFunTelZLFHCJdqgDoHi/qGJWdRJk=",
8237748cd8SNickeau                            "crossorigin" => "anonymous"
8337748cd8SNickeau                        )
8437748cd8SNickeau                    ]
8537748cd8SNickeau                )
8637748cd8SNickeau            );
8737748cd8SNickeau        }
8837748cd8SNickeau
8937748cd8SNickeau        // Add lazy load snippet
9037748cd8SNickeau        if ($lazyLoad) {
9137748cd8SNickeau            LazyLoad::addLozadSnippet();
9237748cd8SNickeau        }
9337748cd8SNickeau
9437748cd8SNickeau        /**
9537748cd8SNickeau         * Remove the cache attribute
9637748cd8SNickeau         * (no cache for the img tag)
9737748cd8SNickeau         */
98*1fa8c418SNickeau        $image = $this->getDefaultImage();
99*1fa8c418SNickeau        $attributes = $image->getAttributes();
100*1fa8c418SNickeau        $attributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY);
10137748cd8SNickeau
10237748cd8SNickeau        /**
10337748cd8SNickeau         * Remove linking (not yet implemented)
10437748cd8SNickeau         */
105*1fa8c418SNickeau        $attributes->removeComponentAttributeIfPresent(MediaLink::LINKING_KEY);
10637748cd8SNickeau
10737748cd8SNickeau
10837748cd8SNickeau        /**
10937748cd8SNickeau         * Src
11037748cd8SNickeau         */
111*1fa8c418SNickeau        $srcValue = $image->getUrl(DokuwikiUrl::URL_ENCODED_AND);
11237748cd8SNickeau        if ($lazyLoad) {
11337748cd8SNickeau
11437748cd8SNickeau            /**
11537748cd8SNickeau             * Note: Responsive image srcset is not needed for svg
11637748cd8SNickeau             */
117*1fa8c418SNickeau            $attributes->addHtmlAttributeValue("data-src", $srcValue);
118*1fa8c418SNickeau            $attributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder(
119*1fa8c418SNickeau                $image->getTargetWidth(),
120*1fa8c418SNickeau                $image->getTargetHeight()
121*1fa8c418SNickeau            ));
12237748cd8SNickeau
12337748cd8SNickeau        } else {
12437748cd8SNickeau
125*1fa8c418SNickeau            $attributes->addHtmlAttributeValue("src", $srcValue);
12637748cd8SNickeau
12737748cd8SNickeau        }
12837748cd8SNickeau
12937748cd8SNickeau        /**
13037748cd8SNickeau         * Adaptive Image
13137748cd8SNickeau         * It adds a `height: auto` that avoid a layout shift when
13237748cd8SNickeau         * using the img tag
13337748cd8SNickeau         */
134*1fa8c418SNickeau        $attributes->addClassName(RasterImageLink::RESPONSIVE_CLASS);
13537748cd8SNickeau
13637748cd8SNickeau
13737748cd8SNickeau        /**
138*1fa8c418SNickeau         * Alt is mandatory
13937748cd8SNickeau         */
140*1fa8c418SNickeau        $attributes->addHtmlAttributeValue("alt", $image->getAltNotEmpty());
14137748cd8SNickeau
14237748cd8SNickeau
14337748cd8SNickeau        /**
14437748cd8SNickeau         * Class management
14537748cd8SNickeau         *
14637748cd8SNickeau         * functionalClass is the class used in Javascript
14737748cd8SNickeau         * that should be in the class attribute
14837748cd8SNickeau         * When injected, the other class should come in a `data-class` attribute
14937748cd8SNickeau         */
15037748cd8SNickeau        $svgFunctionalClass = "";
15137748cd8SNickeau        if ($svgInjection && $lazyLoad) {
15237748cd8SNickeau            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg-injection");
15337748cd8SNickeau            $svgFunctionalClass = "lazy-svg-injection-combo";
15437748cd8SNickeau        } else if ($lazyLoad && !$svgInjection) {
15537748cd8SNickeau            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("lozad-svg");
15637748cd8SNickeau            $svgFunctionalClass = "lazy-svg-combo";
15737748cd8SNickeau        } else if ($svgInjection && !$lazyLoad) {
15837748cd8SNickeau            PluginUtility::getSnippetManager()->attachJavascriptSnippetForBar("svg-injector");
15937748cd8SNickeau            $svgFunctionalClass = "svg-injection-combo";
16037748cd8SNickeau        }
16137748cd8SNickeau        if ($lazyLoad) {
16237748cd8SNickeau            // A class to all component lazy loaded to download them before print
16337748cd8SNickeau            $svgFunctionalClass .= " " . LazyLoad::LAZY_CLASS;
16437748cd8SNickeau        }
165*1fa8c418SNickeau        $attributes->addClassName($svgFunctionalClass);
16637748cd8SNickeau
16737748cd8SNickeau        /**
16837748cd8SNickeau         * Dimension are mandatory
16937748cd8SNickeau         * to avoid layout shift (CLS)
17037748cd8SNickeau         */
171*1fa8c418SNickeau        $attributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth());
172*1fa8c418SNickeau        $attributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight());
17337748cd8SNickeau
17437748cd8SNickeau        /**
17537748cd8SNickeau         * Return the image
17637748cd8SNickeau         */
177*1fa8c418SNickeau        return '<img ' . $attributes->toHTMLAttributeString() . '/>';
17837748cd8SNickeau
17937748cd8SNickeau    }
18037748cd8SNickeau
18137748cd8SNickeau
18237748cd8SNickeau    /**
18337748cd8SNickeau     * Render a link
18437748cd8SNickeau     * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()}
18537748cd8SNickeau     * A media can be a video also
18637748cd8SNickeau     * @return string
18737748cd8SNickeau     */
188*1fa8c418SNickeau    public function renderMediaTag(): string
18937748cd8SNickeau    {
19037748cd8SNickeau
191*1fa8c418SNickeau        /**
192*1fa8c418SNickeau         * @var ImageSvg $image
193*1fa8c418SNickeau         */
194*1fa8c418SNickeau        $image = $this->getDefaultImage();
195*1fa8c418SNickeau        if ($image->exists()) {
19637748cd8SNickeau
19737748cd8SNickeau            /**
19837748cd8SNickeau             * This attributes should not be in the render
19937748cd8SNickeau             */
200*1fa8c418SNickeau            $attributes = $this->getDefaultImage()->getAttributes();
201*1fa8c418SNickeau            $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE);
202*1fa8c418SNickeau            $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC);
20337748cd8SNickeau            /**
20437748cd8SNickeau             * TODO: Title should be a node just below SVG
20537748cd8SNickeau             */
206*1fa8c418SNickeau            $attributes->removeComponentAttributeIfPresent(Page::TITLE_META_PROPERTY);
20737748cd8SNickeau
20837748cd8SNickeau            if (
209*1fa8c418SNickeau                $image->getSize() > $this->getMaxInlineSize()
21037748cd8SNickeau            ) {
21137748cd8SNickeau
21237748cd8SNickeau                /**
21337748cd8SNickeau                 * Img tag
21437748cd8SNickeau                 */
21537748cd8SNickeau                $imgHTML = $this->createImgHTMLTag();
21637748cd8SNickeau
21737748cd8SNickeau            } else {
21837748cd8SNickeau
21937748cd8SNickeau                /**
22037748cd8SNickeau                 * Svg tag
22137748cd8SNickeau                 */
222*1fa8c418SNickeau                $imgHTML = file_get_contents($image->getSvgFile());
22337748cd8SNickeau
22437748cd8SNickeau            }
22537748cd8SNickeau
22637748cd8SNickeau
22737748cd8SNickeau        } else {
22837748cd8SNickeau
22937748cd8SNickeau            $imgHTML = "<span class=\"text-danger\">The svg ($this) does not exist</span>";
23037748cd8SNickeau
23137748cd8SNickeau        }
23237748cd8SNickeau        return $imgHTML;
23337748cd8SNickeau    }
23437748cd8SNickeau
23537748cd8SNickeau    private function getMaxInlineSize()
23637748cd8SNickeau    {
23737748cd8SNickeau        return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024;
23837748cd8SNickeau    }
23937748cd8SNickeau
24037748cd8SNickeau
24137748cd8SNickeau    public function getLazyLoad()
24237748cd8SNickeau    {
24337748cd8SNickeau        $lazyLoad = parent::getLazyLoad();
24437748cd8SNickeau        if ($lazyLoad !== null) {
24537748cd8SNickeau            return $lazyLoad;
24637748cd8SNickeau        } else {
24737748cd8SNickeau            return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE);
24837748cd8SNickeau        }
24937748cd8SNickeau    }
25037748cd8SNickeau
25137748cd8SNickeau
25237748cd8SNickeau}
253