xref: /template/strap/ComboStrap/SvgImageLink.php (revision c3437056399326d621a01da73b649707fbb0ae69)
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