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