Custom Icon Library in 8.3

From the documentation: This does not appear to be the proper location for a custom icon library in 8.3. Did a search for material.svg and it is not found. I'm assuming this is still allowed. What folder should a custom library go into now?

Use a Custom Icon Repository

The materials icon repository is a great source for icons, but if you have a custom library of icons, you can set it up to be accessible by Ignition. All icon repositories are stored as SVG files within the Gateway's Installation Directory. A typical path to an icon file will look like:

  • Windows
  • MacOS
  • Linux
C:\Program Files\Inductive Automation\Ignition\data\modules\com.inductiveautomation.perspective\icons\<repository name>.svg

You can create custom libraries by adding your own SVG file at this path. The properties inside the file are defined using XML, which

1 Like

Okay, here's the current steps.

  1. Navigate to your install directory, and in the /data/config/resources/core/ folder, create a new folder com.inductiveautomation.perspective/ (if it doesn't already exist).

  2. Within that folder create an icons/ folder.

  3. Within that folder create a <repository name>/ folder.

  4. Within that folder, create:

    • The spritesheet of your SVG, at whatever arbitrary filename you want, e.g. icons.svg.
    • A config.json file that contains a single entry, svgFileName, that contains the same name (icons.svg or whatever else you chose)
      {
       "svgFileName": "icons.svg"
      }
      
    • A resource.json file that references both config.json and your custom icons SVG:
      {
          "scope": "A",
          "version": 1,
          "restricted": false,
          "overridable": true,
          "files": [
              "config.json",
              "icons.svg"
          ],
          "attributes": {
          }
      }
      
  5. Restart the gateway (or POST the /data/api/v1/scan/config endpoint) to pick up the changed resource.

5 Likes

As an aside, I'm not sure if there's a rationale I'm not aware of for the indirection of the config.json resource. I'm going to make a case to drop it for final release, so you would just have a resource.json file pointing to the svg file (by whatever arbitrary name). There may be a complicated upgrade related reason we had to do things this way, though.

1 Like

That worked for me. Thanks.

Perfect. Thank you sir!

Any update here now that the final release is out?

Icons work the same way in the final release, though there were some changes in themes.

I haven’t been able to get the icons to open in designer by adding the icon folder as described above. I have my svg file formatted per this section of the manual: Images and Icons in Perspective | Ignition User Manual. Nothing populates in the migrated-icons folder after a gateway restart either.

Attaching the svg and json files below.

PlantPAxIcons_resource_with_readme(1).zip (18.5 KB)

Your icons file is malformed:

E [P.IconManager                 ] [15:28:30.982] [platform-executor-84]: Unable to parse PlantPAxIcons icon library collection=core
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0x0) was found in the element content of the document.
	at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:262)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.computeLibraryAndCache(IconManagerImpl.java:182)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourceAdded$5(IconManagerImpl.java:150)
	at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.onResourceAdded(IconManagerImpl.java:150)
	at com.inductiveautomation.ignition.gateway.config.NamedResourceHandler$Lifecycle.onAfterChanges(NamedResourceHandler.java:705)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycle$LifecycleResourceListener.onAfterChanges(ResourceCollectionLifecycle.java:183)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyListener(AbstractResourceCollection.java:386)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyResourceListeners(AbstractResourceCollection.java:315)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.updateEffectiveState(AbstractResourceCollection.java:156)
	at com.inductiveautomation.ignition.common.resourcecollection.RuntimeResourceCollection.applyChange(RuntimeResourceCollection.java:267)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.lambda$updateOrStartAffected$5(ResourceCollectionLifecycleFactory.java:210)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.updateOrStartAffected(ResourceCollectionLifecycleFactory.java:190)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.collectionUpdated(ResourceCollectionLifecycleFactory.java:167)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1610)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1594)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$27(ResourceCollectionManagerImpl.java:1515)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$28(ResourceCollectionManagerImpl.java:1514)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$wrapNotificationTask$0(ResourceCollectionManagerImpl.java:204)
	at com.inductiveautomation.ignition.common.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:238)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
