Skip to content

Commit d801a7a

Browse files
authored
Adding Draw.io's native XML shape libraries (#123)
* Add mxlibrary parsing functionality and corresponding tests * Add support for importing Draw.io XML shape libraries and enhance error handling * Updated documentation to reflect new functionality and usage examples. * Created tests for loading libraries, error handling, and registering libraries.
1 parent da73480 commit d801a7a

File tree

20 files changed

+1048
-11
lines changed

20 files changed

+1048
-11
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ coverage.xml
5454
.pytest_cache/
5555
cover/
5656

57+
tests/output/*.drawio
58+
5759
# Translations
5860
*.mo
5961
*.pot

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ item_from_lib = drawpyo.diagram.object_from_library(
5151
)
5252
```
5353

54+
## Import external shape libraries
55+
56+
You can import and use external Draw.io XML shape libraries (e.g., Azure, AWS, Google Cloud icons):
57+
58+
```python
59+
# Register an external library
60+
drawpyo.register_mxlibrary(
61+
"developer",
62+
"https://raw.githubusercontent.com/jgraph/drawio-libs/refs/heads/main/libs/integration/developer.xml"
63+
)
64+
65+
# Use shapes from the registered library
66+
icon = drawpyo.diagram.object_from_library(
67+
page=page,
68+
library="developer",
69+
obj_name="App Developing",
70+
position=(100, 100)
71+
)
72+
```
73+
5474
## Style an object from a string
5575

5676
```python

docs/index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ I love Draw.io! Compared to expensive and heavy commercial options like Visio an
88

99
So when I had a need to generate hierarchical tree diagrams of requirement structures I looked to Draw.io but I was surprised to find there wasn't even a single existing Python library for working with these files. I took the project home and spent a weekend building the initial functionality. I've been adding functionality, robustness, and documentation intermittently since.
1010

11+
## Key Features
12+
13+
- **Create diagrams programmatically** - Generate Draw.io diagrams using Python code
14+
- **Built-in shape libraries** - Access Draw.io's standard shape collections (General, Flowchart, etc.)
15+
- **Import external libraries** - Load third-party icon sets from XML files (Azure, AWS, Google Cloud, etc.)
16+
- **Automatic diagram types** - Generate trees, charts, and structured diagrams automatically
17+
- **Full styling control** - Customize colors, fonts, borders, and effects
18+
- **Load and modify** - Import existing Draw.io files and modify them programmatically
19+
1120
# The Future of Drawpyo
1221

1322
We’re constantly working to add new functionality, address issues and feature requests. If you find Drawpyo useful and would like to contribute, we’d greatly appreciate your help! You can reach out directly at [xander@merriman.industries](mailto:xander@merriman.industries) or join the conversation through GitHub issues.

docs/usage/shape_libs.md

Lines changed: 227 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
The Draw.io app has a lot of built-in shape libraries available. The basic library contains shapes and building blocks but there are increasingly more specific libraries such as flowcharts, wiring diagrams, and org charts. You can also export and import shape libraries into Draw.io.
44

5-
To replicate this feature for drawpyo, I created a library format based on TOML. Draw.io's libraries are XML which isn't as human readable or writable and is more specification than necessary.
5+
To replicate this feature for drawpyo, we provide two options for shape libraries:
66

7-
> Supporting Draw.io's XML based library is a planned feature.
7+
1. **TOML format** - A custom human-readable library format we created for drawpyo, which is simpler and more accessible than XML for defining custom shapes.
8+
2. **Draw.io's native XML format (mxlibrary)** - Direct support for Draw.io's XML shape libraries, which is useful for using third-party icon libraries like Azure, AWS, or Google Cloud icons.
89

910
## Built-In Shape Libraries
1011

@@ -79,3 +80,227 @@ new_obj = drawpyo.diagram.object_from_library(
7980
page=page,
8081
)
8182
```
83+
84+
## XML Shape Libraries (mxlibrary)
85+
86+
Drawpyo can import and use Draw.io's native XML shape library format (`.xml` files with `<mxlibrary>` tags). This is particularly useful for using third-party icon libraries like Azure icons, AWS icons, Google Cloud icons, and other custom shape collections available online.
87+
88+
### What is an mxlibrary?
89+
90+
An mxlibrary is Draw.io's XML-based format for storing collections of shapes. These files typically contain shapes with embedded SVG images and are commonly used for icon libraries. The format looks like:
91+
92+
```xml
93+
<mxlibrary>[
94+
{
95+
"h": 50,
96+
"w": 50,
97+
"title": "Icon-Name",
98+
"xml": "...encoded shape data..."
99+
}
100+
]</mxlibrary>
101+
```
102+
103+
### Loading an mxlibrary
104+
105+
You can load an mxlibrary from either a local file or a URL:
106+
107+
```python
108+
import drawpyo
109+
110+
# Load from a URL
111+
shapes = drawpyo.load_mxlibrary(
112+
"https://example.com/azure-icons.xml"
113+
)
114+
115+
# Load from a local file
116+
shapes = drawpyo.load_mxlibrary(
117+
"/path/to/local/library.xml"
118+
)
119+
```
120+
121+
The `load_mxlibrary()` function returns a dictionary of shapes that can be used directly with `object_from_library()`.
122+
123+
### Registering an mxlibrary
124+
125+
For easier reuse, you can register an mxlibrary with a name, making it available just like the built-in libraries:
126+
127+
```python
128+
import drawpyo
129+
130+
# Register the Azure icons library
131+
drawpyo.register_mxlibrary(
132+
"azure",
133+
"https://raw.githubusercontent.com/dwarfered/azure-architecture-icons-for-drawio/main/azure-public-service-icons/004%20azure%20ecosystem.xml"
134+
)
135+
136+
# Now use it like a built-in library
137+
file = drawpyo.File()
138+
page = drawpyo.Page(file=file)
139+
140+
vm_icon = drawpyo.diagram.object_from_library(
141+
library="azure",
142+
obj_name="01038-icon-service-Collaborative-Service",
143+
page=page,
144+
position=(100, 100)
145+
)
146+
147+
file.write()
148+
```
149+
150+
### Using mxlibrary Shapes
151+
152+
Once loaded or registered, you can use shapes from an mxlibrary in three ways:
153+
154+
#### 1. Register and use by name (recommended)
155+
156+
```python
157+
import drawpyo
158+
159+
# Register once
160+
drawpyo.register_mxlibrary(
161+
"custom",
162+
"https://example.com/custom-icons.xml"
163+
)
164+
165+
# Use multiple times
166+
file = drawpyo.File()
167+
page = drawpyo.Page(file=file)
168+
169+
icon1 = drawpyo.diagram.object_from_library(
170+
library="custom",
171+
obj_name="Shape-Name-1",
172+
page=page,
173+
position=(50, 50)
174+
)
175+
176+
icon2 = drawpyo.diagram.object_from_library(
177+
library="custom",
178+
obj_name="Shape-Name-2",
179+
page=page,
180+
position=(150, 50)
181+
)
182+
```
183+
184+
#### 2. Load and pass the dictionary directly
185+
186+
```python
187+
import drawpyo
188+
189+
# Load the library
190+
shapes = drawpyo.load_mxlibrary("https://example.com/icons.xml")
191+
192+
file = drawpyo.File()
193+
page = drawpyo.Page(file=file)
194+
195+
# Pass the dictionary directly
196+
icon = drawpyo.diagram.object_from_library(
197+
library=shapes, # Pass the dict
198+
obj_name="Icon-Name",
199+
page=page,
200+
position=(100, 100)
201+
)
202+
```
203+
204+
#### 3. Use with other object parameters
205+
206+
Like other shapes, you can override properties when creating objects from mxlibraries:
207+
208+
```python
209+
icon = drawpyo.diagram.object_from_library(
210+
library="azure",
211+
obj_name="Virtual-Machine",
212+
page=page,
213+
position=(100, 100),
214+
width=80, # Override default width
215+
height=80, # Override default height
216+
)
217+
```
218+
219+
### Finding Shape Names
220+
221+
To find the available shape names in an mxlibrary:
222+
223+
```python
224+
import drawpyo
225+
226+
shapes = drawpyo.load_mxlibrary("https://example.com/icons.xml")
227+
228+
# List all available shape names
229+
print("Available shapes:")
230+
for shape_name in shapes.keys():
231+
print(f" - {shape_name}")
232+
```
233+
234+
### Error Handling
235+
236+
The mxlibrary functions include comprehensive error handling:
237+
238+
```python
239+
import drawpyo
240+
241+
try:
242+
drawpyo.register_mxlibrary(
243+
"mylib",
244+
"https://example.com/library.xml"
245+
)
246+
except FileNotFoundError:
247+
print("Library file not found")
248+
except ValueError as e:
249+
print(f"Error loading library: {e}")
250+
```
251+
252+
### Example: Complete Workflow with Azure Icons
253+
254+
```python
255+
import drawpyo
256+
257+
# Register the Azure icons library
258+
drawpyo.register_mxlibrary(
259+
"azure",
260+
"https://raw.githubusercontent.com/dwarfered/azure-architecture-icons-for-drawio/main/azure-public-service-icons/004%20azure%20ecosystem.xml"
261+
)
262+
263+
# Create a diagram
264+
file = drawpyo.File()
265+
file.file_name = "azure_architecture"
266+
file.file_path = "./output"
267+
268+
page = drawpyo.Page(file=file)
269+
page.name = "Azure Architecture"
270+
271+
# Add Azure service icons
272+
vm = drawpyo.diagram.object_from_library(
273+
library="azure",
274+
obj_name="01038-icon-service-Collaborative-Service",
275+
page=page,
276+
position=(100, 100)
277+
)
278+
279+
storage = drawpyo.diagram.object_from_library(
280+
library="azure",
281+
obj_name="01039-icon-service-Storage-Account",
282+
page=page,
283+
position=(300, 100)
284+
)
285+
286+
# Connect them with an edge
287+
edge = drawpyo.diagram.Edge(
288+
page=page,
289+
source=vm,
290+
target=storage
291+
)
292+
293+
# Save the diagram
294+
file.write()
295+
```
296+
297+
### Where to Find mxlibrary Files
298+
299+
Many organizations and communities provide mxlibrary files for Draw.io:
300+
301+
- **Azure Icons**: [GitHub - dwarfered/azure-architecture-icons-for-drawio](https://github.com/dwarfered/azure-architecture-icons-for-drawio)
302+
- **AWS Icons**: Available from AWS Architecture Icons
303+
- **Google Cloud Icons**: Available from Google Cloud Architecture Diagramming Tool
304+
- **Custom Libraries**: Export your own from Draw.io via File → Export → Library
305+
306+
You can also create your own mxlibrary files using the Draw.io application by selecting shapes and exporting them as a library.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import drawpyo
2+
3+
# Register Azure icons
4+
azure_url = "https://raw.githubusercontent.com/dwarfered/azure-architecture-icons-for-drawio/refs/heads/main/azure-public-service-icons/004%20azure%20ecosystem.xml"
5+
drawpyo.register_mxlibrary("azure", azure_url)
6+
7+
# Get available shapes
8+
from drawpyo.diagram.objects import base_libraries
9+
10+
azure_shapes = base_libraries["azure"]
11+
shape_names = list(azure_shapes.keys())
12+
13+
# Create diagram
14+
file = drawpyo.File()
15+
file.file_name = "azure_architecture.drawio"
16+
file.file_path = "./etc/reference drawio charts"
17+
18+
page = drawpyo.Page(file=file)
19+
page.name = "Azure Architecture Diagram"
20+
21+
# Add a title
22+
title = drawpyo.diagram.Object(
23+
page=page,
24+
value="Azure Cloud Services",
25+
position=(200, 50),
26+
width=300,
27+
height=40,
28+
)
29+
title.text_format.size = 20
30+
title.text_format.bold = True
31+
title.text_format.align = "center"
32+
title.fillColor = "#0078D4"
33+
title.fontColor = "#FFFFFF"
34+
title.rounded = True
35+
36+
# Create shapes in a row
37+
icons = []
38+
y_position = 150
39+
spacing = 150
40+
41+
for i, shape_name in enumerate(shape_names[:3]):
42+
x_position = 100 + (i * spacing)
43+
44+
icon = drawpyo.diagram.object_from_library(
45+
library="azure",
46+
obj_name=shape_name,
47+
page=page,
48+
position=(x_position, y_position),
49+
)
50+
51+
label = drawpyo.diagram.Object(
52+
page=page,
53+
value=shape_name.replace("-", " "),
54+
position=(x_position - 25, y_position + 80),
55+
width=100,
56+
height=30,
57+
)
58+
label.text_format.size = 10
59+
label.text_format.align = "center"
60+
label.fillColor = "none"
61+
label.strokeColor = "none"
62+
63+
icons.append(icon)
64+
65+
# Connect the icons with arrows
66+
if len(icons) > 1:
67+
for i in range(len(icons) - 1):
68+
edge = drawpyo.diagram.Edge(page=page, source=icons[i], target=icons[i + 1])
69+
edge.strokeColor = "#0078D4"
70+
edge.strokeWidth = 2
71+
72+
# Save
73+
file.write()
74+
print(
75+
"Azure diagram created successfully! You can now open the file in Draw.io to see the Azure icons!"
76+
)

0 commit comments

Comments
 (0)