# Custom Perspective Icon SVG Syntax

**URL:** <https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546>\
**Category:** Ignition\
**Tags:** perspective, ignition81\
**Created:** [November 15, 2022, 8:32pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546 "2022-11-15T20:32:00Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 15, 2022, 8:32pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/1 "2022-11-15T20:32:00Z")

</div>

I am working on converting an open-source icon library into the format necessary for Perspective, and I am not finding great documentation on syntax that the SVG renderer is ignoring vs accepting.

Here is the repo: [https://lucide.dev](https://lucide.dev)

### An example SVG:

```xml
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
</svg>

```

### My example converted version:

```python
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="transparent" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
	<g class="icon" id="activity">
		<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline>
	</g>
</svg>

```

But I am noticing that I can't get it to recognize the additional props in the `<svg>` tag other than `viewBox`. So I am always getting a greyed out icon, unless I add properties in the designer to override `fill` and `stroke`

EDIT: It looks like the icon selection popup renders the `stroke` attribute in the `svg` tag accurately, but not the `fill`.

EDIT #2: It looks like if I move the styling syntax in to the group tag it works as well, just not the svg tag... so maybe this is a bug?

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 15, 2022, 8:53pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/2 "2022-11-15T20:53:13Z")

</div>

Also a "Crash the designer" but that this identified, there are a handful of values that can cause the designer to crash when you open the icon viewer if found inside the icons svg.

For example this is where I have `fill="transparent"` in the group property of an icon

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

You can kind of see the error in the output console, but the whole designer freezes up and I can't go select the text to share the full exception.

It does this whenever I have `fill=var(--icon)` or `fill="transparent"`

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [November 15, 2022, 11:38pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/3 "2022-11-15T23:38:03Z")

</div>

These would make great support tickets 🙂

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 15, 2022, 11:57pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/4 "2022-11-15T23:57:54Z")

</div>

You make a good point, but just imagine the benefit all the random people following behind me will get that don't have support contracts!

Also I just didn't have time to make a call when I posted it. lol

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 16, 2022, 12:58am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/5 "2022-11-16T00:58:46Z")

</div>

To anyone curious who comes after this, here is a simple python script that converts (at least that) folder of `.svg` files into the icon library structure needed for Perspective.

```python
import os, re

# Iterate through a folder of svg files and add them to one svg repo
folder = './myFolder'
repo = './myFolder.svg'
repo = open(repo, 'w')

xml_base = '<?xml version="1.0" encoding="utf-8"?>\n' \
		   '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n' \
		   '\t<defs>\n' \
			'\t\t<style>\n' \
			'\t\t\t.icon { display: none }\n' \
			'\t\t\t.icon:target { display: inline }\n' \
			'\t\t</style>\n' \
			'\t</defs>\n'

repo.write(xml_base)

base_tab_count = 1

for filename in os.listdir(folder):
	if filename.endswith(".svg"):
		base_tabs = '\t' * base_tab_count
		repo.write('\n%s<!--%s-->\n' % (base_tabs, filename[:-4]))
		repo.write('%s<svg viewBox="0 0 24 24" width="24" height="24">\n' % base_tabs)
		svg = open(folder + '/' + filename, 'r')
		for line in svg:
			# If the line starts with <svg, use a regex to replace the xmlns and width/height attributes
			if line.startswith('<svg'):
				line = line.replace('<svg', '<g class="icon" id="%s"' % filename[:-4])
				line = re.sub(r' xmlns="[^"]+"', '', line)
				line = re.sub(r' width="[^"]+"', '', line)
				line = re.sub(r' height="[^"]+"', '', line)
				line = re.sub(r' viewBox="[^"]+"', '', line)
				line = re.sub(r' fill="[^"]+"', ' fill="none"', line)
				line = re.sub(r' stroke="[^"]+"', ' stroke="black"', line)

			if line.startswith('</svg'):
				line = line.replace('</svg', '</g')
			
			if line != '\n':
				repo.write("%s\t%s" % (base_tabs, line))
			
		svg.close()
		repo.write('\n%s</svg>\n' % base_tabs)

repo.write('\n</svg>')
repo.close()

```

---

<div class="post-metadata">

**Author:** ![victordcq](https://avatars.discourse-cdn.com/v4/letter/v/f6c823/32.png) [@victordcq](https://forum.inductiveautomation.com/u/victordcq)\
**Post date:** [November 16, 2022, 7:58am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/6 "2022-11-16T07:58:43Z")

</div>

You are using 8.1.22 right? it has some recent svg changes^^

---

<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:** [November 16, 2022, 12:55pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/7 "2022-11-16T12:55:00Z")

</div>

Why not use an XML library? I use xmltodict in python 3.9  
Not sure if it's got any C dependencies... But otherwise there should be a Java XML library you can use

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 16, 2022, 3:31pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/8 "2022-11-16T15:31:09Z")

</div>

Yes sir 8.1.22

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 16, 2022, 3:31pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/9 "2022-11-16T15:31:48Z")

</div>

To be fair, GitHub copilot wrote most of the script, I was just along for the ride

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [November 16, 2022, 4:47pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/10 "2022-11-16T16:47:41Z")

</div>

> <https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454>

---

<div class="post-metadata">

