Menu

My Requirements for Graphics Editors

SVG Logo

Graphics are an important part of good software and systems documentation. My main requirements for tools to generate and maintain graphics are:

  1. Good support for scalable vector graphics (SVG)
  2. Manual and automatic creation of graphics
  3. Platform independence

Comparison of Editors

To publish my DIY-Synthesizer graphics using SVG, I considered various editors:

  1. Inkscape
  2. Dia
  3. IPE
  4. Emacs
  5. Microsoft Powerpoint
  6. Graphviz

All are open source with the exception of Powerpoint. Here are the pros and cons from my point of view:

Inkscape

Pros

Cons

Dia

Pros

Cons

IPE

See also: https://www.win.tue.nl/aga/tutorials/ipe/ and http://algo2.iti.kit.edu/strash/socnet-seminar-2015/template_ipe_presentation.pdf, especially the "Quirks" section.

Pros

Cons

Emacs

Please also see here for Emacs' graphics support.

Pros

Cons

Powerpoint

Pros

Cons

I am working a lot with Microsoft Powerpoint. However, it does not fulfil requirements 1) and 3). Only some of the more recent versions have SVG import and export functionality.

Graphviz

Pros

Cons

Conclusion

None of the first four I found sufficient for my purpose. I mostly have the following use cases:

For graphs in the mathematical sense, I settled on Graphviz. Other graphs I do in Powerpoint. Logos I edit using either Powerpoint or by hand crafting the SVG.

I rolled my own Powerpoint to SVG export function (see trppt2svg), because

Manipulating SVG Content Using Python Code

SVG's path element is complex and powerful. For example some SVG icon libraries use SVG's path element as a general purpose means to draw icon graphics. The paths themselves are defined in the d attribute of the path element. The d attribute content has its own syntax that is not XML. This has the consequence that tools like XSLT can hardly be used to parse the attribute content to manipulate the path(s).

To programmatically treat the d attribute, I found the following possibilities:

  1. Use regular expressions, e.g. in Javascript

    var cmdRegEx = /([MLQTCSAZVH])([^MLQTCSAZVH]*)/gi
    var commands = d.match(cmdRegEx);
    

    or

    var cmdRegEx = /[a-z][^a-z]*/ig;
    var commands = d.match(cmdRegEx);
    

    or

    (/[MLQTCSAZ][^MLQTCSAZ]*/gi
    

    But there could be subtle issues like not supporting scientific number notation

  2. Use Python svgpathtools

    pip install svgpathtools
    

    svgpathtools is based on svg.path

  3. Use Python svg.path (v2.0)

    pip3 install svg.path
    
  4. Use Python svgelements whose home page is https://github.com/meerk40t/svgelements

    python -m pip install svgelements
    

For example code using options 2) and/or 3) see e.g. https://stackoverflow.com/questions/15857818/python-svg-parser

Note that 1) to 3) only deal with SVG's path element. Other elements are not handled. Option 4) handles all SVG elements. It is implemented as one single .py file, but readilky installs using pip.

I testest svgelements. From the windows command prompt (python is in the path):

>python -m pip install svgelements
Collecting svgelements
  Downloading svgelements-1.6.13-py2.py3-none-any.whl (118 kB)
     |████████████████████████████████| 118 kB 6.4 MB/s
Installing collected packages: svgelements
Successfully installed svgelements-1.6.13

My Test of the svgelements Python Code

My first test uses a simple SVG file, an icon which contains only one SVG element, a path element.

from svgelements import *
#import svgelements

svg = SVG.parse('desktop.svg')
#list(svg.elements())
#print(list)

for element in svg.elements():
	 try:
		 if element.values['visibility'] == 'hidden':
			 continue
	 except (KeyError, AttributeError):
		 pass

	 if isinstance(element, SVGText):
		 print(f'SVGText: {element}')

	 elif isinstance(element, Path):	# lines, bezier, etc.
#         if len(element) != 0:
#             elements.append(element)
		 print(f'Path: {element}')
		 print(f'#subpaths: {element.count_subpaths()}')
		 mySubPs = element.as_subpaths()
		 for mySubP in mySubPs:
			 print(f'mySubP: {mySubP}')
		 mySegs = element.segments()
		 print(f'#segs: {len(mySegs)}')
		 for mySeg in mySegs:
			 if isinstance(mySeg, Line):
				 print(f'Seg Line: {mySeg}')
			 if isinstance(mySeg, Arc):
				 print(f'Seg Arc:  {mySeg}')
			 if isinstance(mySeg, CubicBezier):
				 print(f'Seg CBez: {mySeg}')
			 if isinstance(mySeg, QuadraticBezier):
				 print(f'Seg QBez: {mySeg}')
			 if isinstance(mySeg, Move):
				 print(f'Seg Move: {mySeg}')
			 if isinstance(mySeg, Close):
				 print(f'Seg Clos: {mySeg}')

	 elif isinstance(element, Shape):	# rect, circle, etc.
		 e = Path(element)	# conversion to a path
		 print(f'Shape: {e}')
#         e.reify()  # In some cases the shape could not have reified, the path must.
#         if len(e) != 0:
#             elements.append(e)

	 elif isinstance(element, SVGImage):
		 try:
			 element.load(os.path.dirname(pathname))
			 if element.image is not None:
				 print(f'Shape: {element}')
		 except OSError:
			 pass
Path: M 10,29 C 10,29 10,24 16,24 C 22,24 22,29 22,29 L 10,29 Z M 2,6 L 2,23 L 30,23 L 30,6 L 2,6 Z
#subpaths: 2
mySubP: M 10,29 C 10,29 10,24 16,24 C 22,24 22,29 22,29 L 10,29 Z
mySubP: M 2,6 L 2,23 L 30,23 L 30,6 L 2,6 Z
#segs: 11
Seg Move: M 10,29
Seg CBez: M 10,29 C 10,29 10,24 16,24
Seg CBez: M 16,24 C 22,24 22,29 22,29
Seg Line: M 22,29 L 10,29
Seg Clos: M 10,29 Z
Seg Move: M 10,29 M 2,6
Seg Line: M 2,6 L 2,23
Seg Line: M 2,23 L 30,23
Seg Line: M 30,23 L 30,6
Seg Line: M 30,6 L 2,6
Seg Clos: M 2,6 Z

I have not found any documentation about svgelements beyond what is on github. The wiki is empty. The test code above is the example parser from github, plus my additions which I deduced by looking at the svgelements.el source code.

Observations from the Test

svgelements seems to add an explicit "Move" operation both in front of sub-path and a segment. This helps the further processing of the info.

Re-using SVG Content

For technical drawings, you often have the need to use the same shape or group of shapes multiple times in the same drawing. To have a consistent appearance, it is desirable to define the content once and to re-use it multiple times

There are several ways to re-use SVG content. There is the possibility in include SVG files in a SVG document. There is also the possibility to nest SVG data structures within a SVG file. I found two ways to implement the latter (there may be more):

  1. It is possible and supported to embed an svg XML element in an SVG file. The position of the group on the canvas is determined by the x and y attributes of the embedded svg element. The viewBox attribute of the embedded svg element defines a local co-ordinate system, that the child SVG elements need to use.
    With this approach, you still duplicate an identical group of shapes, but the "inner" co-ordinates stay the same
  2. You can group one or more SVG shapes using the symbol element and "call" that "symbol" one or more times by the use element. The symbol element has a viewBox attribute, that defines a local co-ordinate system. The use element's x and y attributes set the position of the group of shapes on the canvas.
    This approach is very efficient, because there is no duplication of code

Note that the symbol element syntactically looks similar to the g element, but symbol does not draw the content, whereas g does. g main use seems to implement a logical grouping of SVG content for the benefit of SVG editor functionality.


Last change: 2024-02-19
© 2002-2024 Dr. Thomas Redelberger redetho(a‍t)gmx.de

Close menu