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
15require_once(__DIR__ . '/PluginUtility.php');
16
17
18/**
19 * Class IconUtility
20 * @package ComboStrap
21 * @see https://combostrap.com/icon
22 *
23 *
24 * Material design does not have a repository structure where we can extract the location
25 * from the name
26 * https://material.io/resources/icons https://google.github.io/material-design-icons/
27 *
28 * Injection via javascript to avoid problem with the php svgsimple library
29 * https://www.npmjs.com/package/svg-injector
30 */
31class Icon extends ImageSvg
32{
33    const CONF_ICONS_MEDIA_NAMESPACE = "icons_namespace";
34    const CONF_ICONS_MEDIA_NAMESPACE_DEFAULT = ":" . PluginUtility::COMBOSTRAP_NAMESPACE_NAME . ":icons";
35    // Canonical name
36    const ICON_CANONICAL_NAME = "icon";
37
38
39    const ICON_LIBRARY_URLS = array(
40        self::ANT_DESIGN => "https://raw.githubusercontent.com/ant-design/ant-design-icons/master/packages/icons-svg/svg",
41        self::BOOTSTRAP => "https://raw.githubusercontent.com/twbs/icons/main/icons",
42        self::CARBON => "https://raw.githubusercontent.com/carbon-design-system/carbon/main/packages/icons/src/svg/32",
43        self::CLARITY => "https://raw.githubusercontent.com/vmware/clarity-assets/master/icons/essential",
44        self::CODE_ICON => "https://raw.githubusercontent.com/microsoft/vscode-codicons/main/src/icons",
45        self::ELEGANT_THEME => "https://raw.githubusercontent.com/pprince/etlinefont-bower/master/images/svg/individual_icons",
46        self::ENTYPO => "https://raw.githubusercontent.com/hypermodules/entypo/master/src/Entypo",
47        self::ENTYPO_SOCIAL => "https://raw.githubusercontent.com/hypermodules/entypo/master/src/Entypo%20Social%20Extension",
48        self::EVA => "https://raw.githubusercontent.com/akveo/eva-icons/master/package/icons",
49        self::FEATHER => "https://raw.githubusercontent.com/feathericons/feather/master/icons",
50        self::FAD => "https://raw.githubusercontent.com/fefanto/fontaudio/master/svgs",
51        self::ICONSCOUT => "https://raw.githubusercontent.com/Iconscout/unicons/master/svg/line",
52        self::LOGOS => "https://raw.githubusercontent.com/gilbarbara/logos/master/logos",
53        self::MATERIAL_DESIGN => "https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg",
54        self::OCTICON => "https://raw.githubusercontent.com/primer/octicons/main/icons",
55        self::TWEET_EMOJI => "https://raw.githubusercontent.com/twitter/twemoji/master/assets/svg",
56        self::SIMPLE_LINE => "https://raw.githubusercontent.com/thesabbir/simple-line-icons/master/src/svgs",
57        self::ICOMOON => "https://raw.githubusercontent.com/Keyamoon/IcoMoon-Free/master/SVG",
58        self::DASHICONS => "https://raw.githubusercontent.com/WordPress/dashicons/master/svg-min",
59        self::ICONOIR => "https://raw.githubusercontent.com/lucaburgio/iconoir/master/icons",
60        self::BOX_ICON => "https://raw.githubusercontent.com/atisawd/boxicons/master/svg",
61        self::LINE_AWESOME => "https://raw.githubusercontent.com/icons8/line-awesome/master/svg",
62        self::FONT_AWESOME_SOLID => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/solid",
63        self::FONT_AWESOME_BRANDS => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/brands",
64        self::FONT_AWESOME_REGULAR => "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/regular",
65        self::VAADIN => "https://raw.githubusercontent.com/vaadin/vaadin-icons/master/assets/svg",
66        self::CORE_UI_BRAND => "https://raw.githubusercontent.com/coreui/coreui-icons/master/svg/brand",
67        self::FLAT_COLOR_ICON => "https://raw.githubusercontent.com/icons8/flat-color-icons/master/svg",
68        self::PHOSPHOR_ICONS => "https://raw.githubusercontent.com/phosphor-icons/phosphor-icons/master/assets",
69        self::VSCODE => "https://raw.githubusercontent.com/vscode-icons/vscode-icons/master/icons",
70        self::SI_GLYPH => "https://raw.githubusercontent.com/frexy/glyph-iconset/master/svg",
71        self::AKAR_ICONS => "https://raw.githubusercontent.com/artcoholic/akar-icons/master/src/svg"
72    );
73
74    const ICON_LIBRARY_WEBSITE_URLS = array(
75        self::BOOTSTRAP => "https://icons.getbootstrap.com/",
76        self::MATERIAL_DESIGN => "https://materialdesignicons.com/",
77        self::FEATHER => "https://feathericons.com/",
78        self::CODE_ICON => "https://microsoft.github.io/vscode-codicons/",
79        self::LOGOS => "https://svgporn.com/",
80        self::CARBON => "https://www.carbondesignsystem.com/guidelines/icons/library/",
81        self::TWEET_EMOJI => "https://twemoji.twitter.com/",
82        self::ANT_DESIGN => "https://ant.design/components/icon/",
83        self::CLARITY => "https://clarity.design/foundation/icons/",
84        self::OCTICON => "https://primer.style/octicons/",
85        self::ICONSCOUT => "https://iconscout.com/unicons/explore/line",
86        self::ELEGANT_THEME => "https://github.com/pprince/etlinefont-bower",
87        self::EVA => "https://akveo.github.io/eva-icons/",
88        self::ENTYPO_SOCIAL => "http://www.entypo.com",
89        self::ENTYPO => "http://www.entypo.com",
90        self::SIMPLE_LINE => "https://thesabbir.github.io/simple-line-icons",
91        self::ICOMOON => "https://icomoon.io/",
92        self::DASHICONS => "https://developer.wordpress.org/resource/dashicons/",
93        self::ICONOIR => "https://iconoir.com",
94        self::BOX_ICON => "https://boxicons.com",
95        self::LINE_AWESOME => "https://icons8.com/line-awesome",
96        self::FONT_AWESOME => "https://fontawesome.com/",
97        self::FONT_AWESOME_SOLID => "https://fontawesome.com/",
98        self::FONT_AWESOME_BRANDS => "https://fontawesome.com/",
99        self::FONT_AWESOME_REGULAR => "https://fontawesome.com/",
100        self::VAADIN => "https://vaadin.com/icons",
101        self::CORE_UI_BRAND => "https://coreui.io/icons/",
102        self::FLAT_COLOR_ICON => "https://icons8.com/icons/color",
103        self::PHOSPHOR_ICONS => "https://phosphoricons.com/",
104        self::VSCODE => "https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons",
105        self::SI_GLYPH => "https://glyph.smarticons.co/",
106        self::AKAR_ICONS => "https://akaricons.com/"
107
108    );
109
110    const CONF_DEFAULT_ICON_LIBRARY = "defaultIconLibrary";
111    const CONF_DEFAULT_ICON_LIBRARY_DEFAULT = self::MATERIAL_DESIGN_ACRONYM;
112
113    /**
114     * Deprecated library acronym / name
115     */
116    const DEPRECATED_LIBRARY_ACRONYM = array(
117        "bs" => self::BOOTSTRAP, // old one (deprecated) - the good acronym is bi (seen also in the class)
118        "md" => self::MATERIAL_DESIGN
119    );
120
121    /**
122     * Public known acronym / name (Used in the configuration)
123     */
124    const PUBLIC_LIBRARY_ACRONYM = array(
125        "bi" => self::BOOTSTRAP,
126        self::MATERIAL_DESIGN_ACRONYM => self::MATERIAL_DESIGN,
127        "fe" => self::FEATHER,
128        "codicon" => self::CODE_ICON,
129        "logos" => self::LOGOS,
130        "carbon" => self::CARBON,
131        "twemoji" => self::TWEET_EMOJI,
132        "ant-design" => self::ANT_DESIGN,
133        "fad" => self::FAD,
134        "clarity" => self::CLARITY,
135        "octicon" => self::OCTICON,
136        "uit" => self::ICONSCOUT,
137        "et" => self::ELEGANT_THEME,
138        "eva" => self::EVA,
139        "entypo-social" => self::ENTYPO_SOCIAL,
140        "entypo" => self::ENTYPO,
141        "simple-line-icons" => self::SIMPLE_LINE,
142        "icomoon-free" => self::ICOMOON,
143        "dashicons" => self::DASHICONS,
144        "iconoir" => self::ICONOIR,
145        "bx" => self::BOX_ICON,
146        "la" => self::LINE_AWESOME,
147        "fa-solid" => self::FONT_AWESOME_SOLID,
148        "fa-brands" => self::FONT_AWESOME_BRANDS,
149        "fa-regular" => self::FONT_AWESOME_REGULAR,
150        "vaadin" => self::VAADIN,
151        "cib" => self::CORE_UI_BRAND,
152        "flat-color-icons" => self::FLAT_COLOR_ICON,
153        "ph" => self::PHOSPHOR_ICONS,
154        "vscode-icons" => self::VSCODE,
155        "si-glyph" => self::SI_GLYPH,
156        "akar-icons" => self::AKAR_ICONS
157    );
158
159    const FEATHER = "feather";
160    const BOOTSTRAP = "bootstrap";
161    const MATERIAL_DESIGN = "material-design";
162    const CODE_ICON = "codicon";
163    const LOGOS = "logos";
164    const CARBON = "carbon";
165    const MATERIAL_DESIGN_ACRONYM = "mdi";
166    const TWEET_EMOJI = "twemoji";
167    const ANT_DESIGN = "ant-design";
168    const FAD = "fad";
169    const CLARITY = "clarity";
170    const OCTICON = "octicon";
171    const ICONSCOUT = "iconscout";
172    const ELEGANT_THEME = "elegant-theme";
173    const EVA = "eva";
174    const ENTYPO_SOCIAL = "entypo-social";
175    const ENTYPO = "entypo";
176    const SIMPLE_LINE = "simple-line";
177    const ICOMOON = "icomoon";
178    const DASHICONS = " dashicons";
179    const ICONOIR = "iconoir";
180    const BOX_ICON = "box-icon";
181    const LINE_AWESOME = "line-awesome";
182    const FONT_AWESOME_SOLID = "font-awesome-solid";
183    const FONT_AWESOME_BRANDS = "font-awesome-brands";
184    const FONT_AWESOME_REGULAR = "font-awesome-regular";
185    const FONT_AWESOME = "font-awesome";
186    const VAADIN = "vaadin";
187    const CORE_UI_BRAND = "cib";
188    const FLAT_COLOR_ICON = "flat-color-icons";
189    const PHOSPHOR_ICONS = "ph";
190    const VSCODE = "vscode";
191    const SI_GLYPH = "si-glyph";
192    const COMBO = DokuPath::COMBO_DRIVE;
193    const AKAR_ICONS = "akar-icons";
194
195
196    private $fullQualifiedName;
197    /**
198     * The icon library
199     * @var mixed|null
200     */
201    private $library;
202    /**
203     * @var false|string
204     */
205    private $iconName;
206
207    /**
208     * Icon constructor.
209     * @throws ExceptionCombo
210     * @var string $fullQualifiedName - generally a short icon name (but it may be media id)
211     */
212    public function __construct($fullQualifiedName, $tagAttributes = null)
213    {
214
215        $this->fullQualifiedName = $fullQualifiedName;
216
217        /**
218         * After optimization, the width and height of the svg are gone
219         * but the icon type set them again
220         *
221         * The icon type is used to set:
222         *   * the default dimension
223         *   * color styling
224         *   * disable the responsive properties
225         *
226         */
227        if ($tagAttributes === null) {
228            $tagAttributes = TagAttributes::createEmpty();
229        }
230        $tagAttributes->addComponentAttributeValue(TagAttributes::TYPE_KEY, SvgDocument::ICON_TYPE);
231
232        /**
233         * If the name have an extension, it's a file from the media directory
234         * Otherwise, it's an icon from a library
235         */
236        $mediaDokuPath = DokuPath::createFromUnknownRoot($fullQualifiedName);
237        if (!empty($mediaDokuPath->getExtension())) {
238
239            // loop through candidates until a match was found:
240            // May be an icon from the templates
241            if (!FileSystems::exists($mediaDokuPath)) {
242
243                // Trying to see if it's not in the template images directory
244                $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.";
245                $message .= "<BR> Media File Library tested: $mediaDokuPath";
246                throw new ExceptionCombo($message, self::ICON_CANONICAL_NAME);
247
248
249            }
250
251            parent::__construct($mediaDokuPath, $tagAttributes);
252            return;
253
254        }
255
256
257        /**
258         * Resource icon library
259         * {@link Icon::createFromComboResource()}
260         */
261        if (strpos($fullQualifiedName, self::COMBO) === 0) {
262            $iconName = str_replace(self::COMBO . ":", "", $fullQualifiedName);
263            // the icon name is not to be found in the images directory (there is also brand)
264            // but can be anywhere below the resources directory
265            $mediaDokuPath = DokuPath::createComboResource("$iconName.svg");
266        } else {
267            /**
268             * From an icon library
269             */
270            $iconNameSpace = PluginUtility::getConfValue(self::CONF_ICONS_MEDIA_NAMESPACE, self::CONF_ICONS_MEDIA_NAMESPACE_DEFAULT);
271            if (substr($iconNameSpace, 0, 1) != DokuPath::PATH_SEPARATOR) {
272                $iconNameSpace = DokuPath::PATH_SEPARATOR . $iconNameSpace;
273            }
274            if (substr($iconNameSpace, -1) != DokuPath::PATH_SEPARATOR) {
275                $iconNameSpace = $iconNameSpace . ":";
276            }
277
278            $mediaPathId = $iconNameSpace . $fullQualifiedName . ".svg";
279            $mediaDokuPath = DokuPath::createMediaPathFromAbsolutePath($mediaPathId);
280        }
281
282
283        // Bug: null file created when the stream could not get any byte
284        // We delete them
285        if (FileSystems::exists($mediaDokuPath)) {
286            if (FileSystems::getSize($mediaDokuPath) == 0) {
287                FileSystems::delete($mediaDokuPath);
288            }
289        }
290
291        /**
292         * Name parsing to extract the library name and icon name
293         */
294        // default
295        $this->library = PluginUtility::getConfValue(self::CONF_DEFAULT_ICON_LIBRARY, self::CONF_DEFAULT_ICON_LIBRARY_DEFAULT);
296        $this->iconName = $this->fullQualifiedName;
297        // parse
298        $sepPosition = strpos($this->fullQualifiedName, ":");
299        if ($sepPosition != false) {
300            $this->library = substr($this->fullQualifiedName, 0, $sepPosition);
301            $this->iconName = substr($this->fullQualifiedName, $sepPosition + 1);
302        }
303
304        parent::__construct($mediaDokuPath, $tagAttributes);
305
306    }
307
308
309    /**
310     * The function used to render an icon
311     * @param string $name - icon name
312     * @param TagAttributes|null $tagAttributes -  the icon attributes
313     * @return Icon
314     * @throws ExceptionCombo
315     */
316    static public function create(string $name, TagAttributes $tagAttributes = null): Icon
317    {
318
319        return new Icon($name, $tagAttributes);
320
321    }
322
323    /**
324     * @throws ExceptionCombo
325     */
326    public static function createFromComboResource(string $name, TagAttributes $tagAttributes = null): Icon
327    {
328        return self::create(self::COMBO . ":$name", $tagAttributes);
329    }
330
331    /**
332     * @throws ExceptionCombo
333     */
334    private static function getPhysicalNameFromDictionary(string $logicalName, string $library)
335    {
336
337        $jsonArray = Dictionary::getFrom("$library-icons");
338        $physicalName = $jsonArray[$logicalName];
339        if ($physicalName === null) {
340            LogUtility::msg("The icon ($logicalName) is unknown for the library ($library)");
341            // by default, just lowercase
342            return strtolower($logicalName);
343        }
344        return $physicalName;
345
346    }
347
348    public function getFullQualifiedName(): string
349    {
350        return $this->fullQualifiedName;
351    }
352
353    /**
354     * @throws ExceptionCombo
355     */
356    public function getDownloadUrl(): string
357    {
358
359
360        // Get the qualified library name
361        $library = $this->library;
362        $acronymLibraries = self::getLibraries();
363        if (isset($acronymLibraries[$library])) {
364            $library = $acronymLibraries[$library];
365        }
366
367        // Get the url
368        $iconLibraries = self::ICON_LIBRARY_URLS;
369        if (!isset($iconLibraries[$library])) {
370            throw new ExceptionCombo("The icon library ($library) is unknown. The icon could not be downloaded.", self::ICON_CANONICAL_NAME);
371        } else {
372            $iconBaseUrl = $iconLibraries[$library];
373        }
374
375        /**
376         * Name processing
377         */
378        $iconName = $this->iconName;
379        switch ($library) {
380
381            case self::FLAT_COLOR_ICON:
382                $iconName = str_replace("-", "_", $iconName);
383                break;
384            case self::TWEET_EMOJI:
385                try {
386                    $iconName = self::getEmojiCodePoint($iconName);
387                } catch (ExceptionCombo $e) {
388                    throw new ExceptionCombo("The emoji name $iconName is unknown. The emoji could not be downloaded.", self::ICON_CANONICAL_NAME, 0, $e);
389                }
390                break;
391            case self::ANT_DESIGN:
392                // table-outlined where table is the svg, outlined the category
393                // ordered-list-outlined where ordered-list is the svg, outlined the category
394                [$iconName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-");
395                $iconBaseUrl .= "/$iconType";
396                break;
397            case self::CARBON:
398                /**
399                 * Iconify normalized the name of the carbon library (making them lowercase)
400                 *
401                 * For instance, CSV is csv (https://icon-sets.iconify.design/carbon/csv/)
402                 *
403                 * This dictionary reproduce it.
404                 */
405                $iconName = self::getPhysicalNameFromDictionary($iconName, self::CARBON);
406                break;
407            case self::FAD:
408                $iconName = self::getPhysicalNameFromDictionary($iconName, self::FAD);
409                break;
410            case self::ICOMOON:
411                $iconName = self::getPhysicalNameFromDictionary($iconName, self::ICOMOON);
412                break;
413            case self::CORE_UI_BRAND:
414                $iconName = self::getPhysicalNameFromDictionary($iconName, self::CORE_UI_BRAND);
415                break;
416            case self::EVA:
417                // Eva
418                // example: eva:facebook-fill
419                [$iconName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-");
420                $iconBaseUrl .= "/$iconType/svg";
421                if ($iconType === "outline") {
422                    // for whatever reason, the name of outline icon has outline at the end
423                    // and not for the fill icon
424                    $iconName .= "-$iconType";
425                }
426                break;
427            case self::PHOSPHOR_ICONS:
428                // example: activity-light
429                [$iconShortName, $iconType] = self::explodeInTwoPartsByLastPosition($iconName, "-");
430                $iconBaseUrl .= "/$iconType";
431                break;
432            case self::SIMPLE_LINE:
433                // Bug
434                if ($iconName === "social-pinterest") {
435                    $iconName = "social-pintarest";
436                }
437                break;
438            case self::BOX_ICON:
439                [$iconType, $extractedIconName] = self::explodeInTwoPartsByLastPosition($iconName, "-");
440                switch ($iconType) {
441                    case "bxl":
442                        $iconBaseUrl .= "/logos";
443                        break;
444                    case "bx":
445                        $iconBaseUrl .= "/regular";
446                        break;
447                    case "bxs":
448                        $iconBaseUrl .= "/solid";
449                        break;
450                    default:
451                        throw new ExceptionCombo("The box-icon icon ($iconName) has a type ($iconType) that is unknown, we can't determine the location of the icon to download");
452                }
453                break;
454            case self::VSCODE:
455                $iconName = str_replace("-", "_", $iconName);
456                break;
457            case self::SI_GLYPH:
458                $iconName = "si-glyph-" . $iconName;
459                break;
460        }
461
462
463        // The url
464        return "$iconBaseUrl/$iconName.svg";
465
466    }
467
468    /**
469     * @throws ExceptionCombo
470     */
471    public function download()
472    {
473
474        $mediaDokuPath = $this->getPath();
475        if (!($mediaDokuPath instanceof DokuPath)) {
476            throw new ExceptionCombo("The icon path ($mediaDokuPath) is not a wiki path. This is not yet supported");
477        }
478        $library = $this->getLibrary();
479
480        /**
481         * Create the target directory if it does not exist
482         */
483        $iconDir = $mediaDokuPath->getParent();
484        if (!FileSystems::exists($iconDir)) {
485            try {
486                FileSystems::createDirectory($iconDir);
487            } catch (ExceptionCombo $e) {
488                throw new ExceptionCombo("The icon directory ($iconDir) could not be created.", self::ICON_CANONICAL_NAME, 0, $e);
489            }
490        }
491
492        /**
493         * Download the icon
494         */
495        $downloadUrl = $this->getDownloadUrl();
496        $filePointer = @fopen($downloadUrl, 'r');
497        if ($filePointer == false) {
498            // (ie no icon file found at ($downloadUrl)
499            $urlLibrary = self::ICON_LIBRARY_WEBSITE_URLS[$library];
500            throw new ExceptionCombo("The library (<a href=\"$urlLibrary\">$library</a>) does not have a icon (<a href=\"$downloadUrl\">$this->iconName</a>).", self::ICON_CANONICAL_NAME);
501        }
502
503        $numberOfByte = @file_put_contents($mediaDokuPath->toLocalPath()->toAbsolutePath()->toString(), $filePointer);
504        if ($numberOfByte != false) {
505            LogUtility::msg("The icon ($this) from the library ($library) was downloaded to ($mediaDokuPath)", LogUtility::LVL_MSG_INFO, self::ICON_CANONICAL_NAME);
506        } else {
507            LogUtility::msg("Internal error: The icon ($this) from the library ($library) could no be written to ($mediaDokuPath)", LogUtility::LVL_MSG_ERROR, self::ICON_CANONICAL_NAME);
508        }
509
510
511    }
512
513    /**
514     * @param $iconName
515     * @param $mediaFilePath
516     * @deprecated Old code to download icon from the material design api
517     */
518    public
519    static function downloadIconFromMaterialDesignApi($iconName, $mediaFilePath)
520    {
521        // Try the official API
522        // Read the icon meta of
523        // Meta Json file got all icons
524        //
525        //   * Available at: https://raw.githubusercontent.com/Templarian/MaterialDesign/master/meta.json
526        //   * See doc: https://github.com/Templarian/MaterialDesign-Site/blob/master/src/content/api.md)
527        $arrayFormat = true;
528        $iconMetaJson = json_decode(file_get_contents(__DIR__ . '/../resources/dictionary/icon-meta.json'), $arrayFormat);
529        $iconId = null;
530        foreach ($iconMetaJson as $key => $value) {
531            if ($value['name'] == $iconName) {
532                $iconId = $value['id'];
533                break;
534            }
535        }
536        if ($iconId != null) {
537
538            // Download
539            // Call to the API
540            // https://dev.materialdesignicons.com/contribute/site/api
541            $downloadUrl = "https://materialdesignicons.com/api/download/icon/svg/$iconId";
542            $filePointer = file_put_contents($mediaFilePath, fopen($downloadUrl, 'r'));
543            if ($filePointer == false) {
544                LogUtility::msg("The file ($downloadUrl) could not be downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_ERROR, self::ICON_CANONICAL_NAME);
545            } else {
546                LogUtility::msg("The material design icon ($iconName) was downloaded to ($mediaFilePath)", LogUtility::LVL_MSG_INFO, self::ICON_CANONICAL_NAME);
547            }
548
549        }
550
551    }
552
553    private static function getLibraries(): array
554    {
555        return array_merge(
556            self::PUBLIC_LIBRARY_ACRONYM,
557            self::DEPRECATED_LIBRARY_ACRONYM
558        );
559    }
560
561    /**
562     * @throws ExceptionCombo
563     */
564    public static function getEmojiCodePoint(string $emojiName)
565    {
566        $path = Site::getComboDictionaryDirectory()->resolve("emojis.json");
567        $jsonContent = FileSystems::getContent($path);
568        $jsonArray = Json::createFromString($jsonContent)->toArray();
569        return $jsonArray[$emojiName];
570    }
571
572
573    /**
574     * @param string $iconName
575     * @param string $sep
576     * @return array
577     * @throws ExceptionCombo
578     */
579    private static function explodeInTwoPartsByLastPosition(string $iconName, string $sep = "-"): array
580    {
581        $index = strrpos($iconName, $sep);
582        if ($index === false) {
583            throw new ExceptionCombo ("We expect that the icon name ($iconName) has two parts separated by a `-` (example: table-outlined). The icon could not be downloaded.", self::ICON_CANONICAL_NAME);
584        }
585        $firstPart = substr($iconName, 0, $index);
586        $secondPart = substr($iconName, $index + 1);
587        return [$firstPart, $secondPart];
588    }
589
590
591    /**
592     * @throws ExceptionCombo
593     */
594    public function render(): string
595    {
596
597        if (!FileSystems::exists($this->getPath())) {
598            try {
599                $this->download();
600            } catch (ExceptionCombo $e) {
601                throw new ExceptionCombo("The icon ($this) does not exist and could not be downloaded ({$e->getMessage()}.", self::ICON_CANONICAL_NAME);
602            }
603        }
604
605        $svgImageLink = SvgImageLink::createMediaLinkFromPath(
606            $this->getPath(),
607            $this->getAttributes()
608        );
609        return $svgImageLink->renderMediaTag();
610
611
612    }
613
614    public function __toString()
615    {
616        return $this->getFullQualifiedName();
617    }
618
619    private function getLibrary()
620    {
621        return $this->library;
622    }
623
624
625}
626