# How GraphViz Edge Labels Are Converted to draw.io Output: The Complete Technical Pipeline

> Discover how GraphViz edge labels convert to draw.io output. Learn how labels become separate mxCell objects with custom styling and relative geometry for HTML-formatted text on connectors.

- Repository: [Harold Martin/graphviz2drawio](https://github.com/hbmartin/graphviz2drawio)
- Tags: deep-dive
- Published: 2026-03-03

---

**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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/EdgeFactory.py) (lines 25-30)
- **Key Function**: `from_svg` parses the SVG group and instantiates `Text` objects

### 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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Edge.py) (lines 13-35)
- **Storage**: The `labels` attribute maintains the list of `Text` instances

### 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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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` (typically `edgeLabel;html=1;strokeColor=none`)
- `parent=edge.sid` (attaches to the edge cell)
- `value=edge.value_for_labels()` (the HTML-encoded string)
- `vertex="1"` and `connectable="0"`

- **Source**: [`graphviz2drawio/mx/MxGraph.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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:

```python
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:**

```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=&quot;Helvetica&quot; size=&quot;12&quot; color=&quot;#000000&quot;>first</font></div><div><font face=&quot;Helvetica&quot; size=&quot;12&quot; color=&quot;#000000&quot;>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`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/EdgeFactory.py)**: Parses SVG `<text>` tags into `Text` objects and builds the `Edge` with its `labels` list via `from_svg`.

- **[`graphviz2drawio/mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Edge.py)**: Stores the label list and provides `value_for_labels()` that transforms the list into draw.io-compatible HTML.

- **[`graphviz2drawio/mx/Text.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Text.py)**: Converts individual text elements into HTML fragments through `get_mx_value()`, handling font properties.

- **[`graphviz2drawio/models/SvgParser.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/models/SvgParser.py)**: Merges duplicate edges and aggregates their label lists using `key_for_label` logic in `parse_nodes_edges_clusters`.

- **[`graphviz2drawio/mx/MxGraph.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/MxGraph.py)**: Emits the label `<mxCell>` when an edge has at least one label and attaches geometry via `add_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 the `parent` attribute.

- **HTML Encoding**: The `value_for_labels()` method generates HTML with `<div>` wrappers for multi-line labels, preserving GraphViz formatting through `Text.get_mx_value()`.

- **Duplicate Consolidation**: The `SvgParser` merges 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_LABEL` style (typically `edgeLabel;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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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`](https://github.com/hbmartin/graphviz2drawio/blob/main/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.