Diagrams created in draw.io / diagrams.net often contain external icon references such as:
https://api.iconify.design/lucide:landmark.svg?color=%23000000
That works until it does not.
In restricted environments, offline setups, enterprise networks, sandboxes, exported bundles, and long-lived documentation, those icons can stop rendering because the diagram still depends on a live external service.
XML Icon Fixer solves that by replacing those external SVG URLs with inline data:image/svg+xml payloads so the final diagram becomes self-contained.
- Detects external Iconify SVG references inside
.xmland.drawiofiles - Supports both common Iconify URL forms:
collection:icon.svgcollection/icon.svg
- Handles URLs embedded inside Draw.io HTML-escaped cell values like
<img src='...'/> - Fetches SVGs and converts them into UTF-8 encoded inline data URIs
- Escapes output safely for XML attribute usage
- Validates XML before and after processing
- Supports multi-file upload and raw XML paste mode
- Returns per-file success and error reporting for batch processing
- Diagrams render correctly offline
- Shared files stay portable across machines and environments
- Sandboxed browsers no longer block external icon loading
- Documentation becomes more durable and reproducible
- Teams stop debugging broken visual assets caused by expired or blocked URLs
docker compose up --buildOpen:
This starts the web app and persists uploaded and processed files under:
./data/uploads./data/outputs
python3 -m pip install -r requirements.txt
python3 app.pyThen open:
The project ships with a buildable Compose service so any user with Docker can run the software directly from the repo:
services:
xml-icon-fixer:
image: xml-icon-fixer:latest
build: .
container_name: xml-icon-fixer
ports:
- "5000:5000"
volumes:
- ./data/uploads:/app/uploads
- ./data/outputs:/app/outputs
environment:
- PYTHONUNBUFFERED=1
restart: unless-stoppedDrag and drop one or more .xml / .drawio files into the browser UI.
The backend scans the XML for Iconify SVG references, including escaped URLs buried inside HTML fragments in mxCell values.
Each icon is fetched, validated as real SVG, percent-encoded, and embedded back into the XML as a data: URI.
The updated XML is parsed again to make sure the transformation did not break document structure.
Each successfully processed file is returned with a downloadable output link and a replacement count.
flowchart LR
A["User uploads .xml / .drawio"] --> B["Flask app receives content"]
B --> C["Extract Iconify URLs"]
C --> D["Fetch SVGs"]
D --> E["Validate SVG markup"]
E --> F["Convert to encoded data URI"]
F --> G["Replace URLs in XML"]
G --> H["Validate final XML"]
H --> I["Return downloadable fixed file"]
The current implementation is built to handle the kinds of cases that usually break quick XML replacement scripts:
- HTML-escaped icon URLs such as
',",& - Draw.io cell values containing embedded HTML like
<img src='...'> - Style-based image references such as
image=https://...svg;aspect=fixed - Mixed batch uploads where some files succeed and others fail
- Non-UTF-8 uploads
- Invalid XML input
- Invalid or non-SVG icon responses
- Safe filename handling for downloads and generated outputs
Run the automated test suite:
python3 -m pytest -qCurrent coverage includes:
- escaped query handling
lucide:iconstyle Iconify URLs- slash-style Iconify URLs
- malformed SVG responses
- invalid XML rejection
- partial-success upload flows
- non-UTF-8 file rejection
- missing file download behavior
<mxCell value="<img src='https://api.iconify.design/lucide:landmark.svg?color=%23000000'/>" /><mxCell value="<img src='data:image/svg+xml;utf8,%3Csvg...%3E'/>" />xml-icon-fixer/
├── app.py
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── pytest.ini
├── templates/
│ └── index.html
├── static/
│ ├── script.js
│ └── style.css
└── tests/
└── test_app.py
| Layer | Technology |
|---|---|
| Backend | Flask |
| Frontend | HTML, CSS, JavaScript |
| Processing | Regex + XML validation |
| Fetching | Python standard library (urllib) |
| Testing | Pytest |
| Deployment | Docker, Docker Compose, Gunicorn |
- drag and drop uploads
- multiple file support
- paste-XML processing mode
- upload progress indicator
- replacement counts
- per-file error display
- downloadable processed outputs
- live diagram preview before download
- ZIP download for batch results
- caching for repeated icon URLs
- duplicate SVG deduplication
- SVG minification pipeline
- support for more icon/CDN patterns beyond Iconify
- optional auth and usage dashboard
XML Icon Fixer is a lightweight developer utility that repairs fragile diagram dependencies by converting external Iconify SVG references in Draw.io XML into self-contained inline assets, making diagrams portable, offline-safe, and consistent across restricted environments.
Built a Dockerized Flask tool that converts external SVG icon dependencies in Draw.io XML into inline validated data URIs, enabling reliable offline and sandbox-safe rendering.
python3 app.pypython3 -m pytest -qdocker compose up --build- The app currently focuses on Iconify-hosted SVG URLs.
- Outputs are written to the
outputs/directory in local mode and./data/outputsin Docker Compose mode. - Uploaded files are accepted as
.xmland.drawio. - The README visuals are stored locally under
assets/so the repo presentation stays self-contained.