**Author:** ![kgamble](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kgamble/32/8019_2.png) [@kgamble](https://forum.inductiveautomation.com/u/kgamble)\
**Post date:** [November 16, 2022, 4:56pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/11 "2022-11-16T16:56:23Z")

</div>

This was beautiful. Thank you for sharing

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [November 17, 2022, 1:26am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/12 "2022-11-17T01:26:36Z")

</div>

> [@paul-griffith](#):
>
> [html - RegEx match open tags except XHTML self-contained tags - Stack Overflow](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454)

**What a bummer!:**  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/b/fbec5ed150dfafc2e6f37fd59e01d30007b6d18a.png)

That was a well delivered point

---

<div class="post-metadata">

**Author:** ![r123](https://avatars.discourse-cdn.com/v4/letter/r/838e76/32.png) [@r123](https://forum.inductiveautomation.com/u/r123)\
**Post date:** [July 11, 2023, 3:00am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/13 "2023-07-11T03:00:42Z")

</div>

I am trying to get an existing working SVG into the correct format for an icon SVG.

I add the symbol with the class and id but I'm not sure what to do with the extra "g" layers?

```auto
<svg xmlns="http://www.w3.org/2000/svg">
    <symbol class="icon" id="ETECH" viewBox="0 0 24.8 24.8">
        <g id="Layer_2" data-name="Layer 2">
        <g id="Layer_1-2" data-name="Layer 1">
<path d="M14.3,7.8a1.4,1.4,0,0,0,1-.2l5.4-5.5,2,2L17.2,9.5a1.4,1.4,0,0,0-.2,1,1.1,1.1,0,0,0,.9.5h6.9V0h-11V6.9A1.1,1.1,0,0,0,14.3,7.8Z"/><circle cx="12.4" cy="12.4" r="1.7"/><path d="M6.9,11a1.1,1.1,0,0,0,.9-.5,1.4,1.4,0,0,0-.2-1L2.1,4.1l2-2L9.5,7.6a1.4,1.4,0,0,0,1,.2,1.1,1.1,0,0,0,.5-.9V0H0V6.2L4.8,11Z"/><path d="M24.8,13.8H17.9a1.1,1.1,0,0,0-.9.5,1.4,1.4,0,0,0,.2,1l5.5,5.4-2,2-5.4-5.5a1.4,1.4,0,0,0-1-.2,1.1,1.1,0,0,0-.5.9V20l4.8,4.8h6.2v-11Z"/><rect x="2.1" y="18" width="6.6" height="2.76" transform="translate(-12.1 9.5) rotate(-45)"/><rect x="11" y="22.1" width="2.8" height="2.76"/><rect y="11" width="2.8" height="2.76"/>
        </g></g>
    </symbol>
</svg>

```

---

<div class="post-metadata">

**Author:** ![r123](https://avatars.discourse-cdn.com/v4/letter/r/838e76/32.png) [@r123](https://forum.inductiveautomation.com/u/r123)\
**Post date:** [July 12, 2023, 6:26pm UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/14 "2023-07-12T18:26:32Z")

</div>

Not sure what the layers are for but removing them got it working.

```auto
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">  
 
    <!--E TECH Mark icon-->
    <svg viewBox="0 0 24 24">
        <g id="ETECHMark">
		<path d="M14.3,7.8a1.4,1.4,0,0,0,1-.2l5.4-5.5,2,2L17.2,9.5a1.4,1.4,0,0,0-.2,1,1.1,1.1,0,0,0,.9.5h6.9V0h-11V6.9A1.1,1.1,0,0,0,14.3,7.8Z"/><circle cx="12.4" cy="12.4" r="1.7"/><path d="M6.9,11a1.1,1.1,0,0,0,.9-.5,1.4,1.4,0,0,0-.2-1L2.1,4.1l2-2L9.5,7.6a1.4,1.4,0,0,0,1,.2,1.1,1.1,0,0,0,.5-.9V0H0V6.2L4.8,11Z"/><path d="M24.8,13.8H17.9a1.1,1.1,0,0,0-.9.5,1.4,1.4,0,0,0,.2,1l5.5,5.4-2,2-5.4-5.5a1.4,1.4,0,0,0-1-.2,1.1,1.1,0,0,0-.5.9V20l4.8,4.8h6.2v-11Z"/><rect x="2.1" y="18" width="6.6" height="2.76" transform="translate(-12.1 9.5) rotate(-45)"/><rect x="11" y="22.1" width="2.8" height="2.76"/><rect y="11" width="2.8" height="2.76"/>
        </g>
    </svg>  
</svg>

```

---

<div class="post-metadata">

**Author:** ![simeonw](https://avatars.discourse-cdn.com/v4/letter/s/4da419/32.png) [@simeonw](https://forum.inductiveautomation.com/u/simeonw)\
**Post date:** [May 16, 2025, 5:06am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/15 "2025-05-16T05:06:25Z")

</div>

I've often found when attempting to convert an open-source library to Ignition's format, the coloring doesn't always work correctly because some icons are designed to accept a **stroke** color and Ignition applies a **fill** color.

Inkscape can convert strokes to paths and also has a nice command line tool for it. if you have it installed, you run this PowerShell command on a folder of SVGs to convert everything to paths.

```python
$svgFolder = "." # <-- folder path for svgs
$files = Get-ChildItem -Path $svgFolder -Filter *.svg
foreach ($f in $files) {
    & "C:\Program Files\Inkscape\bin\inkscapecom.com" "$($f.FullName)" --actions="select-all;object-stroke-to-path;export-filename:$($f.FullName);export-do" --export-plain-svg
}

```

This way, when Ignition applies a fill color, you end up with this:

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

Instead of this:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/0/8/08ab4dc36bb1fbf8b659fe09b279bf2827182a76.png)

You may get some distortion during the conversion, but it generally works quite well.

---

<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:** [May 16, 2025, 6:11am UTC](https://forum.inductiveautomation.com/t/custom-perspective-icon-svg-syntax/66546/16 "2025-05-16T06:11:17Z")

</div>

All of Google's icons use paths and never strokes. Strokes are less predictable in how they will render (scaling, inside/centre/outside path, end caps, etc.). Good to know though about being able to bulk fix them!
