xref: /plugin/combo/ComboStrap/Icon.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
151fa8c418SNickeaurequire_once(__DIR__ . '/PluginUtility.php');
1637748cd8SNickeau
1737748cd8SNickeau
1837748cd8SNickeau/**
1937748cd8SNickeau * Class IconUtility
2037748cd8SNickeau * @package ComboStrap
2137748cd8SNickeau * @see https://combostrap.com/icon
2237748cd8SNickeau *
2337748cd8SNickeau *
2437748cd8SNickeau * Material design does not have a repository structure where we can extract the location
2537748cd8SNickeau * from the name
2637748cd8SNickeau * https://material.io/resources/icons https://google.github.io/material-design-icons/
2737748cd8SNickeau *
2837748cd8SNickeau * Injection via javascript to avoid problem with the php svgsimple library
2937748cd8SNickeau * https://www.npmjs.com/package/svg-injector
3037748cd8SNickeau */
31*82a60d03SNickeauclass Icon extends ImageSvg
3237748cd8SNickeau{
3337748cd8SNickeau    const CONF_ICONS_MEDIA_NAMESPACE = "icons_namespace";
3437748cd8SNickeau    const CONF_ICONS_MEDIA_NAMESPACE_DEFAULT = ":" . PluginUtility::COMBOSTRAP_NAMESPACE_NAME . ":icons";
3537748cd8SNickeau    // Canonical name
3637748cd8SNickeau    const NAME = "icon";
3737748cd8SNickeau
3837748cd8SNickeau
3937748cd8SNickeau    const ICON_LIBRARY_URLS = array(
4037748cd8SNickeau        self::BOOTSTRAP => "https://raw.githubusercontent.com/twbs/icons/main/icons",
4137748cd8SNickeau        self::MATERIAL_DESIGN => "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg",
42c3437056SNickeau        self::FEATHER => "https://raw.githubusercontent.com/feathericons/feather/master/icons",
43*82a60d03SNickeau        self::CODE_ICON => "https://raw.githubusercontent.com/microsoft/vscode-codicons/main/src/icons",
44*82a60d03SNickeau        self::LOGOS => "https://raw.githubusercontent.com/gilbarbara/logos/master/logos",
45*82a60d03SNickeau        self::CARBON => "https://raw.githubusercontent.com/carbon-design-system/carbon/main/packages/icons/src/svg/32",
46*82a60d03SNickeau        self::TWEET_EMOJI => "https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg",
47*82a60d03SNickeau        self::ANT_DESIGN => "https://raw.githubusercontent.com/ant-design/ant-design-icons/master/packages/icons-svg/svg",
48*82a60d03SNickeau        self::FAD => "https://raw.githubusercontent.com/fefanto/fontaudio/master/svgs",
49*82a60d03SNickeau        self::CLARITY => "https://raw.githubusercontent.com/vmware/clarity-assets/master/icons/essential",
50*82a60d03SNickeau        self::OCTICON => "https://raw.githubusercontent.com/primer/octicons/main/icons"
5137748cd8SNickeau    );
5237748cd8SNickeau
5337748cd8SNickeau    const ICON_LIBRARY_WEBSITE_URLS = array(
5437748cd8SNickeau        self::BOOTSTRAP => "https://icons.getbootstrap.com/",
5537748cd8SNickeau        self::MATERIAL_DESIGN => "https://materialdesignicons.com/",
56c3437056SNickeau        self::FEATHER => "https://feathericons.com/",
57c3437056SNickeau        self::CODE_ICON => "https://microsoft.github.io/vscode-codicons/",
58c3437056SNickeau        self::LOGOS => "https://svgporn.com/",
59*82a60d03SNickeau        self::CARBON => "https://www.carbondesignsystem.com/guidelines/icons/library/",
60*82a60d03SNickeau        self::TWEET_EMOJI => "https://twemoji.twitter.com/",
61*82a60d03SNickeau        self::ANT_DESIGN => "https://ant.design/components/icon/",
62*82a60d03SNickeau        self::CLARITY => "https://clarity.design/foundation/icons/",
63*82a60d03SNickeau        self::OCTICON => "https://primer.style/octicons/"
6437748cd8SNickeau    );
6537748cd8SNickeau
6637748cd8SNickeau    const CONF_DEFAULT_ICON_LIBRARY = "defaultIconLibrary";
67c3437056SNickeau    const CONF_DEFAULT_ICON_LIBRARY_DEFAULT = self::MATERIAL_DESIGN_ACRONYM;
68c3437056SNickeau
69c3437056SNickeau    /**
70c3437056SNickeau     * Deprecated library acronym / name
71c3437056SNickeau     */
72c3437056SNickeau    const DEPRECATED_LIBRARY_ACRONYM = array(
73c3437056SNickeau        "bs" => self::BOOTSTRAP, // old one (deprecated) - the good acronym is bi (seen also in the class)
74c3437056SNickeau        "md" => self::MATERIAL_DESIGN
7537748cd8SNickeau    );
76c3437056SNickeau
77c3437056SNickeau    /**
78c3437056SNickeau     * Public known acronym / name (Used in the configuration)
79c3437056SNickeau     */
80c3437056SNickeau    const PUBLIC_LIBRARY_ACRONYM = array(
81c3437056SNickeau        "bi" => self::BOOTSTRAP,
82c3437056SNickeau        self::MATERIAL_DESIGN_ACRONYM => self::MATERIAL_DESIGN,
83c3437056SNickeau        "fe" => self::FEATHER,
84c3437056SNickeau        "codicon" => self::CODE_ICON,
85c3437056SNickeau        "logos" => self::LOGOS,
86*82a60d03SNickeau        "carbon" => self::CARBON,
87*82a60d03SNickeau        "twemoji" => self::TWEET_EMOJI,
88*82a60d03SNickeau        "ant-design" => self::ANT_DESIGN,
89*82a60d03SNickeau        "fad" => self::FAD,
90*82a60d03SNickeau        "clarity" => self::CLARITY,
91*82a60d03SNickeau        "octicon" => self::OCTICON
92c3437056SNickeau    );
93c3437056SNickeau
9437748cd8SNickeau    const FEATHER = "feather";
9537748cd8SNickeau    const BOOTSTRAP = "bootstrap";
9637748cd8SNickeau    const MATERIAL_DESIGN = "material-design";
97c3437056SNickeau    const CODE_ICON = "codicon";
98c3437056SNickeau    const LOGOS = "logos";
99c3437056SNickeau    const CARBON = "carbon";
100c3437056SNickeau    const MATERIAL_DESIGN_ACRONYM = "mdi";
101*82a60d03SNickeau    const TWEET_EMOJI = "twemoji";
102*82a60d03SNickeau    const ANT_DESIGN = "ant-design";
103*82a60d03SNickeau    const FAD = "fad";
104*82a60d03SNickeau    const CLARITY = "clarity";
105*82a60d03SNickeau    const OCTICON = "octicon";
10637748cd8SNickeau
10737748cd8SNickeau
10837748cd8SNickeau    /**
10937748cd8SNickeau     * The function used to render an icon
11037748cd8SNickeau     * @param TagAttributes $tagAttributes -  the icon attributes
111*82a60d03SNickeau     * @return Icon
112*82a60d03SNickeau     * @throws ExceptionCombo
11337748cd8SNickeau     */
114*82a60d03SNickeau    static public function create(TagAttributes $tagAttributes): Icon
11537748cd8SNickeau    {
11637748cd8SNickeau
11737748cd8SNickeau
11837748cd8SNickeau        $name = "name";
11937748cd8SNickeau        if (!$tagAttributes->hasComponentAttribute($name)) {
120*82a60d03SNickeau            throw new ExceptionCombo("The attributes should have a name. It's mandatory for an icon.", self::NAME);
12137748cd8SNickeau        }
12237748cd8SNickeau
12337748cd8SNickeau        /**
12437748cd8SNickeau         * The Name
12537748cd8SNickeau         */
12637748cd8SNickeau        $iconNameAttribute = $tagAttributes->getValue($name);
12737748cd8SNickeau
12837748cd8SNickeau        /**
12937748cd8SNickeau         * If the name have an extension, it's a file from the media directory
13037748cd8SNickeau         * Otherwise, it's an icon from a library
13137748cd8SNickeau         */
13237748cd8SNickeau        $mediaDokuPath = DokuPath::createMediaPathFromId($iconNameAttribute);
13337748cd8SNickeau        if (!empty($mediaDokuPath->getExtension())) {
13437748cd8SNickeau
13537748cd8SNickeau            // loop through candidates until a match was found:
13637748cd8SNickeau            // May be an icon from the templates
137c3437056SNickeau            if (!FileSystems::exists($mediaDokuPath)) {
13837748cd8SNickeau
13937748cd8SNickeau                // Trying to see if it's not in the template images directory
14037748cd8SNickeau                $message = "The media file could not be found in the media library. If you want an icon from an icon library, indicate a name without extension.";
14137748cd8SNickeau                $message .= "<BR> Media File Library tested: $mediaDokuPath";
142*82a60d03SNickeau                throw new ExceptionCombo($message, self::NAME);
143*82a60d03SNickeau
14437748cd8SNickeau
14537748cd8SNickeau            }
14637748cd8SNickeau
14737748cd8SNickeau        } else {
14837748cd8SNickeau
14937748cd8SNickeau            // It may be a icon already downloaded
150c3437056SNickeau            $iconNameSpace = PluginUtility::getConfValue(self::CONF_ICONS_MEDIA_NAMESPACE, self::CONF_ICONS_MEDIA_NAMESPACE_DEFAULT);
15137748cd8SNickeau            if (substr($iconNameSpace, 0, 1) != DokuPath::PATH_SEPARATOR) {
15237748cd8SNickeau                $iconNameSpace = DokuPath::PATH_SEPARATOR . $iconNameSpace;
15337748cd8SNickeau            }
15437748cd8SNickeau            if (substr($iconNameSpace, -1) != DokuPath::PATH_SEPARATOR) {
15537748cd8SNickeau                $iconNameSpace = $iconNameSpace . ":";
15637748cd8SNickeau            }
15737748cd8SNickeau            $mediaPathId = $iconNameSpace . $iconNameAttribute . ".svg";
15837748cd8SNickeau            $mediaDokuPath = DokuPath::createMediaPathFromAbsolutePath($mediaPathId);
15937748cd8SNickeau
16037748cd8SNickeau            // Bug: null file created when the stream could not get any byte
16137748cd8SNickeau            // We delete them
162c3437056SNickeau            if (FileSystems::exists($mediaDokuPath)) {
163c3437056SNickeau                if (FileSystems::getSize($mediaDokuPath) == 0) {
164c3437056SNickeau                    FileSystems::delete($mediaDokuPath);
16537748cd8SNickeau                }
16637748cd8SNickeau            }
16737748cd8SNickeau
168c3437056SNickeau            if (!FileSystems::exists($mediaDokuPath)) {
16937748cd8SNickeau
17037748cd8SNickeau                /**
17137748cd8SNickeau                 * Download the icon
17237748cd8SNickeau                 */
17337748cd8SNickeau
17437748cd8SNickeau                // Create the target directory if it does not exist
17537748cd8SNickeau                $iconDir = $mediaDokuPath->getParent();
176c3437056SNickeau                if (!FileSystems::exists($iconDir)) {
177c3437056SNickeau                    try {
178c3437056SNickeau                        FileSystems::createDirectory($iconDir);
179c3437056SNickeau                    } catch (ExceptionCombo $e) {
180*82a60d03SNickeau                        throw new ExceptionCombo("The icon directory ($iconDir) could not be created.", self::NAME, 0, $e);
18137748cd8SNickeau                    }
18237748cd8SNickeau                }
18337748cd8SNickeau
18437748cd8SNickeau                // Name parsing to extract the library name and icon name
18537748cd8SNickeau                $sepPosition = strpos($iconNameAttribute, ":");
186c3437056SNickeau                $library = PluginUtility::getConfValue(self::CONF_DEFAULT_ICON_LIBRARY, self::CONF_DEFAULT_ICON_LIBRARY_DEFAULT);
18737748cd8SNickeau                $iconName = $iconNameAttribute;
18837748cd8SNickeau                if ($sepPosition != false) {
18937748cd8SNickeau                    $library = substr($iconNameAttribute, 0, $sepPosition);
19037748cd8SNickeau                    $iconName = substr($iconNameAttribute, $sepPosition + 1);
19137748cd8SNickeau                }
19237748cd8SNickeau
19337748cd8SNickeau                // Get the qualified library name
194c3437056SNickeau                $acronymLibraries = self::getLibraries();
19537748cd8SNickeau                if (isset($acronymLibraries[$library])) {
19637748cd8SNickeau                    $library = $acronymLibraries[$library];
19737748cd8SNickeau                }
19837748cd8SNickeau
19937748cd8SNickeau                // Get the url
20037748cd8SNickeau                $iconLibraries = self::ICON_LIBRARY_URLS;
20137748cd8SNickeau                if (!isset($iconLibraries[$library])) {
202*82a60d03SNickeau                    throw new ExceptionCombo("The icon library ($library) is unknown. The icon could not be downloaded.", self::NAME);
20337748cd8SNickeau                } else {
20437748cd8SNickeau                    $iconBaseUrl = $iconLibraries[$library];
20537748cd8SNickeau                }
20637748cd8SNickeau
207*82a60d03SNickeau                /**
208*82a60d03SNickeau                 * Name processing
209*82a60d03SNickeau                 */
210*82a60d03SNickeau                switch ($library) {
211*82a60d03SNickeau
212*82a60d03SNickeau                    case self::TWEET_EMOJI:
213*82a60d03SNickeau                        try {
214*82a60d03SNickeau                            $iconName = self::getEmojiCodePoint($iconName);
215*82a60d03SNickeau                        } catch (ExceptionCombo $e) {
216*82a60d03SNickeau                            throw new ExceptionCombo("The emoji name $iconName is unknown. The emoji could not be downloaded.", self::NAME, 0, $e);
217*82a60d03SNickeau                        }
218*82a60d03SNickeau                        break;
219*82a60d03SNickeau                    case self::ANT_DESIGN:
220*82a60d03SNickeau                        // table-outlined where table is the svg, outlined the category
221*82a60d03SNickeau                        // ordered-list-outlined where ordered-list is the svg, outlined the category
222*82a60d03SNickeau                        $iconProcessed = $iconName;
223*82a60d03SNickeau                        $index = strrpos($iconProcessed, "-");
224*82a60d03SNickeau                        if ($index === false) {
225*82a60d03SNickeau                            throw new ExceptionCombo ("We expect that a ant design icon name ($iconName) has two parts separated by a `-` (example: table-outlined). The icon could not be downloaded.", self::NAME);
226*82a60d03SNickeau                        }
227*82a60d03SNickeau                        $iconName = substr($iconProcessed, 0, $index);
228*82a60d03SNickeau                        $iconType = substr($iconProcessed, $index + 1);
229*82a60d03SNickeau                        $iconBaseUrl .= "/$iconType";
230*82a60d03SNickeau                        break;
231*82a60d03SNickeau                    case self::CARBON:
232*82a60d03SNickeau                        $iconName = self::getCarbonPhysicalName($iconName);
233*82a60d03SNickeau                        break;
234*82a60d03SNickeau                    case self::FAD:
235*82a60d03SNickeau                        $iconName = self::getFadPhysicalName($iconName);
236*82a60d03SNickeau                }
237*82a60d03SNickeau
238*82a60d03SNickeau
23937748cd8SNickeau                // The url
24037748cd8SNickeau                $downloadUrl = "$iconBaseUrl/$iconName.svg";
24137748cd8SNickeau                $filePointer = @fopen($downloadUrl, 'r');
24237748cd8SNickeau                if ($filePointer != false) {
24337748cd8SNickeau
244c3437056SNickeau                    $numberOfByte = @file_put_contents($mediaDokuPath->toLocalPath()->toAbsolutePath()->toString(), $filePointer);
24537748cd8SNickeau                    if ($numberOfByte != false) {
24637748cd8SNickeau                        LogUtility::msg("The icon ($iconName) from the library ($library) was downloaded to ($mediaPathId)", LogUtility::LVL_MSG_INFO, self::NAME);
24737748cd8SNickeau                    } else {
24837748cd8SNickeau                        LogUtility::msg("Internal error: The icon ($iconName) from the library ($library) could no be written to ($mediaPathId)", LogUtility::LVL_MSG_ERROR, self::NAME);
24937748cd8SNickeau                    }
25037748cd8SNickeau
25137748cd8SNickeau                } else {
25237748cd8SNickeau
25337748cd8SNickeau                    // (ie no icon file found at ($downloadUrl)
25437748cd8SNickeau                    $urlLibrary = self::ICON_LIBRARY_WEBSITE_URLS[$library];
25537748cd8SNickeau                    LogUtility::msg("The library (<a href=\"$urlLibrary\">$library</a>) does not have a icon (<a href=\"$downloadUrl\">$iconName</a>).", LogUtility::LVL_MSG_ERROR, self::NAME);
25637748cd8SNickeau
25737748cd8SNickeau                }
25837748cd8SNickeau
25937748cd8SNickeau            }
26037748cd8SNickeau
26137748cd8SNickeau        }
26237748cd8SNickeau
26337748cd8SNickeau        /**
26437748cd8SNickeau         * After optimization, the width and height of the svg are gone
26537748cd8SNickeau         * but the icon type set them again
26637748cd8SNickeau         *
26737748cd8SNickeau         * The icon type is used to set:
26837748cd8SNickeau         *   * the default dimension
26937748cd8SNickeau         *   * color styling
27037748cd8SNickeau         *   * disable the responsive properties
27137748cd8SNickeau         *
27237748cd8SNickeau         */
273*82a60d03SNickeau        $tagAttributes->addComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ICON_TYPE);
27437748cd8SNickeau
275*82a60d03SNickeau        return new Icon($mediaDokuPath, $tagAttributes);
27637748cd8SNickeau
27737748cd8SNickeau    }
27837748cd8SNickeau
27937748cd8SNickeau    /**
28037748cd8SNickeau     * @param $iconName
28137748cd8SNickeau     * @param $mediaFilePath
28237748cd8SNickeau     * @deprecated Old code to download icon from the material design api
28337748cd8SNickeau     */
28437748cd8SNickeau    public
28537748cd8SNickeau    static function downloadIconFromMaterialDesignApi($iconName, $mediaFilePath)
28637748cd8SNickeau    {
28737748cd8SNickeau        // Try the official API
28837748cd8SNickeau        // Read the icon meta of
28937748cd8SNickeau        // Meta Json file got all icons
29037748cd8SNickeau        //
29137748cd8SNickeau        //   * Available at: https://raw.githubusercontent.com/Templarian/MaterialDesign/master/meta.json
29237748cd8SNickeau        //   * See doc: https://github.com/Templarian/MaterialDesign-Site/blob/master/src/content/api.md)
29337748cd8SNickeau        $arrayFormat = true;
294*82a60d03SNickeau        $iconMetaJson = json_decode(file_get_contents(__DIR__ . '/../resources/dictionary/icon-meta.json'), $arrayFormat);
29537748cd8SNickeau        $iconId = null;
29637748cd8SNickeau        foreach ($iconMetaJson as $key => $value) {
29737748cd8SNickeau            if ($value['name'] == $iconName) {
29837748cd8SNickeau                $iconId = $value['id'];
29937748cd8SNickeau                break;
30037748cd8SNickeau            }
30137748cd8SNickeau        }
30237748cd8SNickeau        if ($iconId != null) {
30337748cd8SNickeau
30437748cd8SNickeau            // Download
30537748cd8SNickeau            // Call to the API
30637748cd8SNickeau            // https://dev.materialdesignicons.com/contribute/site/api
30737748cd8SNickeau            $downloadUrl = "https://materialdesignicons.com/api/download/icon/svg/$iconId";
30837748cd8SNickeau            $filePointer = file_put_contents($mediaFilePath, fopen($downloadUrl, 'r'));
30937748cd8SNickeau            if ($filePointer == false) {
31037748cd8SNickeau                LogUtility::msg("The file ($downloadUrl) could not be downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_ERROR, self::NAME);
31137748cd8SNickeau            } else {
31237748cd8SNickeau                LogUtility::msg("The material design icon ($iconName) was downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_INFO, self::NAME);
31337748cd8SNickeau            }
31437748cd8SNickeau
31537748cd8SNickeau        }
31637748cd8SNickeau
31737748cd8SNickeau    }
31837748cd8SNickeau
319*82a60d03SNickeau    private static function getLibraries(): array
320c3437056SNickeau    {
321*82a60d03SNickeau        return array_merge(
322*82a60d03SNickeau            self::PUBLIC_LIBRARY_ACRONYM,
323*82a60d03SNickeau            self::DEPRECATED_LIBRARY_ACRONYM
324*82a60d03SNickeau        );
325*82a60d03SNickeau    }
326*82a60d03SNickeau
327*82a60d03SNickeau    /**
328*82a60d03SNickeau     * @throws ExceptionCombo
329*82a60d03SNickeau     */
330*82a60d03SNickeau    public static function getEmojiCodePoint(string $emojiName)
331*82a60d03SNickeau    {
332*82a60d03SNickeau        $path = LocalPath::createFromPath(Resources::getDictionaryDirectory() . "/emojis.json");
333*82a60d03SNickeau        $jsonContent = FileSystems::getContent($path);
334*82a60d03SNickeau        $jsonArray = Json::createFromString($jsonContent)->toArray();
335*82a60d03SNickeau        return $jsonArray[$emojiName];
336*82a60d03SNickeau    }
337*82a60d03SNickeau
338*82a60d03SNickeau    /**
339*82a60d03SNickeau     * Iconify normalized the name of the carbon library (making them lowercase)
340*82a60d03SNickeau     *
341*82a60d03SNickeau     * For instance, CSV is csv (https://icon-sets.iconify.design/carbon/csv/)
342*82a60d03SNickeau     *
343*82a60d03SNickeau     * This dictionary reproduce it.
344*82a60d03SNickeau     *
345*82a60d03SNickeau     * @param string $logicalName
346*82a60d03SNickeau     * @return mixed
347*82a60d03SNickeau     * @throws ExceptionCombo
348*82a60d03SNickeau     */
349*82a60d03SNickeau    private static function getCarbonPhysicalName(string $logicalName)
350*82a60d03SNickeau    {
351*82a60d03SNickeau        $path = LocalPath::createFromPath(Resources::getDictionaryDirectory() . "/carbon-icons.json");
352*82a60d03SNickeau        $jsonContent = FileSystems::getContent($path);
353*82a60d03SNickeau        $jsonArray = Json::createFromString($jsonContent)->toArray();
354*82a60d03SNickeau        $physicalName = $jsonArray[$logicalName];
355*82a60d03SNickeau        if ($physicalName === null) {
356*82a60d03SNickeau            LogUtility::msg("The icon ($logicalName) is unknown as 32x32 carbon icon");
357*82a60d03SNickeau            // by default, just lowercase
358*82a60d03SNickeau            return lower($logicalName);
359*82a60d03SNickeau        }
360*82a60d03SNickeau        return $physicalName;
361*82a60d03SNickeau    }
362*82a60d03SNickeau
363*82a60d03SNickeau    /**
364*82a60d03SNickeau     * @throws ExceptionCombo
365*82a60d03SNickeau     */
366*82a60d03SNickeau    private static function getFadPhysicalName($logicalName)
367*82a60d03SNickeau    {
368*82a60d03SNickeau        $path = LocalPath::createFromPath(Resources::getDictionaryDirectory() . "/fad-icons.json");
369*82a60d03SNickeau        $jsonContent = FileSystems::getContent($path);
370*82a60d03SNickeau        $jsonArray = Json::createFromString($jsonContent)->toArray();
371*82a60d03SNickeau        $physicalName = $jsonArray[$logicalName];
372*82a60d03SNickeau        if ($physicalName === null) {
373*82a60d03SNickeau            LogUtility::msg("The icon ($logicalName) is unknown as fad icon");
374*82a60d03SNickeau            return $logicalName;
375*82a60d03SNickeau        }
376*82a60d03SNickeau        return $physicalName;
377*82a60d03SNickeau    }
378*82a60d03SNickeau
379*82a60d03SNickeau
380*82a60d03SNickeau    public function render(): string
381*82a60d03SNickeau    {
382*82a60d03SNickeau
383*82a60d03SNickeau        if (FileSystems::exists($this->getPath())) {
384*82a60d03SNickeau
385*82a60d03SNickeau            $svgImageLink = SvgImageLink::createMediaLinkFromPath(
386*82a60d03SNickeau                $this->getPath(),
387*82a60d03SNickeau                $this->getAttributes()
388*82a60d03SNickeau            );
389*82a60d03SNickeau            return $svgImageLink->renderMediaTag();
390*82a60d03SNickeau
391*82a60d03SNickeau        } else {
392*82a60d03SNickeau
393*82a60d03SNickeau            LogUtility::msg("The icon ($this) does not exist and cannot be rendered.");
394*82a60d03SNickeau            return "";
395*82a60d03SNickeau
396*82a60d03SNickeau        }
397*82a60d03SNickeau
398c3437056SNickeau    }
399c3437056SNickeau
40037748cd8SNickeau
40137748cd8SNickeau}
402