How graphviz2drawio Converts Graphviz Clusters and Subgraphs to Draw.io

graphviz2drawio converts Graphviz clusters into rectangular container nodes in Draw.io by rendering the DOT file to SVG, parsing cluster groups, and inserting them before inner nodes to preserve visual layering.

The hbmartin/graphviz2drawio library bridges the gap between Graphviz graph descriptions and Draw.io diagrams. When your Graphviz source contains clusters (subgraphs with the cluster attribute), the tool follows a specific pipeline to transform these grouped containers into properly layered Draw.io shapes.

The Conversion Pipeline

The conversion process follows eight distinct steps to transform Graphviz clusters into Draw.io-compatible XML.

1. Load and Normalize the Graph

The pipeline begins in graphviz2drawio/graphviz2drawio.py within the _load_pygraphviz_agraph function. This step normalizes user input—whether a file path, string, or pygraphviz.AGraph object—into a consistent pygraphviz.AGraph instance.

2. Render to SVG

The convert function renders the normalized graph using the specified layout engine (defaulting to dot). This produces SVG bytes that encode the visual structure, including cluster boundaries as <g class="cluster"> elements.

3. Parse SVG Elements

In graphviz2drawio/models/SvgParser.py, the parse_nodes_edges_clusters method walks the SVG tree. It identifies three classes of top-level <g> elements: node, edge, and cluster. The parser specifically looks for the cluster class attribute to distinguish containers from regular nodes.

4. Extract Cluster Titles

For each cluster group, the parser extracts titles from preceding XML comments or inner <title> elements. If neither exists, the code raises a MissingTitleError to enforce proper labeling.

5. Build Cluster Nodes

The NodeFactory.from_svg function in graphviz2drawio/mx/NodeFactory.py constructs a Node object for each cluster. It applies labelloc="t" to position the title at the top and derives the shape from the SVG geometry (typically a rectangle).

6. Store Collections Separately

The parser returns three distinct collections: nodes, edges, and clusters (stored as an OrderedDict of Node objects). This separation allows the assembly stage to handle clusters differently from regular graph elements.

7. Assemble the Draw.io Model

In graphviz2drawio/mx/MxGraph.py, the __init__ method receives all three collections. Crucially, it adds clusters first to the model. This ensures they render underneath ordinary nodes, matching Graphviz's visual convention where containers appear behind their contents.

8. Emit Final XML

The MxGraph.value method generates the final XML string. After all objects are added with proper layering, this method produces the complete Draw.io diagram ready for import.

How Clusters Become Containers

In Draw.io, every visual element is a cell. Graphviz clusters function as rectangular containers that group other nodes, but Draw.io lacks a native "cluster" primitive. The conversion treats each cluster as a regular node with a rectangular shape, but leverages the rendering order to create the container effect.

By inserting cluster nodes before their inner contents in the XML sequence, graphviz2drawio ensures that Draw.io draws the background rectangle first. The cluster title persists as the node's gid attribute, displayed at the top via the labelloc="t" setting applied during node construction.

Handling Non-Cluster Subgraphs

Graphviz supports sub-graphs that lack the cluster attribute. These structural groupings do not generate <g class="cluster"> elements in the SVG output. Consequently, the SVG parser ignores them entirely.

Only sub-graphs explicitly declared as cluster survive the conversion process. Ordinary sub-graphs flatten into their constituent nodes and edges, losing their grouping container in the final Draw.io output.

Code Examples

Convert a .gv file containing clusters:

from graphviz2drawio import convert

drawio_xml = convert("examples/cluster_demo.gv")
with open("cluster_demo.drawio", "w") as f:
    f.write(drawio_xml)

Convert from a DOT string directly:

dot_source = """
digraph G {
    subgraph cluster_A {
        label="Group A";
        a1 -> a2;
    }
    subgraph cluster_B {
        label="Group B";
        b1 -> b2;
    }
    a1 -> b1;
}
"""
drawio_xml = convert(dot_source)  # layout performed with 'dot'

Both examples produce Draw.io XML where Group A and Group B appear as rectangular containers with top-aligned titles, with internal nodes drawn above the container backgrounds.

Key Implementation Files

  • graphviz2drawio/graphviz2drawio.py: Contains the public convert API, handles SVG generation via _load_pygraphviz_agraph, and orchestrates the parsing and graph assembly pipeline.
  • graphviz2drawio/models/SvgParser.py: Implements parse_nodes_edges_clusters to walk the SVG tree, identify <g class="cluster"> elements, and extract titles from comments or <title> tags.
  • graphviz2drawio/mx/NodeFactory.py: Defines NodeFactory.from_svg to construct Node objects for clusters, applying labelloc="t" for top-positioned titles and storing the cluster name as gid.
  • graphviz2drawio/mx/MxGraph.py: Builds the final Draw.io XML structure in MxGraph.__init__, ensuring clusters populate the model before inner nodes to maintain correct visual layering, and emits output via MxGraph.value.

Summary

  • graphviz2drawio renders Graphviz DOT files to SVG, then parses the output to identify cluster containers via <g class="cluster"> elements.
  • Clusters are converted to rectangular Node objects with top-aligned titles using labelloc="t" and stored in an OrderedDict to preserve order.
  • The tool inserts clusters into the Draw.io model before their contents, creating the visual effect of background containers.
  • Only sub-graphs with the explicit cluster attribute are preserved; non-cluster sub-graphs flatten into individual nodes and edges.
  • The conversion raises MissingTitleError if a cluster lacks a label or title element in the SVG.

Frequently Asked Questions

How does graphviz2drawio handle cluster labels?

The parser extracts cluster labels from XML comments preceding the <g> element or from inner <title> tags. If neither exists, the code raises a MissingTitleError. The label is stored as the node's gid and displayed at the top using labelloc="t".

Why do clusters appear as nodes in the Draw.io output?

Draw.io represents all diagram elements as cells without a native grouping container concept. graphviz2drawio treats clusters as rectangular nodes but renders them before their contents, creating the visual appearance of a background container that groups related elements.

Can I convert Graphviz sub-graphs that are not clusters?

No. The SVG parser specifically filters for <g class="cluster"> elements. Sub-graphs lacking the cluster attribute do not generate this class in the SVG output and are ignored during conversion, resulting in flattened node and edge structures.

Which layout engines does graphviz2drawio support for cluster conversion?

The tool supports any layout engine available through pygraphviz, defaulting to dot. You can specify alternatives like neato or fdp when calling the convert function, though dot is recommended for proper hierarchical cluster layouts.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →