Graphics are an important part of good software and systems documentation. My main requirements for tools to generate and maintain graphics are:
To publish my DIY-Synthesizer graphics using SVG, I considered various editors:
All are open source with the exception of Powerpoint. Here are the pros and cons from my point of view:
E.g.
inkscape -d 300 in.svg -o out.png
converts an SVG file to PNG with 300 DPI resolution
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.
Please also see here for Emacs' graphics support.
org-mode
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.
org-mode
out-of-the boxNone 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
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:
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
Use Python svgpathtools
pip install svgpathtools
svgpathtools
is based on svg.path
Use Python svg.path (v2.0)
pip3 install svg.path
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
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
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.
d
attribute of the path
element
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.
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):
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.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.
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.