How graphviz2drawio Handles Special Graphviz Attributes During SVG-to-Draw.io Conversion

graphviz2drawio converts Graphviz SVG output into draw.io's mxGraph format by extracting and transforming special attributes—including fill colors, opacity values, dash styles, gradients, and shape definitions—through specialized factory classes that map these properties to equivalent draw.io style parameters.

The hbmartin/graphviz2drawio library bridges the gap between Graphviz diagram generation and draw.io's interactive editing environment. During conversion, it parses the SVG output produced by Graphviz tools and normalizes special visual attributes into the mxGraph XML format that draw.io consumes. This article examines the specific mechanisms used to handle Graphviz-specific styling during this SVG-to-draw.io transformation.

The Three Core Factories for Attribute Extraction

The conversion logic centers on three specialized factories that walk the SVG tree and extract Graphviz-specific visual properties:

  • NodeFactory (in graphviz2drawio/mx/NodeFactory.py): Extracts node geometry, fill and stroke colors, opacity, dash style, and gradient references from SVG <polygon> and <ellipse> elements. The from_svg method (lines 25-95) serves as the primary entry point for node processing.
  • EdgeFactory (in graphviz2drawio/mx/EdgeFactory.py): Pulls edge geometry, stroke color, opacity, width, and dash style from the <path> elements that Graphviz emits for edges, implemented in from_svg (lines 18-55).
  • Text (in graphviz2drawio/mx/Text.py): Reads font-family, size, color, and style (bold/italic) from SVG <text> elements via the from_svg class method (lines 44-56).

Converting Colors and Opacity

Graphviz emits raw color attributes like fill="#ff0000" and stroke="#00ff00" that draw.io understands directly. However, opacity handling requires special processing because draw.io does not support the fill-opacity and stroke-opacity attributes natively.

The Opacity Blending Algorithm

Both NodeFactory and EdgeFactory call adjust_color_opacity from graphviz2drawio/mx/utils.py to blend semi-transparent colors against a white background, producing new opaque hex values:

def adjust_color_opacity(hex_color: str, opacity: float) -> str:
    hex_color = hex_color.lstrip("#")
    r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
    r = int(r * opacity + 255 * (1 - opacity))
    g = int(g * opacity + 255 * (1 - opacity))
    b = int(b * opacity + 255 * (1 - opacity))
    return f"#{r:02x}{g:02x}{b:02x}"

In NodeFactory._extract_fill (lines 100-107), the factory checks for fill-opacity and applies the transformation:

if "fill-opacity" in g.attrib and fill != MxConst.NONE:
    fill = adjust_color_opacity(fill, float(g.attrib["fill-opacity"]))

Similarly, EdgeFactory.from_svg (lines 34-40) performs the same blending for stroke colors when stroke-opacity is present in the path element's attributes.

Handling Line Styles and Dash Arrays

Graphviz defines dashed lines using the stroke-dasharray SVG attribute, which must be converted to draw.io's boolean dashed flag.

In NodeFactory.from_svg (lines 52-54), the code detects dash patterns on polygon elements:

if "stroke-dasharray" in polygon.attrib:
    dashed = True

For edges, EdgeFactory.from_svg (lines 43-44) sets a style constant when processing the path element:

if "stroke-dasharray" in path.attrib:
    line_style = DotAttr.DASHED

These boolean flags are passed to the Node or Edge constructors and ultimately formatted into the final style string via Styles.NODE or Styles.EDGE constants.

Gradient Support in Graphviz SVG

When Graphviz defines gradients, the SVG fill attribute contains a URL reference like fill="url(#grad1)". The graphviz2drawio library resolves these references to extract gradient definitions.

In NodeFactory._extract_fill (lines 99-107), the code uses a regex pattern to identify gradient URLs:

_fill_url_re = re.compile(r"url\(#([^)]+)\)")

if fill.startswith("url"):
    match = NodeFactory._fill_url_re.search(fill)
    if match is not None:
        return gradients[match.group(1)]

The method returns a tuple (start_color, end_color, direction) that Node.get_node_style (lines 60-66 in graphviz2drawio/mx/Node.py) converts into draw.io's gradientColor and gradientDirection parameters.

Mapping Graphviz Shapes to Draw.io Styles

Graphviz shape names such as box, ellipse, and doublecircle do not map one-to-one to draw.io shapes. The conversion uses Styles.get_for_shape (defined in graphviz2drawio/mx/Styles.py, lines 102-141) which references the _shape_to_style dictionary:

_shape_to_style = {
    Shape.BOX: Styles.NODE,
    Shape.ELLIPSE: Styles.ELLIPSE,
    Shape.DOUBLE_CIRCLE: Styles.DOUBLE_CIRCLE,
    # …

}

When Node.get_node_style executes (line 51 of graphviz2drawio/mx/Node.py), it obtains the correct style via Styles.get_for_shape(self.shape), ensuring the resulting mxGraph XML uses the proper draw.io shape identifiers.

Preserving Text Formatting Attributes

