xref: /plugin/combo/ComboStrap/SvgImageLink.php (revision 82a60d039cd81033dc8147c27f0a50716b7a5301)
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)
96c3437056SNickeau         * @var ImageSvg $image
9737748cd8SNickeau         */
981fa8c418SNickeau        $image = $this->getDefaultImage();
99c3437056SNickeau        $responseAttributes = TagAttributes::createFromTagAttributes($image->getAttributes());
100c3437056SNickeau        $responseAttributes->removeComponentAttributeIfPresent(CacheMedia::CACHE_KEY);
10137748cd8SNickeau
10237748cd8SNickeau        /**
10337748cd8SNickeau         * Remove linking (not yet implemented)
10437748cd8SNickeau         */
105c3437056SNickeau        $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         */
113c3437056SNickeau        $responseAttributes->addClassName(RasterImageLink::RESPONSIVE_CLASS);
11437748cd8SNickeau
11537748cd8SNickeau
11637748cd8SNickeau        /**
1171fa8c418SNickeau         * Alt is mandatory
11837748cd8SNickeau         */
119c3437056SNickeau        $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        }
144c3437056SNickeau        $responseAttributes->addClassName($svgFunctionalClass);
14537748cd8SNickeau
14637748cd8SNickeau        /**
14737748cd8SNickeau         * Dimension are mandatory
14837748cd8SNickeau         * to avoid layout shift (CLS)
14937748cd8SNickeau         */
150c3437056SNickeau        $responseAttributes->addHtmlAttributeValue(Dimension::WIDTH_KEY, $image->getTargetWidth());
151c3437056SNickeau        $responseAttributes->addHtmlAttributeValue(Dimension::HEIGHT_KEY, $image->getTargetHeight());
152c3437056SNickeau
153c3437056SNickeau        /**
154c3437056SNickeau         * Src call
155c3437056SNickeau         */
156c3437056SNickeau        $srcValue = $image->getUrl(DokuwikiUrl::AMPERSAND_URL_ENCODED_FOR_HTML);
157c3437056SNickeau        if ($lazyLoad) {
158c3437056SNickeau
159c3437056SNickeau            /**
160c3437056SNickeau             * Note: Responsive image srcset is not needed for svg
161c3437056SNickeau             */
162c3437056SNickeau            $responseAttributes->addHtmlAttributeValue("data-src", $srcValue);
163c3437056SNickeau            $responseAttributes->addHtmlAttributeValue("src", LazyLoad::getPlaceholder(
164c3437056SNickeau                $image->getTargetWidth(),
165c3437056SNickeau                $image->getTargetHeight()
166c3437056SNickeau            ));
167c3437056SNickeau
168c3437056SNickeau        } else {
169c3437056SNickeau
170c3437056SNickeau            $responseAttributes->addHtmlAttributeValue("src", $srcValue);
171c3437056SNickeau
172c3437056SNickeau        }
173c3437056SNickeau
174c3437056SNickeau        /**
175c3437056SNickeau         * Old model where dokuwiki parses the src in handle
176c3437056SNickeau         */
177c3437056SNickeau        $responseAttributes->removeAttributeIfPresent(PagePath::PROPERTY_NAME);
17837748cd8SNickeau
17937748cd8SNickeau        /**
180*82a60d03SNickeau         * Ratio is an attribute of the request, not or rendering
181*82a60d03SNickeau         */
182*82a60d03SNickeau        $responseAttributes->removeAttributeIfPresent(Dimension::RATIO_ATTRIBUTE);
183*82a60d03SNickeau
184*82a60d03SNickeau        /**
18537748cd8SNickeau         * Return the image
18637748cd8SNickeau         */
187c3437056SNickeau        return '<img ' . $responseAttributes->toHTMLAttributeString() . '/>';
18837748cd8SNickeau
18937748cd8SNickeau    }
19037748cd8SNickeau
19137748cd8SNickeau
19237748cd8SNickeau    /**
19337748cd8SNickeau     * Render a link
19437748cd8SNickeau     * Snippet derived from {@link \Doku_Renderer_xhtml::internalmedia()}
19537748cd8SNickeau     * A media can be a video also
19637748cd8SNickeau     * @return string
19737748cd8SNickeau     */
1981fa8c418SNickeau    public function renderMediaTag(): string
19937748cd8SNickeau    {
20037748cd8SNickeau
2011fa8c418SNickeau        /**
2021fa8c418SNickeau         * @var ImageSvg $image
2031fa8c418SNickeau         */
2041fa8c418SNickeau        $image = $this->getDefaultImage();
2051fa8c418SNickeau        if ($image->exists()) {
20637748cd8SNickeau
20737748cd8SNickeau            /**
20837748cd8SNickeau             * This attributes should not be in the render
20937748cd8SNickeau             */
2101fa8c418SNickeau            $attributes = $this->getDefaultImage()->getAttributes();
2111fa8c418SNickeau            $attributes->removeComponentAttributeIfPresent(MediaLink::MEDIA_DOKUWIKI_TYPE);
2121fa8c418SNickeau            $attributes->removeComponentAttributeIfPresent(MediaLink::DOKUWIKI_SRC);
21337748cd8SNickeau            /**
21437748cd8SNickeau             * TODO: Title should be a node just below SVG
21537748cd8SNickeau             */
216c3437056SNickeau            $attributes->removeComponentAttributeIfPresent(PageTitle::PROPERTY_NAME);
21737748cd8SNickeau
218c3437056SNickeau            $imageSize = FileSystems::getSize($image->getPath());
21937748cd8SNickeau            if (
220c3437056SNickeau                $imageSize > $this->getMaxInlineSize()
22137748cd8SNickeau            ) {
22237748cd8SNickeau
22337748cd8SNickeau                /**
22437748cd8SNickeau                 * Img tag
22537748cd8SNickeau                 */
22637748cd8SNickeau                $imgHTML = $this->createImgHTMLTag();
22737748cd8SNickeau
22837748cd8SNickeau            } else {
22937748cd8SNickeau
23037748cd8SNickeau                /**
23137748cd8SNickeau                 * Svg tag
23237748cd8SNickeau                 */
233c3437056SNickeau                $imgHTML = FileSystems::getContent($image->getSvgFile());
23437748cd8SNickeau
23537748cd8SNickeau            }
23637748cd8SNickeau
23737748cd8SNickeau
23837748cd8SNickeau        } else {
23937748cd8SNickeau
24037748cd8SNickeau            $imgHTML = "<span class=\"text-danger\">The svg ($this) does not exist</span>";
24137748cd8SNickeau
24237748cd8SNickeau        }
24337748cd8SNickeau        return $imgHTML;
24437748cd8SNickeau    }
24537748cd8SNickeau
24637748cd8SNickeau    private function getMaxInlineSize()
24737748cd8SNickeau    {
24837748cd8SNickeau        return PluginUtility::getConfValue(self::CONF_MAX_KB_SIZE_FOR_INLINE_SVG, 2) * 1024;
24937748cd8SNickeau    }
25037748cd8SNickeau
25137748cd8SNickeau
25237748cd8SNickeau    public function getLazyLoad()
25337748cd8SNickeau    {
25437748cd8SNickeau        $lazyLoad = parent::getLazyLoad();
25537748cd8SNickeau        if ($lazyLoad !== null) {
25637748cd8SNickeau            return $lazyLoad;
25737748cd8SNickeau        } else {
25837748cd8SNickeau            return PluginUtility::getConfValue(SvgImageLink::CONF_LAZY_LOAD_ENABLE);
25937748cd8SNickeau        }
26037748cd8SNickeau    }
26137748cd8SNickeau
26237748cd8SNickeau
26337748cd8SNickeau}
264