1. Introduction: Three Pain Points

SVG [SVG11] is a well-established and reliable XML schema for drawing. It differs from many drawing tools in providing very few shapes, mainly <rectangle>, <circle>, and <ellipse> plus generic tools such as <polyline>, <polygon>, and <path>. Any other shapes must be constructed from these.

For customization and structure it offers a few tools to define macro objects, which are used via <use> with # plus their ID on @href:

  1. <g> (for group) is rendered where defined. It is useful for re-using objects (via <use>) rather than repeating them entirely.

  2. <symbol> is a more pure definition. It is only rendered when referenced. It also imposes its own local co-ordinate space (with clipping to that viewbox).[1]

  3. <marker> is very similar to <symbol> but adds the option of auto-rotation to the current direction of a <path>, and the notion of a refX/refY point to be considered the origin when placed. These are especially useful for arrowheads, so their direction and precise placement are appropriate to the line they are attached to.

Co-ordinates can generally be expressed as percentages of the viewbox height and width. For example, this defines a rectangle with a horizontal bisecting line:

<symbol id="bisected_box" viewBox="0 0 100 200">
    <rect x="0" y="0" width="100" height="200"/>
    <line x1="0%" y1="50%" x2="100%" y2="50%"/>
</symbol>

This can be instantiated via <use>, which may also set x, y, height, and width.

This paper introduces MVG (short for Meta Vector Graphics), a layer on top of SVG that tries to address three main pain points related to re-use:

  1. The lack of a general way for <use> to pass values to a referenced object to modify its treatment. This notion adds a lot of power, specifically to make objects declarative rather than procedural.

  2. The lack of a way to attach things (such as ends of lines) to SVG objects rather than co-ordinates.

  3. The lack of some shapes (common in drawing apps) that are not easy to construct.

1.1 The reuse/parameterization problem

SVG can draw anything with enough paths (or even a single <path> element, raising and lowering the pen as needed). The most salient barrier to creating novel objects and expressing them as such, is the lack of a way to pass in values on each use, that modify that use.

<use> only allows passing a very small set of predefined properties:

  • x, y, width, and height, without which re-use would be of little use. transform, which is important for resizing, rotating, etc. And href.

  • Presentational attributes (fill, stroke, opacity, etc.) can be inherited from the calling context, but not passed in (which is not at all the same thing).

Such values are known as parameters, and are ubiquitous in programming, formal logic, math, markup languages, and elsewhere. They greatly reduce space and complexity. SVG uses parameters heavily for its built-in constructs; users just can't have any for themselves. Consider drawing the unit octagon in SVG:

<polygon points="0.9239,0.3827 0.3827,0.9239 -0.3827,0.9239 -0.9239,0.3827
    -0.9239,-0.3827 -0.3827,-0.9239 0.3827,-0.9239 0.9239,-0.3827" />

This is very general: any figure at all can be drawn. However, humans have concepts and terms for many more specific shapes (and coin new ones regularly). Humans also associate many shapes closely with meanings. Regular polygons are the bread and butter of drawing; yet to get them in SVG involves calculating and repeating all those coordinates.

Most users would have to look up how to get from octagon, radius 1 to the set of points just shown, and would not readily recognize the points as being a unit octagon.[2]

For developers, the verbose representation is very slightly easier to implement:

def polygon_from_points(points: list[tuple[float, float]]) -> None:
    moveto(points[0])
    for pt in points[1:]:
        lineto(pt)
    lineto(points[0])

versus this for regular polygons:

def regular_polygon(cx: float, cy: float, r: float, n: int) -> None:
    offset = math.radians(90 / n)
    moveto(cx + r * math.cos(offset),
           cy + r * math.sin(offset))
    for k in range(1, n + 1):
        lineto(cx + r * math.cos(2 * math.pi * k / n + offset),
               cy + r * math.sin(2 * math.pi * k / n + offset))

The difference is minor for someone writing graphics code, and they must write similar code anyway — it just goes elsewhere. Having regular_polygon also saves on creating and parsing a long string of points, and fits basic principles of software engineering such as Don't repeat yourself, as well as being more compact and less error-prone.

For power users such as someone writing transforms over the SVG, porting data, or looking at the actual values for any reason, the gain is vast. With just points it takes real work even to identify all the octagons, especially given floating-point number issuess. Just grouping objects into meaningful types is a big win [DeRo20].

Many drawing programs retain shape types internally, but don't exported that data. Inkscape makes heavy use of custom options in a separate namespace, which is similar to our approach below.

1.2 The connection problem

The first problem I consider is that SVG lacks a notion of connecting things to other things per se (cf [DeRo14]). It does support referring to objects by ID for re-use, but this cannot be leveraged for this line should end at that object. Actual attachment is implicit, mainly (a) is drawn by the same <symbol> object, and (b) happens to be located nearby.

1.2.1 Connectors

The most obvious need for logical attachment to objects is the connector. A connector expresses a semantic relationship between objects. These are ubiquitous in diagrams of all kinds (see definitions in [DeRo20]), but in SVG no such concept exists. Connectors also differ from most other lines in that they commonly have meaningful direction and terminal arrowheads.

Connectors are trivial for developers to support — you simply retrieve the referenced object's location and use those co-ordinates. However, the distinct problem of routing the connectors can be harder. Minimal routing solutions are simple: draw a straight line from start to end, or let the user set the route just like they would for <polyline>. But better automatic routing is harder, and a point of competition for implementations.

Omitting the notion of connectors does not ease the routing problem, but adds substantial extra work for users, bloats the data, compromises portability and post-processability, and leads to aesthetic problems.

  • Most obviously, if a user moves either box that seems connected, the line doesn't follow.

  • Endpoints are imprecise. Snapping helps, but is typically to the background grid, not the target box edge.

  • Automated processing cannot fix this. Searching for the nearest vertex is a good heuristic, but often fails.

