# Exporting SVGs from Perspective

**URL:** <https://forum.inductiveautomation.com/t/exporting-svgs-from-perspective/65912>\
**Category:** Ignition\
**Tags:** ignition81, perspective\
**Created:** [November 1, 2022, 4:02am UTC](https://forum.inductiveautomation.com/t/exporting-svgs-from-perspective/65912 "2022-11-01T04:02:46Z")\
**Posts on this page:** 1\
**Showing post:** 4

<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 2, 2022, 10:02pm UTC](https://forum.inductiveautomation.com/t/exporting-svgs-from-perspective/65912/4 "2022-11-02T22:02:47Z")

</div>

Sorry, time got away from me.  
Script below.  
`obj` is a copy of the "SVG" in the View, converted into a Py Object with `json.loads` / `system.util.jsonDecode`  
The `ATTR_LOOKUP` is a conversion table (dictionary) of Ignition styles to its corresponding SVG style.

EDIT: Tested and working in the script console, although you'll need the clipboard functions. I've posted them elsewhere on the forum

> [@System.tag.configure incorrectly overriding multiple UDT tag properties](https://forum.inductiveautomation.com/t/system-tag-configure-incorrectly-overriding-multiple-udt-tag-properties/40593/18):
>
> FWIW, I recently forgot about this and inadvertently added property overrides to a large number of UDTInstance tags confused So I created a script to remove the prop overrides on tags part of UDT instances that were not included in a list of property names. The script copies the tag json back into clipboard, so you can review it first before saving as a json and importing it back in. 
> 
> > **Main Functions**
> >
> > def removePropOverrides(tags, retainProps=None, tagpath='', insideUDTInstance=False): …

```auto
import xml.etree.cElementTree as et
import json
import copy

def extract_svg_from_view(obj):
	#json_str = clipboard.getClipboard()
	#'[{ "type": "ia.shapes.svg", "version": 0, "props": { "viewBox": "0 0 185 527.99", "elements": [{ "type": "path", "name": "rect933", "d": "m92.5 5c-43.75 0-87.5 30.69-87.5 92.07v425.92h175v-425.92c0-61.38-43.75-92.07-87.5-92.07z", "fill": { "paint": "#fff" }, "stroke": { "paint": "#000", "linecap": "round", "width": "10" } }, { "type": "path", "name": "rect933-3", "d": "m92.5 5c-43.75 0-87.5 30.69-87.5 92.07v425.92h175v-425.92c0-61.38-43.75-92.07-87.5-92.07z", "fill": { "paint": "#fff" }, "stroke": { "paint": "#000", "linecap": "round", "width": "10" } }, { "type": "group", "name": "g7299", "fill": { "paint": "#ff2a2a" }, "stroke": { "paint": "#000", "linecap": "round", "width": "10" }, "elements": [{ "type": "circle", "name": "circle7295", "cx": "69.758", "cy": "157.43", "r": "64.685" }, { "type": "circle", "name": "path7213", "cx": "92.5", "cy": "263.99", "r": "64.685" }] } ] }, "meta": { "name": "TEST_Export" }, "position": { "basis": "20px" }, "custom": {} } ] '
	#svg_obj = json.loads(json_str)
	svg_obj = obj[0] # assume that the obj passed in is a copied "SVG" from a Perspective View which adds the SVG definition into a single item array. We want the item
	viewbox = svg_obj['props']['viewBox']
	elements = svg_obj['props']['elements']

	ATTR_LOOKUP = {}
	ATTR_LOOKUP['name'] = 'id'
	ATTR_LOOKUP['fill.paint'] = 'fill'
	ATTR_LOOKUP['fill.opacity'] = 'opacity'
	ATTR_LOOKUP['stroke.paint'] = 'stroke'
	ATTR_LOOKUP['style.stroke'] = 'stroke'
	ATTR_LOOKUP['stroke.linecap'] = 'stroke-linecap'
	ATTR_LOOKUP['stroke.width'] = 'stroke-width'
	ATTR_LOOKUP['style.strokeWidth'] = 'stroke-width'
	ATTR_LOOKUP['style.vector-effect'] = 'vector-effect'

	doc = et.Element('svg', viewBox=viewbox, version='1.1', xmlns='http://www.w3.org/2000/svg')

	not_included_SVG_elements = []
	def addElements(xmlobj, elements):
		for element in elements:
			if 'type' in element:
				attrs_dict = {}

				# collect the attributes for the current SVG element are store them in a dictionary
				for attr_name in element:
					# exclude any nested elements as they need to be added later. These are not added as attributes
					if attr_name != 'elements':
						attr = element[attr_name]

						# if the attribute is a dict in Ignition, then the dict will contain the actual attribute names
						if isinstance(attr, dict):
							for prop_name in attr:
								svg_prop_name = ATTR_LOOKUP.get('{}.{}'.format(attr_name, prop_name), 'UNSET')
								if svg_prop_name == 'UNSET':
									not_included_SVG_elements.append('{}.{}'.format(attr_name, prop_name))

								svg_prop_val = attr[prop_name]

								attrs_dict[svg_prop_name] = svg_prop_val
						else:
							attrs_dict[ATTR_LOOKUP.get(attr_name, attr_name)] = attr

				element_name = attrs_dict['type']
				if element_name == 'group':
					element_name = 'g'

				xmlobj_attr = et.SubElement(xmlobj, element_name, **attrs_dict)

				if 'elements' in element:
					attr_elements = copy.deepcopy(element['elements'])

					addElements(xmlobj_attr, attr_elements)

	addElements(doc, elements)
	#print ET.tostring(doc, encoding='utf8', method='xml')
	
	if not_included_SVG_elements != []:
		print "Copied to clipboard, but some SVG attributes are missing mapping. These have not been extracted {}".format(not_included_SVG_elements)
	
	return et.tostring(doc)	

json = shared.util.clipboard.readText()
obj = system.util.jsonDecode(json)
svg = extract_svg_from_view(obj)
shared.util.clipboard.writeText(svg)

```

---

_[View the full topic](https://forum.inductiveautomation.com/t/exporting-svgs-from-perspective/65912)._
