# How Node Styles Are Converted from Graphviz to Draw.io: A Technical Deep Dive

> Discover how Graphviz node styles are converted to Draw.io. Learn the technical process of mapping shapes, colors, and line styles from SVG to Draw.io XML using hbmartin/graphviz2drawio.

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

---

**The conversion extracts visual attributes like fill, stroke, and dash patterns from SVG elements rendered by PyGraphviz, maps Graphviz shapes to Draw.io style templates in [`Styles.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/Styles.py), and populates placeholder values to generate the final XML style strings.**

The `hbmartin/graphviz2drawio` library automates the migration of Graphviz diagrams to the Draw.io format by transforming DOT language attributes into equivalent XML styling. Understanding how **node styles are converted from Graphviz to Draw.io** requires examining the data-driven pipeline that bridges these two visualization formats.

## The Three-Stage Conversion Pipeline

The transformation operates through a sequence that renders, parses, and re-serializes graphical data without hard-coded attribute mappings.

### Stage 1: SVG Generation via PyGraphviz

In [`graphviz2drawio/graphviz2drawio.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/graphviz2drawio.py), the `convert()` function receives either a Graphviz object or raw DOT source. It invokes PyGraphviz to render the diagram as SVG using `graph.draw(..., format="svg")`. This SVG serves as the canonical intermediate representation containing all resolved visual attributes.

### Stage 2: SVG Parsing and Node Extraction

The `parse_nodes_edges_clusters` function in [`graphviz2drawio/models/SvgParser.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/models/SvgParser.py) traverses the SVG DOM and identifies elements with `class="node"`. For each node element, it delegates instantiation to `NodeFactory.from_svg` in [`graphviz2drawio/mx/NodeFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/NodeFactory.py).

The factory detects the geometric SVG element—whether `<rect>`, `<ellipse>`, `<polygon>`, or `<path>`—to determine the node shape. It then extracts raw styling through helper methods:

- **`_extract_fill`**: Parses the `fill` attribute and opacity, returning either a hex color string or a gradient tuple `(start_color, end_color, direction)`.
- **`_extract_stroke`**: Reads `stroke`, `stroke-width`, and `stroke-dasharray` to capture border color, thickness, and dash patterns.

### Stage 3: Style Template Population

Each `Node` object calls `get_node_style()` (defined in `graphviz2drawio/mx/Node.py#L50-L77`) to assemble the final Draw.io style string. This method queries `Styles.get_for_shape(self.shape)` to retrieve the appropriate template from the `Styles` enum, then substitutes placeholders like `{fill}`, `{stroke}`, `{stroke_width}`, `{dashed}`, and `{vertical_align}` with values extracted during Stage 2.

## Mapping Graphviz Shapes to Draw.io Templates

The **shape-to-style mapping** resides in [`graphviz2drawio/mx/Styles.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Styles.py). The `Styles` enum defines base templates as string constants with named placeholders:

```python

# Conceptual representation from Styles.py

NODE = "verticalAlign={vertical_align};html=1;rounded=0;strokeColor={stroke};fillColor={fill};strokeWidth={stroke_width};dashed={dashed};"
ELLIPSE = "ellipse;" + NODE
CIRCLE = "ellipse;aspect=fixed;" + NODE

```

The `Styles.get_for_shape` method (lines 88-102) consults an internal `_shape_to_style` dictionary. If a Graphviz shape lacks a specific entry, it safely falls back to `Styles.NODE` (the default rectangle template).

## Handling Gradients and Line Styles

When `NodeFactory._extract_fill` detects a gradient, `Node.get_node_style` appends additional fragments to the base template:

- **`gradientColor`**: The secondary color of the gradient.
- **`gradientDirection`**: The angle or direction of the fade.

For line styling, the presence of a `stroke-dasharray` attribute in the SVG triggers `dashed=1` in the template, converting Graphviz dotted or dashed borders into Draw.io equivalents.

## Serializing to Draw.io XML

The final assembly occurs in [`graphviz2drawio/mx/MxGraph.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/MxGraph.py). The `MxGraph` class iterates over all node objects, calls `node.get_node_style()` for each, and injects the resolved string into the `style` attribute of the corresponding `<mxCell>` XML element. This produces the complete Draw.io document structure consumable by the diagrams.net editor.

## Practical Conversion Example

The following Python script demonstrates how Graphviz attributes translate through the pipeline:

```python
from graphviz2drawio import convert
from pygraphviz import AGraph

dot_source = """
digraph G {
    a [shape=box, style=filled, fillcolor="#ffdddd", color="#aa0000"];
    b [shape=circle, style=filled, fillcolor=lightblue];
    a -> b;
}
"""

# Convert to Draw.io XML

drawio_xml = convert(AGraph(string=dot_source))
print(drawio_xml[:500])

```

**Internal transformation breakdown:**

1. **Shape mapping**: `box` resolves to `Shape.RECT`, selecting `Styles.NODE` (rectangle template).
2. **Fill application**: The `#ffdddd` value populates the `{fill}` placeholder.
3. **Stroke application**: The `#aa0000` border color fills the `{stroke}` slot.
4. **Output**: The resulting `<mxCell>` for node `a` contains `style="verticalAlign=middle;html=1;rounded=0;strokeColor=#aa0000;fillColor=#ffdddd;strokeWidth=1;dashed=0;"`.

## Summary

- **SVG intermediation**: The library uses PyGraphviz to render Graphviz to SVG, creating a parseable source of truth for all visual attributes.
- **Data-driven extraction**: `NodeFactory` reads `fill`, `stroke`, `stroke-width`, and `stroke-dasharray` directly from SVG elements, supporting both solid colors and gradients.
- **Template-based styling**: The `Styles` enum maps detected shapes to parameterized Draw.io style strings, ensuring consistent XML generation.
- **Dynamic assembly**: `Node.get_node_style` merges extracted values with templates, while `MxGraph` handles final XML serialization.

## Frequently Asked Questions

### How does the library handle unsupported Graphviz shapes?

According to the source code in [`graphviz2drawio/mx/Styles.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/mx/Styles.py), the `get_for_shape` method implements a fallback mechanism. If a Graphviz shape name is not found in the `_shape_to_style` lookup table, it automatically defaults to `Styles.NODE`, which renders as a standard rectangle in Draw.io.

### Are gradient fills preserved during the conversion?

Yes. The `NodeFactory._extract_fill` method detects SVG gradient definitions and returns them as tuples containing start color, end color, and direction. The `Node.get_node_style` method then appends `gradientColor` and `gradientDirection` parameters to the Draw.io style string, preserving the gradient effect.

### What happens to Graphviz line styles like dotted or dashed?

The `NodeFactory` inspects the `stroke-dasharray` attribute on SVG elements. If present, it sets `dashed=1` in the style attributes dictionary. This value populates the `{dashed}` placeholder in the Draw.io template, accurately reproducing dashed or dotted borders from the original Graphviz diagram.

### Where is the entry point for converting a Graphviz file?

The primary conversion function resides in [`graphviz2drawio/graphviz2drawio.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/graphviz2drawio/graphviz2drawio.py) at lines 13-21. The `convert()` function accepts either a PyGraphviz `AGraph` object or a DOT string, orchestrates the SVG rendering, and returns the complete Draw.io XML document as a string.