The amount of manual editing work this one omission creates is astonishing, particularly given how ubiquitous connectors are in drawing programs, how conceptually and structurally distinct they are, and how easy they are to implement (at least with basic routing).

1.2.2 Labels

A very similar issue arises because SVG objects do not own labels. A large share of boxes want <text> labels, but in SVG that attachment cannot be readily expressed. One can mebed a <text> object inside another shape, but it doesn't help because one cannot pass specific text in when it is <use>d.

1.2.3 Decorated basic shapes

A third example emerges from the shape inventories of many drawing apps. Many shapes are merely slight modifications of others. For example, folder is a rectangle with a small tab added; page is a rectangle with a folded corner; flowchart subprocess and draw.io's envelope are rectangles with extra lines; and so on.

Some cases are achievable with <symbol>, but as before the lack of parameters is limiting. For example, it can't express a <folder> that can position its tab wherever the user wants, or a <decorated-rect> that lets the user choose another shape as decorator.

1.2.4 Walls vs. rooms

A similar case shows up in architecture apps: walls and doors are highly salient user objects, but have an awkward relationship: a wall's two sides belong to different rooms, and some rooms (such as a porch) are not even completely enclosed. Some apps, such as SweetHome 3D [https://github.com/ralic/sweethome-3d], use an XML file format and/or can export SVG, but even though walls and rooms may be linked by IDs, they are not inherently related. Worse, they are not even co-located (room edges may be offset by half the walls' thicknesses, with odd consequences).

1.2.5 Ports

At minimum, supporting such connections requires only a way to draw line-like objects whose end locations are given as IDREFs to objects (rather than co-incident co-ordinates). But it is very useful (and common outside SVG) to specify where on such an object to attach. Most drawing apps provide a notion of ports (aka handles, glue points, or anchors), which can be defined on shapes and are attached to.

Without ports, routing can default. The connection semantic is then intact, but rendering is be ideal. Ports address this.

The user easily provides port choices when they create line in the first place; apps typically provide snap to functionality for this. SVG requires roughly the same work on creation, but adds much work on any edit. The precision problems mentioned earlier may seem minor, but [Will94] points out that even slight misalignments come across as mistakes in graphic design:

A problem with many non-designers' publications is a subtle lack of alignment [36]

All these minor misalignments add up to create a visually messy page. [37]

If your alignments are strong, you can choose to break through the alignment consciously and it will look intentional. [41]

1.3 The creeping generality problem

Beyond parameters and ports, SVG has no user-accessible constructs for looping or calculation (other than expressing co-ordinates as percentages of the current viewbox). Rendering some kinds of objects requires multiple operations, such as iterating through a points or path attribute. Another common need is calculating locations relative to the enclosing shape (SVG does have a notion of % of bounding box size).

SVG uses all these concepts inside primitives. Making them user-available, however, is a large design task (as the [Bell18] effort discovered), so the SVG limitation is understandable even if unfortunate. Adding such features to SVG has been discussed, but never actually finalized.

SVG does permit embedding Javascript code that operates on the resulting DOM. However, it runs at load time rather than separately for (say) each <use> of a given <symbol>. XSLT can similarly process SVG code, tokenize points or other attributes, and loop or calculate at transform time, writing out more SVG; but it's not especially easy. MVG instead proposes a very much smaller extension.

1.4 Symbol libraries

SVG decided not to include many predefined shapes. Its inventory includes <rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, and <path>. Anything can be drawn somehow, but not always descriptively.

Nearly all drawing apps have many more shapes than SVG, many of which are familiar and intuitive for users. I examined the shape inventories of several drawing apps,, primarily graphviz [GVShapes] and [Gans93], draw.io [Draw], Inkscape [Inkscape], and TikZ [TikZ]. I also considered Canvas, LibreOffice Draw, and PowerPoint.

One could never specify all the shapes humans will invent. But some shapes are common, or even what [Pike67] christened emic: they are not merely a sensory experience, but a more abstract categorial value or symbol.

The good news is that given parameterization and ports only a few shapes enable constructing many others. MVG thus defines several new drawing objects, particularly ones that require iteration, calculation, or testing conditions. For example, <regular_polygon sides=""> needs to iterate sides times and do a calculation each time. Others involve subdividing a main shape regularly.

The initial MVG implementation is a Python pre-processor that transforms custom elements, parameterization, and ports into basic SVG 1.0. It will be placed at https://github.com/sderose/mvg, and is freely re-usable. It takes only a few hundred lines.

