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

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 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 script exposes a CLI entry point with flags for --page, --json, --max-rows, and --out:


# 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:

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:

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.

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 →