E [P.IconManager                 ] [15:28:30.985] [platform-executor-84]: Unable to parse PlantPAxIcons icon library collection=core
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0x0) was found in the element content of the document.
	at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:262)
	at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.computeLibraryAndCache(IconManagerImpl.java:182)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourcesUpdated$7(IconManagerImpl.java:167)
	at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1916)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.lambda$onResourcesUpdated$8(IconManagerImpl.java:166)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at com.inductiveautomation.perspective.gateway.assets.icons.IconManagerImpl.onResourcesUpdated(IconManagerImpl.java:166)
	at com.inductiveautomation.ignition.gateway.config.NamedResourceHandler$Lifecycle.onAfterChanges(NamedResourceHandler.java:720)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycle$LifecycleResourceListener.onAfterChanges(ResourceCollectionLifecycle.java:183)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyListener(AbstractResourceCollection.java:386)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.notifyResourceListeners(AbstractResourceCollection.java:315)
	at com.inductiveautomation.ignition.common.resourcecollection.AbstractResourceCollection.updateEffectiveState(AbstractResourceCollection.java:156)
	at com.inductiveautomation.ignition.common.resourcecollection.RuntimeResourceCollection.applyChange(RuntimeResourceCollection.java:267)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.lambda$updateOrStartAffected$5(ResourceCollectionLifecycleFactory.java:210)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.updateOrStartAffected(ResourceCollectionLifecycleFactory.java:190)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionLifecycleFactory$1.collectionUpdated(ResourceCollectionLifecycleFactory.java:167)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1610)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.notifyCollectionChanged(ResourceCollectionManagerImpl.java:1594)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$27(ResourceCollectionManagerImpl.java:1515)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$onFileTreeChange$28(ResourceCollectionManagerImpl.java:1514)
	at com.inductiveautomation.ignition.gateway.resourcecollection.ResourceCollectionManagerImpl.lambda$wrapNotificationTask$0(ResourceCollectionManagerImpl.java:204)
	at com.inductiveautomation.ignition.common.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:238)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)

There's a bunch of invalid binary characters:

Was this updated in the 8.3.1 release?

Your fix worked perfectly for me in the 8.3.0 release, but on a fresh Gateway (running 8.3.1) using the same setup methodology (and using the same SVG sprite sheet), I am getting the following error.

Error

Failed to load resource: the server responded with a status of 404 (Not Found)

Screenshot of Chrome Dev Tools Console

My config.json and resource.json files are identical to yours (I even used the same icons.svg file name). If I follow the link at the end of the Dev Tools Console Error I am greeted with the following page:

Note: I have been send POST requests to /data/api/v1/scan/config to pick the update (I have also tried restarting the Gateway several times.

Files

I can send the SVG if required but as I say, the same file was used on a different gateway with no problems. So I didn’t bother copying it over.

  • Result of running the ls command on the directory in which my dashboard_icon_set resource is located.

Result of 'ls' command run in the directory of my icon resource

  • My config.json file:
{
 "svgFileName": "icons.svg"
}
  • My resource.json file:
{
    "scope": "A",
    "version": 1,
    "restricted": false,
    "overridable": true,
    "files": [
        "config.json",
        "icons.svg"
    ],
    "attributes": {
    }
}

Any solutions would be appreciated, I very well may have made a silly mistake somewhere when copying my resource from one Gateway to another but I can’t figure out what’s going on. Any ideas?

Nothing should have changed. Are there any errors in your gateway logs from the asset manager?

I am so sorry! I completely forgot to check the logs (didn’t even realise an error like this would get logged). There was a small error in my SVG. I assume I must have accidentally introduced it when copying the file over somehow.

Thanks for your help!

1 Like

Hello. Is anyone else unable to add icons with this approach?