How GraphViz Edge Labels Are Converted to draw.io Output: The Complete Technical Pipeline
Edge labels are converted into separate mxCell objects with specialized edgeLabel styling and relative geometry that attach to their parent edge cells, allowing draw.io to render GraphViz annotations as HTML-formatted text that follows the connector line.
When converting diagrams from GraphViz DOT format to draw.io (mxGraph) XML, handling edge labels requires a sophisticated multi-stage pipeline. The hbmartin/graphviz2drawio library implements this by parsing SVG text elements, merging duplicate connections, and generating dedicated label cells with relative geometry. Understanding how edge labels in draw.io output are structured helps developers customize the conversion process and troubleshoot formatting issues.
The Edge Label Conversion Pipeline
The conversion process treats edge labels as distinct mxCell objects rather than inline attributes. This approach preserves formatting and positioning while ensuring draw.io can manipulate the labels independently. The pipeline consists of six distinct phases, each handled by specific modules in the codebase.
Phase 1: Extracting Text from SVG
The process begins in EdgeFactory.from_svg, which walks the SVG <g class="edge"> element and collects every <text> child node. Each text element is converted to a Text instance via Text.from_svg. These objects capture the raw label content and formatting metadata from the GraphViz SVG output.
- Source:
graphviz2drawio/mx/EdgeFactory.py(lines 25-30) - Key Function:
from_svgparses the SVG group and instantiatesTextobjects
Phase 2: Building the Edge Object
The collected labels are passed as a list to the Edge constructor. The Edge class stores these labels unchanged in its internal state, maintaining the association between the geometric edge and its textual annotations until XML generation occurs.
- Source:
graphviz2drawio/mx/Edge.py(lines 13-35) - Storage: The
labelsattribute maintains the list ofTextinstances
Phase 3: Merging Duplicate Edges
In SvgParser.parse_nodes_edges_clusters, the system identifies edges that share the same logical connection using edge.key_for_label. When duplicate edges are detected, they are de-duplicated and their label arrays are concatenated using existing_edge.labels.extend(edge.labels). This ensures that multiple labels targeting the same source-target pair appear on a single connector.
- Source:
graphviz2drawio/models/SvgParser.py(lines 64-73) - Logic: Edges with identical keys have their label lists merged
Phase 4: Rendering HTML Label Values
The Edge.value_for_labels() method transforms the label list into draw.io-compatible HTML. The first label is inserted raw, while subsequent labels are wrapped in <div> elements to force line breaks. Each individual label's HTML is produced by Text.get_mx_value(), which handles font face, size, and color encoding.
- Source:
graphviz2drawio/mx/Edge.py(lines 24-29) - Formatting: First label raw, additional labels wrapped in
<div>tags
Phase 5: Creating the Label mxCell
In MxGraph.add_edge, the system checks if len(edge.labels) > 0. When labels exist, it creates a new <mxCell> with:
-
id="label_{edge.sid}" -
style=Styles.EDGE_LABEL.value(typicallyedgeLabel;html=1;strokeColor=none) -
parent=edge.sid(attaches to the edge cell) -
value=edge.value_for_labels()(the HTML-encoded string) -
vertex="1"andconnectable="0" -
Source:
graphviz2drawio/mx/MxGraph.py(lines 59-71) -
Element: A dedicated vertex cell attached to the edge parent
Phase 6: Attaching Relative Geometry
Finally, add_mx_geo attaches a <mxGeometry as="geometry" relative="1"/> entry to the label cell. The relative="1" attribute instructs draw.io to position the label automatically along the edge line, maintaining visual association regardless of how the connector is manipulated.
- Source:
graphviz2drawio/mx/MxGraph.py(lines 95-122) - Geometry: Relative positioning allows the label to follow the edge
Practical Example: Converting Labeled Edges
The following example demonstrates how multiple labels on duplicate edges are consolidated into a single label cell:
import graphviz2drawio as g2d
dot = """
digraph {
A -> B [label="first"];
A -> B [label="second"];
}
"""
# Convert the DOT source to a draw.io XML string
mx_xml = g2d.graphviz2drawio(dot, directed=True)
print(mx_xml)
Relevant excerpt from the generated XML:
<mxCell id="e1" style="edgeStyle=elbowEdgeStyle;strokeColor=#000000;endArrow=block;endFill=1;" parent="1" edge="1" source="n0" target="n1"/>
<mxCell id="label_e1" style="edgeLabel;html=1;strokeColor=none;" parent="e1"
value="<div><font face="Helvetica" size="12" color="#000000">first</font></div><div><font face="Helvetica" size="12" color="#000000">second</font></div>"
vertex="1" connectable="0">
<mxGeometry as="geometry" relative="1"/>
</mxCell>
The two label strings (first and second) were collected during parsing, concatenated into a single label cell during the merge phase, and wrapped in <div> elements to preserve line-break ordering in the final draw.io output.
Key Source Files and Functions
-
graphviz2drawio/mx/EdgeFactory.py: Parses SVG<text>tags intoTextobjects and builds theEdgewith itslabelslist viafrom_svg. -
graphviz2drawio/mx/Edge.py: Stores the label list and providesvalue_for_labels()that transforms the list into draw.io-compatible HTML. -
graphviz2drawio/mx/Text.py: Converts individual text elements into HTML fragments throughget_mx_value(), handling font properties. -
graphviz2drawio/models/SvgParser.py: Merges duplicate edges and aggregates their label lists usingkey_for_labellogic inparse_nodes_edges_clusters. -
graphviz2drawio/mx/MxGraph.py: Emits the label<mxCell>when an edge has at least one label and attaches geometry viaadd_mx_geo.
Summary
-
Separate mxCell Objects: Edge labels are not inline attributes but distinct cells with
vertex="1"that attach to their parent edge via theparentattribute. -
HTML Encoding: The
value_for_labels()method generates HTML with<div>wrappers for multi-line labels, preserving GraphViz formatting throughText.get_mx_value(). -
Duplicate Consolidation: The
SvgParsermerges edges sharing the same source-target pair, concatenating their labels into a single annotation. -
Relative Geometry: Labels use
<mxGeometry relative="1"/>to maintain position along the edge line when the diagram is edited in draw.io. -
Style Constants: Labels receive the
EDGE_LABELstyle (typicallyedgeLabel;html=1;strokeColor=none) to ensure proper rendering in draw.io.
Frequently Asked Questions
Why are edge labels stored as separate mxCell objects instead of inline attributes?
Draw.io (mxGraph) requires edge labels to be separate vertex cells with connectable="0" in order to support rich HTML formatting, positioning, and independent manipulation. Storing labels as the value attribute of the edge cell itself would limit formatting options and prevent the label from following the edge when it is moved or reshaped. The separate cell approach with relative geometry ensures the label behaves as a first-class diagram element.
How does the converter handle multiple labels on the same edge?
When SvgParser.parse_nodes_edges_clusters detects multiple edges connecting the same source and target (identified by key_for_label), it merges them into a single edge instance and concatenates their label arrays using existing_edge.labels.extend(edge.labels). During XML generation, Edge.value_for_labels() renders the first label directly and wraps subsequent labels in <div> elements, creating a vertical stack of annotations in the final draw.io output.
Can I customize the HTML formatting of edge labels in the output?
Yes, by modifying the Text.get_mx_value() method in graphviz2drawio/mx/Text.py, you can change how individual labels are wrapped in HTML font tags. Additionally, adjusting Edge.value_for_labels() in graphviz2drawio/mx/Edge.py allows customization of the container structure—such as changing <div> wrappers to <br/> separators or adding CSS classes. The library encodes the HTML for XML compatibility before insertion into the value attribute.
What happens to edge labels when duplicate edges are merged?
Duplicate edges are consolidated during the parsing phase in SvgParser, where their label lists are combined through list extension. This means if two GraphViz edges connect the same nodes with different labels, both labels appear on the single resulting draw.io connector. The order of labels in the final output corresponds to the order in which the edges were processed and merged in the SVG parsing loop.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →