# How Draw.io Diagram Data Is Converted for Editorial Use: A Complete Technical Guide

> Learn how draw.io diagram data converts to editorial Markdown or JSON. This guide details the four-stage pipeline: XML decoding, IR building, topology analysis, and content rendering.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: technical-guide
- Published: 2026-09-05

---

**The `diagram-design` repository converts raw draw.io files into editorial-ready Markdown or JSON through a four-stage pipeline that decodes embedded XML, builds a deterministic intermediate representation, analyzes structural topology, and renders human-readable content.**

Converting complex visual diagrams into structured text requires more than simple metadata extraction. The open-source `diagram-design` repository by Cathryn Lavery ships a dedicated Python extractor at [`skills/diagram-design/scripts/drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/drawio_extract.py) that processes any draw.io file—whether plain XML, compressed URL-encoded payloads, or diagrams embedded in PNG/SVG images—into clean formats optimized for documentation, blog posts, and knowledge bases.

## The 4-Stage Conversion Pipeline

### 1. Load Raw Diagrams with `load_mxfile()`

The extraction begins by detecting the input format and safely extracting the embedded `<mxfile>` or `<mxGraphModel>` XML. The `load_mxfile()` function (lines 62-70) handles three distinct input types:

- **Plain `.drawio` XML** – Read directly from disk
- **Compressed payloads** – Inflated via the `_inflate` helper using `_decompress_limited` to prevent zip-bomb attacks
- **Embedded diagrams** – Extracted from PNG via `_png_embedded_xml` or SVG via `_svg_embedded_xml`

Safety checks occur immediately through `_reject_unsafe_xml`, which blocks DTD and ENTITY declarations to prevent external entity expansion attacks. Size guards `MAX_INPUT_BYTES` and `MAX_XML_BYTES` enforce hard limits on memory consumption, ensuring the pipeline rejects pathological inputs before processing begins.

### 2. Parse XML into a Structured Intermediate Representation

Once loaded, `parse_file()` (lines 23-33) transforms the raw XML into a deterministic **intermediate representation (IR)** composed of `Page`, `Node`, and `Edge` dataclasses. The parser performs three sequential passes over the `<mxCell>` elements:

1. **Collection** – Gathers raw cells from the XML tree
2. **Node resolution** – Creates `Node` objects with absolute coordinates via the nested `resolve()` function (lines 58-66 and 68-71)
3. **Edge mapping** – Links nodes through `Edge` objects that preserve relational geometry and style attributes

This IR abstraction decouples the draw.io storage format from the editorial output, enabling consistent processing regardless of whether the source was a compressed URL or a flat XML file.

### 3. Analyze Diagram Structure with `analyze()`

The `analyze()` function (lines 73-87) walks the IR to compute structural signals that inform the editorial rendering. It calculates:

- **Topology metrics** – Node count, edge count, container depth, and shape distribution
- **Flow detection** – Entry and exit points via hub-node identification
- **Pattern recognition** – Cycle detection using `_has_cycle` and alignment checks via `_aligned` to suggest candidate diagram types (flowcharts, sequence diagrams, architectures)

These statistics populate a dictionary consumed by the renderers, allowing the output to include intelligent hints about diagram complexity and appropriate simplifications such as collapsible groups.

### 4. Render Editorial Output with `digest()` and `to_json()`

The final stage converts the analyzed IR into consumable formats. The `digest()` function (line 13) generates a Markdown report containing:

- A summary table with canvas dimensions and node statistics
- Structured node and edge listings with geometry and style data
- Simplification hints for documentation integration

For programmatic consumption, `to_json()` serializes the full IR—including all nodes, edges, and analysis metadata—into a JSON payload. Helper functions `_escape_markdown` and `_escape_table` sanitize content to ensure safe rendering in GFM-compatible documentation platforms.

## Security and Robustness Features

The conversion pipeline implements defense-in-depth strategies to protect automated editorial workflows:

- **Input size limits** – `MAX_INPUT_BYTES` caps the raw file size, while `MAX_XML_BYTES` restricts the decompressed XML payload
- **XML entity protection** – `_reject_unsafe_xml` strictly forbids DTD declarations and inline ENTITY definitions that could trigger XXE vulnerabilities
- **Decompression guards** – `_decompress_limited` aborts if inflation exceeds configured thresholds, preventing zip-bomb denial-of-service attacks

These safeguards ensure that the extraction remains deterministic and secure when processing untrusted diagrams from public repositories or user submissions.

## Practical Implementation Examples

### Command-Line Usage

The [`drawio_extract.py`](https://github.com/cathrynlavery/diagram-design/blob/main/drawio_extract.py) script exposes a CLI entry point with flags for `--page`, `--json`, `--max-rows`, and `--out`:

```bash

# Generate a concise Markdown summary for documentation

python3 drawio_extract.py diagram.drawio --out summary.md

# Export the full intermediate representation as JSON

python3 drawio_extract.py diagram.drawio --json > diagram.json

# Limit table output to 20 rows for preview purposes

python3 drawio_extract.py complex.drawio --out brief.md --max-rows 20

```

### Programmatic Integration

Import the module to integrate extraction into custom documentation generators:

```python
from pathlib import Path
from skills.diagram_design.scripts.drawio_extract import (
    load_mxfile,
    parse_file,
    select_pages,
    digest,
)

# Load and parse a draw.io file

xml_content = load_mxfile(Path("architecture.drawio"))
pages = parse_file(Path("architecture.drawio"))

# Select specific pages (None selects all)

selected = select_pages(pages, page_selector=None)

# Generate editorial Markdown with controlled verbosity

markdown_output = digest(
    Path("architecture.drawio"), 
    pages, 
    selected, 
    max_rows=20
)
print(markdown_output)

```

### Extracting Raw IR for Custom Transformations

Access the full structured data for bespoke rendering pipelines:

```python
from skills.diagram_design.scripts.drawio_extract import parse_file, to_json
from pathlib import Path

pages = parse_file(Path("workflow.drawio"))
ir_json = to_json(Path("workflow.drawio"), pages, pages)  # All pages

# ir_json contains complete node coordinates, edge connections, and analysis metadata

```

## Summary

- **Four-stage pipeline** – The converter loads raw draw.io data, parses it into an IR of `Page`/`Node`/`Edge` objects, analyzes structural patterns, and renders Markdown or JSON outputs.
- **Universal input support** – Handles plain XML, compressed payloads, and diagrams embedded in PNG/SVG images through `load_mxfile()` and specialized extraction helpers.
- **Security-first design** – Implements `MAX_INPUT_BYTES`, `MAX_XML_BYTES`, and `_reject_unsafe_xml` to prevent zip-bomb and XXE attacks during automated processing.
- **Editorial optimization** – The `digest()` function produces documentation-ready Markdown with summary tables, while `to_json()` exposes the full IR for programmatic transformation.
- **Deterministic extraction** – The intermediate representation ensures consistent output regardless of whether the source file was compressed, embedded, or raw XML.

## Frequently Asked Questions

### What draw.io file formats does the extractor support?

The extractor supports three distinct formats: plain `.drawio` XML files, URL-encoded compressed payloads (commonly found in shared links), and diagrams embedded as metadata within `.png` or `.svg` images. The `load_mxfile()` function automatically detects the format and delegates to `_inflate`, `_png_embedded_xml`, or `_svg_embedded_xml` as needed.

### How does the pipeline handle compressed or embedded diagram data?

Compressed data is inflated using `_decompress_limited`, which enforces the `MAX_XML_BYTES` threshold to prevent memory exhaustion. For embedded diagrams, the extractor reads the tEXt chunk in PNG files or the XML metadata in SVG containers, then strips the outer image wrapper to access the underlying `<mxGraphModel>` XML before parsing begins.

### What security measures protect against malicious diagram files?

The converter implements three critical safeguards: strict byte limits (`MAX_INPUT_BYTES` and `MAX_XML_BYTES`), DTD/ENTITY rejection via `_reject_unsafe_xml` to block XXE attacks, and bounded decompression through `_decompress_limited` to prevent zip-bomb denial-of-service. These checks run before any XML parsing occurs, ensuring unsafe files never reach the intermediate representation stage.

### Can I customize the Markdown output format for specific editorial styles?

While the `digest()` function produces standard GFM-compatible Markdown with summary tables and node listings, you can customize output by consuming the JSON representation from `to_json()` and applying your own templates. The IR structure exposes all geometry, styling, and analysis metadata, allowing you to generate bespoke formats for Jekyll, Docusaurus, or proprietary documentation systems without modifying the core extraction logic.