# How drawio_extract.py Works: Converting draw.io Files to a Normalized Intermediate Representation

> Discover how drawio_extract.py transforms draw.io diagrams into normalized JSON or Markdown. This script parses XML, PNG, and SVG files, flattening graph models for efficient downstream processing.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: how-to-guide
- Published: 2026-08-21

---

**drawio_extract.py** is a deterministic parser that decodes draw.io files (XML, PNG, SVG, or compressed payloads), flattens the graph model into absolute-positioned nodes and edges, and emits either a Markdown digest or a JSON intermediate representation for downstream processing.

The [`drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/drawio_extract.py) script in the **cathrynlavery/diagram-design** repository serves as the deterministic import engine for draw.io diagrams. Unlike heuristic-driven converters, it strictly extracts and normalizes content without making design decisions, producing a canonical data structure that the rest of the Diagram-Design skill consumes.

## Overview of the Extraction Pipeline

The script operates as a pure transformation layer. It never applies design logic; instead, it decodes whatever the user-produced file contains and normalizes it into an intermediate representation (IR). According to the source code in [`skills/diagram-design/scripts/drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/drawio_extract.py), the pipeline consists of nine distinct phases, from file detection to final rendering.

## Detecting and Loading File Formats

The entry point `load_mxfile()` (lines 62‑87) determines the container format and extracts the raw `<mxfile>` or `<mxGraphModel>` XML. It handles four distinct input types:

- **PNG with embedded XML**: Extracts the `mxfile` payload using `_png_embedded_xml`
- **SVG with embedded XML**: Parses the XML chunk via `_svg_embedded_xml`
- **Plain XML**: Directly loads uncompressed diagram files
- **Deflated/Base64 payload**: Decompresses compressed streams using `_inflate`

This flexibility allows the script to process diagrams exported directly from draw.io as well as images containing embedded diagram data.

## Security Validation

Before parsing, `_reject_unsafe_xml()` (lines 62‑67) inspects the raw content for DTD or ENTITY declarations to prevent XML external entity (XXE) attacks. If the scanner detects unsafe constructs, the script aborts with exit code 2 via the `_fail()` handler, ensuring the extraction pipeline never processes malicious XML.

## Parsing the XML Graph Model

The `parse_file()` function (lines 23‑38) orchestrates the initial XML processing. It calls `load_mxfile()` to retrieve the raw content, then uses `ElementTree` to parse the structure. The function iterates over each `<diagram>` element (or a single wrapper for simpler files) and delegates page-level processing to `parse_page()`.

## Building Node and Edge Objects

Inside `parse_page()` (lines 58‑121), the script constructs the IR through three sequential passes:

1. **Pass 1**: Collects raw cell data, including `<object>` and `UserObject` wrappers, preserving all metadata attributes
2. **Pass 2**: Creates `Node` objects for every vertex, resolves absolute geometry coordinates, and establishes parent-child relationships
3. **Pass 3**: Creates `Edge` objects, attaches edge-label text via `clean_label()`, and computes node degrees (in-degree and out-degree)

This multi-pass approach ensures that all geometric relationships are normalized to absolute coordinates before structural analysis begins.

## Helper Utilities for Data Normalization

Several utility functions standardize the extracted data (lines 98‑132):

- **`parse_style()`**: Converts semicolon-delimited style strings into Python dictionaries for programmatic access
- **`clean_label()`**: Strips HTML fragments and formatting artifacts from node and edge labels
- **`classify_shape()`**: Maps style keys to canonical shape names (e.g., `ellipse`, `swimlane`, `icon:aws`)

These helpers ensure the IR contains clean, structured data regardless of the complexity of the original draw.io styling.

## Structural Analysis

Once the IR is built, `analyze()` (lines 87‑136) inspects the graph topology to produce diagnostic signals. It detects cycles, identifies hub nodes with high connectivity, and suggests type candidates (such as “sequence” or “architecture” diagrams). This analysis guides downstream rendering decisions but remains non-deterministic—[`drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/drawio_extract.py) never selects the final diagram type itself.

## Rendering Output

The final stage produces human-readable or machine-readable output based on command-line flags:

- **`digest()`** (lines 84‑115): Generates a Markdown table showing page bounds, node/edge statistics, and the first N rows of entities (controlled by `--max-rows`)
- **`to_json()`** (lines 118‑134): Emits the complete intermediate representation as JSON when the `--json` flag is provided

The `main()` function (lines 57‑71, 86‑92) handles argument parsing with `argparse`, supports the `--out` parameter for file writing, and coordinates the pipeline execution.

## Command-Line and Programmatic Usage

You can run the script directly from the terminal:

```bash

# Generate a readable Markdown digest (default 40 rows per table)

python3 drawio_extract.py examples/my-diagram.drawio

# Limit tables to 20 rows and write to a file

python3 drawio_extract.py examples/my-diagram.drawio --max-rows 20 --out report.md

# Emit the full intermediate representation as JSON

python3 drawio_extract.py examples/my-diagram.drawio --json > report.json

```

For integration into Python applications, import the core functions directly:

```python
from pathlib import Path
from drawio_extract import parse_file, digest, to_json

# Load a file and get a list of Page objects

pages = parse_file(Path("examples/my-diagram.drawio"))

# Produce a Markdown digest for the first page

md = digest(Path("examples/my-diagram.drawio"), pages, pages[:1], max_rows=30)
print(md)

# Or get the complete JSON IR

json_payload = to_json(Path("examples/my-diagram.drawio"), pages, pages[:1])
print(json_payload)

```

## Summary

- **drawio_extract.py** acts as a deterministic decoder for draw.io files, supporting XML, PNG, SVG, and compressed Base64 formats.
- The script validates XML for security threats via `_reject_unsafe_xml()` before parsing.
- It constructs an intermediate representation through a three-pass process in `parse_page()`, normalizing all coordinates to absolute positions.
- Helper functions like `classify_shape()` and `parse_style()` ensure consistent data formatting.
- Output options include a concise Markdown digest (`digest()`) or a complete JSON intermediate representation (`to_json()`).
- The tool operates without heuristic design decisions, leaving diagram-type selection to downstream consumers in the Diagram-Design skill.

## Frequently Asked Questions

### What file formats does drawio_extract.py support?

The script supports plain XML files, PNG images with embedded `mxfile` data, SVG files with embedded XML, and compressed Base64 payloads. The `load_mxfile()` function automatically detects the container type and extracts the raw diagram XML accordingly.

### How does the script handle security risks in XML parsing?

Before parsing, `_reject_unsafe_xml()` scans the input for DTD or ENTITY declarations to prevent XXE attacks. If unsafe XML constructs are detected, the script immediately aborts with exit code 2, ensuring no malicious content reaches the processing pipeline.

### Can I use drawio_extract.py as a library in my Python project?

Yes. You can import `parse_file()`, `digest()`, and `to_json()` directly from the script to process draw.io files programmatically. The functions accept `pathlib.Path` objects and return structured Python objects or formatted strings suitable for further processing.

### What is the difference between the Markdown digest and JSON output?

The Markdown digest provides a human-readable summary with page statistics and sample rows of nodes and edges, useful for quick inspection or documentation. The JSON output contains the complete intermediate representation with all geometric data, styles, and structural analysis signals, intended for machine consumption by downstream rendering engines.