# Perspective - Icon browser picks up custom icon libraries :)

**URL:** <https://forum.inductiveautomation.com/t/perspective-icon-browser-picks-up-custom-icon-libraries/49074>\
**Category:** Ignition\
**Tags:** perspective\
**Created:** [July 22, 2021, 7:41am UTC](https://forum.inductiveautomation.com/t/perspective-icon-browser-picks-up-custom-icon-libraries/49074 "2021-07-22T07:41:13Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [July 22, 2021, 7:41am UTC](https://forum.inductiveautomation.com/t/perspective-icon-browser-picks-up-custom-icon-libraries/49074/1 "2021-07-22T07:41:13Z")

</div>

I was working on a tool to smoosh a directory of SVGs into an icon library and I just noticed that the icon browser picks up custom icon libraries 🙂 Very cool! Thanks IA team

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/b/eb76deb91d6f37ed6ceec666db3148c2221735cf.png)

Not the greatest SVGs to use for icons, but it proves the concept! The blank ones are extra useful

---

<div class="post-metadata">

**Author:** ![Tommy\_Evans](https://avatars.discourse-cdn.com/v4/letter/t/fbc32d/32.png) [@Tommy\_Evans](https://forum.inductiveautomation.com/u/Tommy_Evans)\
**Post date:** [January 17, 2022, 9:32am UTC](https://forum.inductiveautomation.com/t/perspective-icon-browser-picks-up-custom-icon-libraries/49074/2 "2022-01-17T09:32:49Z")

</div>

did you finish this tool? I’m having a hell of a time trying to get custom svg icons into ignition.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [January 18, 2022, 12:33am UTC](https://forum.inductiveautomation.com/t/perspective-icon-browser-picks-up-custom-icon-libraries/49074/3 "2022-01-18T00:33:54Z")

</div>

I definitely have a script working, but it needs some pretty… It was written in Python 3.9, I haven’t checked if it’ll work in Jython.  
It only handles simple SVGs (which icons should be anyway) and doesn’t cope with and ignores SVGs with groups

```auto
# standard libraries
import xmltodict
from collections import OrderedDict
import copy
import os
from os import walk

ignitionIconLibraryName = 'lib-custom'
# NOT USED YET
# ignitionIconLibraryPath = r'C:\Program Files\Inductive Automation\Ignition\data\modules\com.inductiveautomation.perspective\icons'
svgFolderPath = r'G:\My Drive\Work\Resources\Symbols\Icons\material.io-custom'

# NOT USED YET
# ignitionIconLibraryFullpath = '{}\{}.svg'.format(ignitionIconLibraryPath, ignitionIconLibraryName)

# setup the SVG library "XML-as-a-dict" object
svgLibraryDict = OrderedDict()
svgLibraryDict['svg'] = OrderedDict()
svgLibraryDict['svg']['@xmlns'] = 'http://www.w3.org/2000/svg'
svgLibraryDict['svg']['@xmlns:xlink'] = 'http://www.w3.org/1999/xlink'
svgLibraryDict['svg']['defs'] = [OrderedDict()]
svgLibraryDict['svg']['defs'][0]['style'] = '.icon { display: none }\n\t\t\t.icon:target { display: inline }'
svgLibraryDict['svg']['svg'] = []

skeletonSVGDef = OrderedDict()
skeletonSVGDef['@viewBox'] = '<REPLACE ME>'
skeletonSVGDef['g'] = OrderedDict()
skeletonSVGDef['g']['@class'] = 'icon'
skeletonSVGDef['g']['@id'] = '<REPLACE ME>'
skeletonSVGDef['g']['path'] = OrderedDict()
skeletonSVGDef['g']['path']['@d'] = '<REPLACE ME>'

filenames = next(walk(svgFolderPath), ([], [], []))[2] # [] if no file

unprocessedFiles = []
processedFiles = []

for filename in filenames:
    svg_viewBox = None
    svg_g_path_d = None

    filepath ='{}\{}'.format(svgFolderPath, filename)
    if filepath.endswith('.svg') and filepath.split('.') != ignitionIconLibraryName:
        with open(filepath) as fd:
            new_svg = copy.deepcopy(skeletonSVGDef)
            doc = xmltodict.parse(fd.read())

            if doc.get('svg', None) is not None:
                svg_viewBox = doc.get('svg', None).get('@viewBox', None)

                if doc['svg'].get('path', None) is not None:
                    if isinstance(doc['svg']['path'], OrderedDict):
                        svg_g_path_d = doc['svg']['path'].get('@d', None)
                        processedFiles.append({'path': filepath,
                                               'filename': filename,
                                               'error': 'None'})
                    else:
                        unprocessedFiles.append({'path': filepath,
                                               'filename': filename,
                                               'error': 'None'})

                elif doc['svg'].get('g', None) is not None:
                    if isinstance(doc['svg']['g'], OrderedDict):
                        if doc['svg']['g'].get('path', None) is not None:
                            if isinstance(doc['svg']['g']['path'], OrderedDict):
                                svg_g_path_d = doc['svg']['g']['path'].get('@d', None)
                                processedFiles.append({'path': filepath,
                                                         'filename': filename,
                                                         'error': 'None'})
                            else:
                                unprocessedFiles.append({'path': filepath,
                                                         'filename': filename,
                                                         'error': 'SVG.g.path object is a list and cannot be processed. SVG icons are required to be flat'})
                    else:
                        unprocessedFiles.append({'path': filepath,
                                                 'filename': filename,
                                                 'error': 'SVG.g object is a list and cannot be processed. SVG icons are required to be flat'})

            if svg_g_path_d is not None and svg_viewBox is not None:
                new_svg['@viewBox'] = svg_viewBox
                new_svg['g']['path']['@d'] = svg_g_path_d
                new_svg['g']['@id'] = filename.split('.')[0].replace('_', '-').replace(' ', '-').replace('-24px', '')
                svgLibraryDict['svg']['svg'].append(new_svg)

print('SVGs added: {}'.format(len(svgLibraryDict['svg'])))
print('Successful Files:')
print('-----------------------')
for stuff in processedFiles:
    print('{}'.format(stuff['filename']))

xml = xmltodict.unparse(svgLibraryDict)
with open('{}\\{}.svg'.format(svgFolderPath, ignitionIconLibraryName), 'w') as f:
    f.write(xml)

print('Unprocessed Files:')
print('-----------------------')
for stuff in unprocessedFiles:
    print('{}: {}'.format(stuff['filename'], stuff['error']))
    # move the unprocessed files into an unprocessed folder
    os.replace(stuff['path'], '{}\unprocessed\{}'.format(svgFolderPath, stuff['filename']))

```