The Text.from_svg method (lines 44-56 in graphviz2drawio/mx/Text.py) extracts font-related attributes from SVG <text> elements and supplies sensible defaults:

  • font-family: Extracted from the font-family attribute
  • font-size: Parsed from font-size with fallback to MxConst.DEFAULT_FONT_SIZE
  • color: Read from the fill attribute
  • font-weight and font-style: Detected to set bold and italic flags

These values are formatted into draw.io HTML-styled labels using the Styles.TEXT_VALUE template.

Complete Conversion Pipeline

The handling of special Graphviz attributes follows a strict seven-step pipeline orchestrated by SvgParser.parse_nodes_edges_clusters in graphviz2drawio/models/SvgParser.py:

  1. Parse SVG Structure: Walk the SVG tree and build a coordinate transformer from the root <svg> element's transform attribute.
  2. Collect Gradients: Execute _extract_gradients to read <linearGradient> and <radialGradient> definitions into a lookup dictionary.
  3. Create Nodes: Invoke NodeFactory.from_svg to build Node objects with geometry, colors, opacity-blended fills, dash flags, and resolved gradients.
  4. Create Edges: Invoke EdgeFactory.from_svg to build Edge objects with stroke properties, opacity adjustments, dash styles, and label texts.
  5. Process Clusters: Handle Graphviz cluster subgraphs as parent containers.
  6. Style Generation: Call Node.get_node_style and Edge formatting methods to generate mxGraph-compatible style strings.
  7. XML Serialization: Assemble the final draw.io XML document with proper parent-child relationships and style attributes.

Practical Implementation Examples

Converting a Graphviz SVG File


# Install the package

pip install graphviz2drawio

# Convert SVG to draw.io format

python -m graphviz2drawio input.svg output.drawio

The command runs the complete pipeline, automatically handling fill-opacity, gradients, dash arrays, and shape mapping without manual intervention.

Inspecting Computed Node Styles

from graphviz2drawio.mx.NodeFactory import NodeFactory
from graphviz2drawio.mx.utils import adjust_color_opacity
from graphviz2drawio.models.SvgParser import parse_nodes_edges_clusters
from pathlib import Path

svg_bytes = Path("example.svg").read_bytes()
nodes, edges, clusters = parse_nodes_edges_clusters(svg_bytes, is_directed=False)

node = nodes["my_node"]
print("mx style string:", node.get_node_style())

# Output: ellipse;verticalAlign=middle;html=1;rounded=0;...

A fill-opacity="0.5" in the source SVG results in a blended fillColor value after processing by adjust_color_opacity.

Accessing Gradient Definitions


# If the SVG contains fill="url(#grad1)"

node = nodes["gradient_node"]
if isinstance(node.fill, tuple):
    start, end, direction = node.fill
    print(f"Gradient: {start}{end}, direction: {direction}")
    # Results in draw.io XML: gradientColor={end};gradientDirection={direction}

Summary

  • graphviz2drawio processes Graphviz SVG output through three specialized factories: NodeFactory, EdgeFactory, and Text.
  • Opacity values are blended against white backgrounds using adjust_color_opacity in utils.py because draw.io does not support alpha channels directly.
  • Dash styles are detected via stroke-dasharray attributes and converted to boolean dashed flags in the mxGraph style string.
  • Gradients are resolved from URL references using regex pattern matching and converted to draw.io gradientColor and gradientDirection parameters.
  • Shape names are mapped from Graphviz conventions to draw.io style constants via the _shape_to_style dictionary in Styles.py.
  • Text attributes including font family, size, color, and weight are preserved through the Text.from_svg extraction method.

Frequently Asked Questions

How does graphviz2drawio handle semi-transparent colors in Graphviz diagrams?

The library blends semi-transparent colors against a white background using the adjust_color_opacity function in graphviz2drawio/mx/utils.py. Both NodeFactory._extract_fill (lines 100-107) and EdgeFactory.from_svg (lines 34-40) call this utility to convert fill-opacity and stroke-opacity values into solid hex colors that draw.io can render correctly.

Can graphviz2drawio convert Graphviz gradients to draw.io format?

Yes. When Graphviz emits fill="url(#grad1)" references, NodeFactory._extract_fill (lines 99-107) uses a regex pattern to extract the gradient ID, looks up the definition in the gradients dictionary collected by _extract_gradients, and returns a tuple of start color, end color, and direction. The Node.get_node_style method then formats these into draw.io's gradientColor and gradientDirection style parameters.

What happens to dashed lines during the Graphviz to draw.io conversion?

The factories check for the stroke-dasharray SVG attribute. NodeFactory.from_svg (lines 52-54) sets a dashed boolean flag when present on polygon elements, while EdgeFactory.from_svg (lines 43-44) assigns DotAttr.DASHED to the line style. These flags are later rendered as dashed=1 in the final mxGraph style string.

How are Graphviz shapes mapped to draw.io shapes?

Graphviz shape names like box or ellipse are mapped to draw.io style constants through the _shape_to_style dictionary in graphviz2drawio/mx/Styles.py (lines 102-141). The Styles.get_for_shape method retrieves the appropriate style template, which Node.get_node_style (line 51 of Node.py) uses to generate the final shape definition in the output XML.

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 →