I have sought to choose parameters for consistency across shapes even though shapes can be specified in a variety of ways. For example, an ellipse can be specified in any of the ways listed below, but I follow the SVG precedent (listed first):

  • Center + two radii (cx, cy, rx, ry) — SVG <ellipse>, TikZ \draw (cx,cy) ellipse (rx and ry), similar to Graphviz.

  • Bounding box — LibreOffice Draw, PowerPoint/OOXML, draw.io show this, but might not use it internally.

  • Center + semi-major axis + eccentricity — astronomy/orbital mechanics (Kepler), not drawing tools.

  • Center + semi-major axis + aspect ratio — TikZ shapes.geometric ellipse uses minimum width/minimum height, which is effectively this. PowerPoint also has something similar.

  • Parametric angle sweep (cx, cy, rx, ry, start_angle, end_angle) — SVG <path> arc command, Canvas ctx.ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle).

  • Two foci + point on curve, or two foci + major axis length, or three points on the curve — mathematicians and some CAD or GIS systems (e.g., FreeCAD constraint-based sketcher, PostGIS ST_MinimumBoundingCircle-style fitting).

  • Center plus conjugate diameters — two vectors from center defining a parallelogram that the ellipse is inscribed in; allows sheared/rotated ellipses without a rotate parameter (Matplotlib's matplotlib.patches.Ellipse?).

2 General approach

The overall challenges are adding parameter support, adding ports, and defining the new symbols. The layered approach minimizes disturbance to SVG implementations. All the extensions use a separate namespace.

2.1 Prior work

A lot of conceptual ground has been productively explored by others, some of whom I list here. I have tried to build on such concepts, aiming at a most bang for the least buck compromise.

  • Mary Holstege's extensive work with XSLT, SVG, and other tools for art [Hols20] and [Hols23]

  • XPath/XSLT/XQuery work such as [Kay17a] and [Kay17b]

  • SVG 2 (a CR) and the SVG Parameters proposal (a WD) [Sche09a], [Sche09b], and [Bell18].

  • Inkscape and the Sodipodi namespace [Inkscape]. This architecture is broadly similar, especially in using a secondary namespace to separate extensions, and mapping them (at some level) to regular SVG.

  • TikZ/PGF [TikZ]. The node-as-first-class-object also appears prominently here, for example, treating label + bounding box + named anchors as composite unit: \inheritsavedanchors.

  • GraphML [GraphML]. This treats <port> as first-class named element, though the position is left to extensions. As noted in [DeRo20], GraphML's underlying graph-theoretic model is attractive.

  • Eclipse Layout Kernel [ELK], [Domr23]. ELK seems to have the most formally specified port model in the layout literature: side + position + constraint level. I have not gone quite that far.

2.2 Parameter replacement

Parameterization requires three conceptual parts: declaration, referencing/use, and passing.

In XSLT, parameters are declared by full-fledged sub-elements of <xsl:function>. For example:

<xsl:function name="clamp" as="xs:double">
  <xsl:param name="val"  as="xs:double"/>
  <xsl:param name="hi"   as="xs:double"/>
  <xsl:value-of select="min(($val, $hi)) => max(0)"/>
</xsl:function>

For MVG we want parameters on most or all SVG shapes or shape-like objects, and we don't have a <function> element already to hand. For this more limited context, the XSLT form seems to me slightly heavy, especially since the existing drawing objects are the "functions". A more compact syntax suffices: Parameters are declared merely by setting them on the object that is to receive them, with the value there as the default. I have shown initial _ so they cannot collide with SVG attributes (of course, such attributes must be added to one's schema for validation to work). It is a simple matter for an application to add the attributes to the symbol table before interpreting the body:

<symbol id="somegon" _sides="4" _fgcolor="red">
    ...
</symbol>
 

Referring to parameters within the function body (e.g., the <symbol> element) exactly copies XSLT's Attribute Value Templates (AVT) syntax, with curly braces and $:

<symbol id="somegon" _sides="4" _fgcolor="red">
    <polygon sides="{$_sides}" foreground="{$_fgcolor}" />
</symbol>

For most cases MVG does not need a whole AVT evaluator, but only two very constrained specal cases. Thus, MVG developers and users need not implement, integrate, or really even understand XPath, XSLT, or AVTs; just those special cases (on which see Section 2.2.2).

Invocation is just using the parameterized drawing element, setting any non-default parameters by using attributes in the usual way:

<use href="#somegon" _sides="8" _fgcolor="blue"/>

2.2.1 Automatic parameters

Given their importance to almost all objects in SVG, the coordinates of the bounding box are likely to be needed often. Therefore MVG defines them to be available as if they were attributes on the object.

Often, they actually are exactly that: x, y, width, and height and others are there explicitly, and so naturally available via AVTs. However, they can be given in other ways, or modified. For MVG purposes, we want the resolved values (for example, so objects far away can attach to them). So these attribute names are reserved for that purpose (experience may recommend adding others):

bbLeft   bbRight   bbCenterX
bbTop    bbBottom  bbCenterY
bbWidth  bbHeight

2.2.2 Levels of support

The implementation of <use> must interpret the body of the referenced element, replacing AVT expressions in the same way AVTs work in XSLT. This does mean that the final interpretation of the used object must wait until it is used (for example, the implementation can't just interpret it fully when a definition such as <symbol> is first read, and then naively copy the result for each <use>), just as it can't finalize x or y. This may involve some architectural adjustment, but is not in principle difficult.[3]

To enable simplicity of implementation but also higher functionality, MVG defines two conformance levels. They are named after two devices of Old Norse skaldic poetry:

The first is heiti-conformant: a heiti in Norse is a simple synonym substitution — one name for another, no interpretation required. At this level, {$sides} in an attribute value is replaced by the literal string value of the sides parameter. The grammar for references is not the full AVT language, but merely the {$name} and {id($name)/@name} (escaping literal "{" by doublilng it is also necessary):

expr    ::= "{" "$" name "}"
        ::= "{" "id($" name ")/@" name "}"

The second, optional conformance level is called kenning. In Norse, a kenning is a compound poetic expression requiring interpretive calculation, such as whale-road for sea.[4] At kenning level, applications support the entire XPath expression language (thus providing arithmetic, string handling, XPath axes, etc.). That greatly increases the power, and is especially valuable for doing arithmetic on co-ordinates. It intergrates in exactly the same way; only the interpreter for the brace-expressions needs to be upgraded.

For kenning, MVG permits applications to choose the version(s) of XPath to support (likely 2 or 3 at this time). Trigonometric functions are especially important in SVG (see, for example, [Hols20] and [Hols23]). They come for free with XPath 3 [Robi14], or can be provided in XPath 2 [Berg10] via EXSLT's math library, or by other means. The Saxon Java extension mechanism would also work well for this. XPath 1 [Clar16] does not have AVTs.

If starting from scratch, supporting full expressions would involve implementing an XPath engine; but many XPath implementations exist, and many apps involving SVG already have one of those to hand. In that case the extension should be straightforward.

Parameterization of either level must be supported not only for <symbol> and <use>, but for all SVG and MVG elements (that's probably easier anyway; it just goes directly into generic processing).

text seems to be the only case where {} could occur literally in an SVG attribute, but brace-doubling works for that.

One could consider using CSS var() and calc() for some level of parameter support. However, that brings in many other issues, as well as calc having limited power (see the discussion in [DeRo18]).

2.2.3 The scaling problem and @scale-lock

Some properties should not scale when the object they are part of scales (perhaps font-size, line-weight, dashed-line segment lengths, etc) . It may prove necessary to offer a scale-lock setting for these, but with parameters it is likely possible to get all the needed effects in other ways.

2.3 Port and anchor models

2.3.1 Port definition

The fundamental requirements for ports include a way to define them when defining a <symbol> with a name and a location; and a way to attach objects (connectors, labels, shape-decorators, etc) to them.

An MVG port name is an XML NAME, with three additional requirements. They:

  • must be unique within each <symbol>, but may be re-used in different ones;

  • must not contain . (more on that below);

  • must not match the name of any attribute SVG defines (especially for <use>).

Port definitions look like this:

<symbol id="foo">
    <port name="top" x="100%" y="0"/>
    <port name="foo-3" x="10" y="25%"/>
    ...usual SVG...
</symbol>

x and y attributes, with co-ordinate values as usual (including possibly with %), specify the port's location in the <symbol>'s viewbox. They mayt point outside the viewbox; one might, after all, want to place something adjacent but outside the target object.

This method is not great for shapes like circles or ellipses (quick! tell me the x% and y% for the upper-right of the unit circle). Many drawing apps support compass points for this and other uses (for example, Graphviz, TikZ, and mxGraph/Visio). Some others support numeric angles (for example, TikZ/PGF, though it counts in the opposite direction from SVG). Therefore, MVG also allows angle as a mutually-exclusive alternative to x and y. angle's value may be either a reference angle (counting in degrees CW from east), or one of the 8 major compass points (upper case), or CENTER (which is also the default if none of x, y, or angle is provided):

<port name="port2" angle="NE".../>

For convenience, the 8 compass point abbreviations and CENTER are also predefined to be port names with the same meanings. Users may not re-define them via <port>. Port name ANCHOR also has a special meaning, to be discussed later.[5]

Also allowed is edge segment number plus distance along that edge. MVG currently permits this in this form (segments count from 1 like XPath, and the second token defaults to 50% for distance along that segment):

<port name="foo" segment="5 30%" />

This is especially valuable for <polygon> and similar. However, this is only meaningful given a normative starting segment. That segment is known for polyline and polygon. Curves are construed as having only 1 segment. For shapes that do not have an obvious starting segment, we define the outermost segment passing through Θ = 0 as that segment. If the segment number is out of range for the object, Θ = 0 is used instead.

Note that the precise placement behavior relative to any of these locations is slightly different for labels, ports, and decorators. A port is actually a point, so on a rectangle, specifying NE or a corresponding angle means a connector actually terminates on that corner. However, a label or decorator occupies space with the object it is bound to, and so needs to know which part of it goes right on the port.

This is easily solved. Labels and decorators are themselves objects, and so can define their own ports. As noted earlier a port named ANCHOR is reserved, which is used as the label's or decorator's place to align with the specified port on the target object. ANCHOR can have its location defined like any other port, but it defaults to the center of the bounding box.

2.3.2 Port reference

Reference to ports may be useful in various places, such as:

  • specifying the attachment target for connectors or similar

  • replacing a point location within <polyline points="..."/> or similar

  • referring to the port's x or y location in cases where SVG expects those as separate items.

But there are a few syntactic issues for using port names. Using a port requires also specifying the object (typically via <use>). SVG references typically specify # plus an ID value using href. But where should MVG put the port name?

  1. It could go on an additional attribute, but that seems a bit clumsy and doesn't fit neatly into existing SVG objects or XPath queries ( these <polyline> could as well be a new <connector> type):

    <polyline start="#obj12" start-port="S" end="#obj30" end-port="NE"/>
    

  2. It could be added as if it were a second fragment identifier, but that is a bit odd:

    <polyline start="#obj12#S" end="#obj30#NE"/>
    

  3. It could be compounded onto the ID so the compound remained an XML NAME, but I think it would be less clear:

    <polyline start="#obj12.S" end="#obj30.NE"/>
    

Considering that such locations could also be quite useful as points, it seems desirable to keep them as single tokens, and to make a way to refer separately to their actual x and y locations (post-resolution). But that means x or y must also be specified somehow.

In addition, it is highly desirable to have a compact, readable way to refer to port locations in XPath expressions (both in general, and for MVG itself).

Getting at such values is possible, but not pretty in XPath:

id(objId)/port[@name = 'NE']/@x

However, the entire second step can be eliminated by this defining that a port definition P on an object has the effect of creating attributes on each <use> of the object (implementations may calculate these lazily). The attributes' names and values are:

  1. P (the same as the port name), whose value is the string of the resolved co-ordinates of the port in that object's viewbox.

  2. P.x (that is, the port name with .x appended), whose value is the x coordinate of the port; and

  3. P.y (that is, the port name with .y appended), whose value is the y coordinate of the port.

Note that all these names are still valid XML NAMEs. The dots here are why port names are not allowed to contain dots. In XPath, the expression shown above then shortens to this:

id(objId)/@NE.x

This is just normal XPath given the generated attributes just defined (that is, the XPath implementation doesn't have to know that . is special here). Within a <symbol> or similar definition, the ID for a particular <use> of it is not known. This means we need also to add it to the available attributes (as use_id).

The same syntax could be embedded within points or other attributes for implementations that provide no support for more complete XPath expression syntax in {}.

There is some added value in having access to properties of an object more generally, particularly width and height, as well as the handy x+width and y+height, which are the opposite corner of the bounding box (perhaps as x2 and y2. It requires little effort to support the form above for all SVG elements and attributes (the XPath would just work). Indeed, it would typically take more programming effort to avoid that. In that case the port-specific usage reduces to the convention that the locations of an object's ports are attributed to the object for convenience.

2.3.3 Connector routing

MVG distinguishes the fact of connection from the routing chosen to render it. Routing algorithms are highly varied, and differ greatly in complexity. Thus, MVG defines names for several general approaches, but does not require apps to implement any but line and polyline, which have the same behaviors as those SVG elements.

MVG introduces a routing attribute for those elements, which is a pragmatic hint that renderers may treat as a request for a particular routing method. If an unsupported routing method is requested, apps may simply fall back to line for lines, and polyline for polylines.

See [Wybr06] and [Tama87] for general information on routing. The defined routing method names in MVG are:

  • direct (straight line from port to port; as noted in [DeRo20] this may be ugly, but at least expresses the correct connection);

  • poly (user-specified waypoints)

  • orthogonal (alternating horizontal and vertical segments, with the first and last segments no colinear with the face(s) adjacent to the respective port) [Wybr09]

  • curved (for example, using spline interpolation) [Dobk97] and [Frei01]

  • force-directed this acts as if the connectors contain springs, and minimizes stretching force [Eade84] and [Fruc91] Such methods are sometimes integrated with movement of the endpoint shapes

  • auto (obstacle avoidance, not otherwise specified). [Dobk97] and [Dwye09]

  • bundling some algorithms also combine parts of connectors to reduce crowding [Holt06]

Additional types of routing may be supported by implementations, and should be named beginning with x-.

MVG also provides a hint to control what happens when connectors cross. Common renderings are (a) do nothing, (b) open a small gap for one, or (c) render a half-circle hop for one. These can be expressed in MVG as shown below, but applications are not required to implement them. If an unsupported value is requested, the attribute is ignored, and SVG's default behavior (essentially none) applies.

<mvg:connector mvg:crossings="bridge|gap|none"...>

Connectors, labels, and lines may also have ports,

3. Design Principles

MVG provides a few elements in a separate namespace from SVG. All can be implemented fairly easily in the sort of environment where SVG is in use, and all can be mapped to vanilla SVG 1. I provide a pre-processor implementation that does just that.

MVG's additions should be highly intuitive to users of XML, XSLT, and SVG, because they re-use notions that are very well known there. The overall goal is to make MVG more descriptive, so users can define shapes and other constructs that correspond better to their own conceptual models, and that greatly reduce tedium.

4. Additional Shapes

A review of shape inventories across multiple drawing apps revealed these shapes which are difficult to implement merely via SVG <symbol>, even with MVG parameterization added. Usually this is because they need iteration or non-trivial conditional logic. An MVG implementation should provide them. I provide implementations of them in Python for convenience.

The specific criteria for inclusion here are that a shape showed up in at least one of the drawing app inventories considered, or greatly eases construction of other shapes; and that expressing it in SVG 1.x requires either

  • an unbounded number of elements or coordinates as a function of its parameters — O(n) SVG for O(1) conceptual content, or

  • a fixed number of coordinates that are opaque encodings of a small parameter set, with the parametric structure lost. The first criterion is the stronger case; the second covers shapes like block arrows where the vertex count is fixed but coordinates are non-recoverable functions of shaft-width, head-width, head-depth, etc.

All these elements also allow the transform or rotate attributes common in SVG.

4.1 mvg:rpolygon

This represents a regular polygon by its center, radius, number of sides, and rotation: @cx, @cy, @r, @sides.

4.2 mvg:roundpolygon

This represents a polygon (not necessarily regular) but with uniform rounded corners. It is specified like SVG <polygon>, but adding @corner-r to represent the radius for the rounded corners.

4.3 mvg:star

A star polygon, in which alternating vertices have radii of r-inner and r-outer. @cx, @cy, @n, @r-outer, @r-inner. n should typically be even. Based on Inkscape sodipodi:star.

4.4 mvg:arc

This is similar to ellipse, but can draw just a portion: a circular or elliptical arc. @cx, @cy, @r, @start-angle, @end-angle. SVG has circle and ellipse but lacks this useful case. Drawn clockwise.

4.5 mvg:smoothcurve

This draws a smooth spkine curve (such as Catmull-Rom) through @points knots. User specifies knots only; preprocessor computes Bézier control points. This can eliminate many cases where SVG ends up containing of large static coordinate dumps being briefer, more perspicuous, and propertly scalable.

4.6 mvg:wave

This draws basic sine, cosine, and other periodic curves. @x1 @y1 @x2 @y2, @type (sine | square | sawtooth | triangle), @amplitude, @frequency, @phase. x and y represent placement of the origin. This covers the document shape's bottom edge, but is generally useful in technical work. Phase is measured in degrees. Phase 0 must be defined for each wave type, for example sine rises rightward from (0,0). The conventions I have encountered for the others seem to prefer falling from their maximum amplitude.

type permits x- prefixed values for extension, and such extensions must specify their initial phase state.

4.7 mvg:spiral

Draw an Archimedean spiral. @cx, @cy, @r-start, @r-end, @turns, @t0 (start fraction, 0–1). Subsumes Inkscape sodipodi:spiral.

4.8 mvg:curly-brace

This represents the curly braces commonly needed in technical drawing. @x1 @y1 @x2 @y2 (endpoints), @depth (bow depth), @orientation (left | right). Consists of three Bézier curves whose control points are computed from the parameters.

4.9 mvg:tile

This provides simple iteration across x and/or y. Within a bounding box, repeats a child element at regular intervals of dx and dy. For example:

<mvg:tile x="0" y="0" width="400" height="300"
         dx="50" dy="50"
         nx="8" ny="6"
         odd-row-phase-x="0.5">
  <mvg:ngon sides="6" r="20" cx="0" cy="0" style="fill:none; stroke:#ccc"/>
</mvg:tile>

dx/nx and dy/ny are complementary pairs: specify either spacing or count; the other is derived from the bounding box dimension. If both are given, count takes precedence and spacing is computed.

Phase attributes @phase-x, @phase-y offset the whole pattern relative to the bounding box. @odd-row-phase-x, @even-row-phase-x, @odd-col-phase-y, @even-col-phase-y do this for alternating rows and columns in units of dx/dy, as needed for brick, hexagonal, and some other layouts). Rows and columns count from 1. All phase attributes default to 0.

Basic grids fall out as a special case: a horizontal line child repeated at dy intervals, plus a vertical line child repeated at dx intervals. A minor grid can be handled via two nested mvg:tile elements with different stroke styles.

4.10 itransform

This is somewhat similar to <tile>, but instead of simply moving the sub-object across the referencing object's bounding box, it repeatedly applies a transform. This enables positioning things across an affine path.

4.11 coil (from TikZ)

4.12 mvg:block-arrow

This provides an (optionally filled) directional arrow shape. @direction takes the same values as the angle parameter of <port>s. @shaft-width, @head-width, @head-depth define basic arrowhead dimension. @tip uses the arrowhead types defined below.

4.13 block_arc (from OOXML)

This is basically like arc (see above), but draws the arc twice, separated by @shaft-width and centered at the given r.

4.14 Line types

SVG <line> already provides extensive control over stroke rendering. Comparison with multiple drawing apps suggested only a few additions, all expressed as mvg: presentation attributes applicable to any stroked element (shapes, connectors, or standalone lines):

@mvg:crossings — controls what happens when connectors cross.[6] Three values: none (do nothing, the SVG default), gap (open a small break in the lower connector), and bridge (render a half-circle hop). Implementations are encouraged but not required to implement all three; the interaction with routing choice is noted in §2.3.3.

@mvg:stroke-repeat and @mvg:stroke-gap — draws multiple copies of the stroke, to make double, triple, or similar lines. stroke-gap control blank space between the repetitions. The entire set of strokes is centered where a single stroke would be centered. The result if this is used with dashed lines is not defined.

@mvg:corner-radius — generalizes SVG's rx/ry on <rect> to <polygon> and <polyline>, with a single uniform radius (no rx/ry distinction). Deliberately avoids the rx/ry names, which mean something different on <ellipse>.

With these additions, nearly all the line-type capabilities of the surveyed apps are covered.

4.15 Arrowheads

Drawing apps also commonly provide libraries of arrowheads, which can attach to the ends of (at least) lines. My review found high overlap on basic forms, but much variation at the edges. A large share of the variations, however, are easily constructible with general SVG and MVG constructs.

MVG defines but does not require, an <mvg:arrowhead> object that works like SVG <marker> but has settings that can produce a wide range of arrowheads. Arrowhead size is governed by the bounding box and transform on <mvg:arrowhead>, just like SVG <marker>.

The attribute are those of <marker>, plus:

  • shape: either none (or ), or one of triangle, vee, dot, diamond, box, tee, curve, crow (drawn from ER diagrams). Extensions should be prefixed by x-. These are the same as in graphviz once you remove affixes such as used in odiamond (open diamond). The affixes are covered by the other settings, which also make them slightly more general than in graphviz. inv in graphviz means a 180° rotation, so can be done with SVG's ubiquitous transform or with marker orient. graphviz normal is just triangle.

Because Unicode has a huge variety of useful arrows (e.g., U+2190–U+21FF), dingbats, and other shapes, MVG defines the convention that if shape is set to a single Unicode character, that character is used. The 0 rotation has the character readable from the perspective on one standing on the line leading up to it, and scaled to fill the <marker> viewbox.

  • filled: open, halfopen, or filled (the default).

  • clipped: left, right, or full (the default)

MVG does not address the fact that graphviz allows stacking a sequence of arrowheads onto a line-end.

Several other arrowhead types I found are addressable by the Unicode-character alternative:

  • some EE conventions (primarily in Europe?) use slash or backslash-like shapes as arrowheads.

  • OOXML has a bracketArr head, that looks like a square bracket.

  • LibreOffice has a wavy or zigzag terminus, for which various tildes (U+0007e, U+FF5E, etc.), wave dash (U+301C), almost equal to (U+2248), or sine wave (U+223F) may suffice.

  • TikZ has quite a few more.

5. Connectables

5.1. Connectors

Connectors do not require a new element, but are simply a useful combination of <line> or <polyline> with parameterization, ports, and the routing pragma. For example:

<mvg:line obj1="id1" port1="e"
              obj2="id2" port2="w"
              routing="direct"
              linetype="dashed"
              arrow1="none" arrow2="filled-triangle">
</mvg:line>

<text> objects to serve as labels may be attached to a connector
just as they are to other shapes.

5.2 Labels

SVG <text> objects benefit from parameters and ports, and can simply refer to the object to which they attach instead of setting x and y position manually. It would instead be feasible to express the attachment the other way. However, that would need to apply to many (all?) shapes and grouping objects, and would need a way to do either raw strings, or objects with more structure such as complex formatting properties or multiple <text> parts.

<text> objects in MVG can define ports just like other objects. By default, their ANCHOR port is placed at the ANCHOR of the referenced object (ANCHOR defaults to the center of the relevant bounding box).

5.3 Decorators

A decorator is the MVG term for a shape (perhaps a <symbol> or <marker>) attached to a host shape to create a new (sub-)class of shape. For example, the commonplace folder shape can be constructed (or construed) as a rectangle with an extra rect or round-rect placed somewhere along the top.

Given MVG as already described, decorators are simply a concept; no additional elements are required. Both shapes can be drawn by an enclosing <symbol> or similar, or one can be attached by setting its location using a port or coordinate reference to the host shape. The decorator's own ANCHOR port aligns with the host's target port, and the decorator inherits the host's coordinate system for rendering purposes.

Decorator positioning reuses the port-location grammar of §2.3.1: @attach names the host port; @anchor names the decorator's own contact point (default: anchor port of the decorator, which itself defaults to center). The @scale-lock attribute (§2.2.3) prevents decorators from scaling disproportionately when the host is resized.

No new element type is required. A decorator is any mvg:symbol instance referred to from an @attach attribute on <mvg:use>:

<mvg:use href="#tab" attach="n" anchor="s"
         mvg:width="{$tab-w}" mvg:height="{$tab-h}" />

The preprocessor resolves the port coordinates and emits an SVG <use> at the computed position.

6. Conclusions

This work defines and implements MVG, a layer over SVG that provides more declarative, user-oriented construction for drawings. Everything in it can be reduced to plain SVG (for example, with the provided pre-processor).

It is mainly intended to make it easier, clearer, and more compact to express drawings descriptively in cases where that is difficult to do in plain SVG. It provides two conformance levels: heiti-conformant (string substitution only — nearly trivial to implement) and kenning-conformant (full XPath AVT expression evaluation). The heiti level paves the way for kenning because the latter merely attaches a better AVT expression evaluator in the same context as the trivial one needed for heiti.

References

[Bell18] Bellamy-Royds, Amelia; Bogdan, Brinza; Lilley, Chris; Schulze, Dirk; Storey, David; and Willigers, Eric. Scalable Vector Graphics (SVG) 2. W3C Candidate Recommendation 04 October 2018. https://www.w3.org/TR/SVG2/

[Berg10] Berglund, Anders; Boag, Scott; Chamberlin, Don; Fernández, Mary F.; Kay, Michael; Robie, Jonathan; and Siméon, Jérôme. XML Path Language (XPath) 2.0 (Second Edition). W3C Recommendation 14 December 2010. https://www.w3.org/TR/xpath20/

[GraphML] Brandes, Ulrik, et al. 2014. Graph Markup Language (GraphML). In Tamassia (ed.), Handbook of Graph Drawing and Visualization. CRC Press. http://cs.brown.edu/people/rtamassi/gdhandbook/chapters/graphml.pdf

[Clar16] Clark, James and DeRose, Steve. XML Path Language (XPath) Version 1.0. W3C Recommendation 16 November 1999 (Status updated October 2016). https://www.w3.org/TR/xpath-10/

[SVG11] Dahlström, Erik; Dengler, Patrick; Grasso, Anthony; Lilley, Chris; McCormack, Cameron; Schepers, Doug; Watt, Jonathan; Ferraiolo, Jon; FUJISAWA, Jun (藤沢 淳); and Jackson, Dean. Scalable Vector Graphics (SVG) 1.1 (Second Edition). W3C Recommendation 16 August 2011. https://www.w3.org/TR/SVG11/

[DeRo14] DeRose, Steven J. “What do we still lack? Or: Prolegomena to any future hypertext system.” Presented at Symposium on HTML5 and XML, Washington, DC, August 4, 2014. In Proceedings of the Symposium on HTML5 and XML. Balisage Series on Markup Technologies, vol. 14 (2014). doi:https://doi.org/10.4242/BalisageVol14.DeRose01

[DeRo18] DeRose, Steven J. “Dynamic Style: Implementing Hypertext through Embedding Javascript in CSS.” Presented at Balisage: The Markup Conference 2018, Washington, DC, July 31 - August 3, 2018. In Proceedings of Balisage: The Markup Conference 2018. Balisage Series on Markup Technologies, vol. 21 (2018). doi:https://doi.org/10.4242/BalisageVol21.DeRose01

[DeRo20] DeRose, Steven. “What is a diagram, really?” Presented at Balisage: The Markup Conference 2020, Washington, DC, July 27 - 31, 2020. In Proceedings of Balisage: The Markup Conference 2020. Balisage Series on Markup Technologies, vol. 25 (2020). doi:https://doi.org/10.4242/BalisageVol25.DeRose01

[Dobk97] Dobkin, D.P.; Gansner, E.R.; Koutsofios, E.; and North, S.C. Implementing a General-Purpose Edge Router. In DiBattista (ed.), Graph Drawing (GD 1997), LNCS 1353, pp. 262–271. Springer, 1997. doi:https://doi.org/10.1007/3-540-63938-1_68

[Domr23] Domrös, Sören; von Hanxleden, Reinhard; Spönemann, Miro; Rüegg, Ulf; and Schulze, Christoph Daniel. The Eclipse Layout Kernel. https://arxiv.org/abs/2311.00533. doi:https://doi.org/10.48550/arXiv.2311.00533

[Draw] JGraph Ltd. draw.io / diagrams.net. https://www.drawio.com

[Dwye09] Dwyer, T. and Nachmanson, L. Fast Edge-Routing for Large Graphs. In Eppstein & Gansner (eds.), Graph Drawing (GD 2009), LNCS 5849, pp. 147–158. Springer, 2009. doi:https://doi.org/10.1007/978-3-642-11805-0_15

[Eade84] Eades, P. A Heuristic for Graph Drawing. Congressus Numerantium 42:149–160, 1984.

[Frei01] Freivalds, K. Curved Edge Routing. In Freivalds (ed.), Fundamentals of Computation Theory (FCT 2001), LNCS 2138, pp. 126–137. Springer, 2001. doi:https://doi.org/10.1007/3-540-44669-9_14

[Fruc91] Fruchterman, T.M.J. and Reingold, E.M. Graph Drawing by Force-Directed Placement. Software: Practice and Experience 21(11):1129–1164, 1991. doi:https://doi.org/10.1002/spe.4380211102

[Gans93] Gansner, E.R.; Koutsofios, E.; North, S.C.; and Vo, K.-P. A Technique for Drawing Directed Graphs. IEEE Transactions on Software Engineering 19(3):214–230, 1993. doi:https://doi.org/10.1109/32.221135

[GVShapes] Graphviz. Node Shapes. https://graphviz.org/doc/info/shapes.html

[Hols20] Holstege, Mary. “XML for Art: A Case Study.” Presented at Balisage: The Markup Conference 2020, Washington, DC, July 27 - 31, 2020. In Proceedings of Balisage: The Markup Conference 2020. Balisage Series on Markup Technologies, vol. 25 (2020). doi:https://doi.org/10.4242/BalisageVol25.Holstege01

[Hols23] Holstege, Mary. “Adventures in Single-Sourcing XQuery and XSLT.” Presented at Balisage: The Markup Conference 2023, Washington, DC, July 31 - August 4, 2023. In Proceedings of Balisage: The Markup Conference 2023. Balisage Series on Markup Technologies, vol. 28 (2023). doi:https://doi.org/10.4242/BalisageVol28.Holstege01

[Holt06] Holten, D. Hierarchical Edge Bundles: Visualization of Adjacency Relations in Hierarchical Data. IEEE Transactions on Visualization and Computer Graphics 12(5):741–748, 2006. doi:https://doi.org/10.1109/TVCG.2006.147

[Inkscape] Inkscape Project. https://inkscape.org

[Kay17a] Kay, Michael, ed. XSL Transformations (XSLT) Version 3.0. W3C Recommendation, 8 June 2017. https://www.w3.org/TR/xslt-30/

[Kay17b] Kay, Michael, ed. XPath and XQuery Functions and Operators 3.1. W3C Recommendation, 21 March 2017. https://www.w3.org/TR/xpath-functions-31/

[Pike67] Pike, Kenneth L. Language in Relation to a Unified Theory of the Structure of Human Behavior. 2nd rev. ed. The Hague: Mouton, 1967. (First published in three parts, 1954–1960.)

[Robi14] Robie, Jonathan; Chamberlin, Don; Dyck, Michael; and Snelson, John. XML Path Language (XPath) 3.0. W3C Recommendation 08 April 2014. https://www.w3.org/TR/xpath-30/

[Sche09a] Schepers, Doug. SVG Parameters 1.0, Part 1: Primer. W3C Working Draft 16 June 2009. https://www.w3.org/TR/2009/WD-SVGParamPrimer-20090616/

[Sche09b] Schepers, Doug. SVG Parameters 1.0, Part 2: Language. W3C Working Draft 16 June 2009. https://www.w3.org/TR/2009/WD-SVGParam-20090616/

[ELK] Schulze, C. Sönke, et al. Eclipse Layout Kernel. https://eclipse.dev/elk/

[Tama87] Tamassia, R. On Embedding a Graph in the Grid with the Minimum Number of Bends. SIAM Journal on Computing 16(3):421–444, 1987. doi:https://doi.org/10.1137/0216030

[TikZ] Tantau, Till. The TikZ and PGF Packages. Manual for version 3.1.x. https://tikz.dev/

[Will94] Williams, Robin. The Non-Designer's Design Book: Design and Typographic Principles for the Visual Novice. Peachpit Press, 1994. ISBN 978-1-56609-159-4.

[Wybr06] Wybrow, M.; Marriott, K.; and Stuckey, P.J. Incremental Connector Routing. In Healy & Nikolov (eds.), Graph Drawing (GD 2005), LNCS 3843, pp. 446–457. Springer, 2006. doi:https://doi.org/10.1007/11618058_40

[Wybr09] Wybrow, M.; Marriott, K.; and Stuckey, P.J. Orthogonal Connector Routing. In Eppstein & Gansner (eds.), Graph Drawing (GD 2009), LNCS 5849, pp. 219–231. Springer, 2009. doi:https://doi.org/10.1007/978-3-642-11805-0_22



[1] Some browsers have implemented the SVG 2 proposal [Bell18] to add refX and refY to <symbol> (from <marker>).

[2] Is the reader certain that is even an octagon? It has 8 points and only 3 distinct magnitudes; but so would a rectangle traced twice.

[3] For any object that does not have {} in the body, no change is necessary, and those can be cached up front. SVG does not use braces for any of its own syntax.

[4] The English and proto-Germanic cognates are of course purely accidental.

[5] Note that directions like NE are not defined to be at 315° on the bounding box, but at the corner of it, projected toward the center until hitting non-background such as the shape's visual edge.

[6] This may be better limited to only <mvg:connector>?

Author's keywords for this paper:
SVG; XPath; XSLT; Descriptive Markup; Vector graphics

Steven J. DeRose

Consultant

Steve DeRose has been working with electronic document and hypertext systems since 1979. He holds degrees in Computer Science and Linguistics and a Ph.D. in Computational Linguistics from Brown University.

He co-founded Electronic Book Technologies in 1989 to build the first SGML browser and retrieval system, DynaText, and has been deeply involved in document standards including XML, TEI, HyTime, HTML 4, XPath, XPointer, EAD, Open eBook, OSIS, and others. He has served as adjunct faculty at Brown and Calvin Universities and has written many papers, two books, and more than fifteen patents. Most recently he has been working as a consultant in text analytics.