SVG2DrawIOLib 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- SVG2DrawIOLib/__about__.py +3 -0
- SVG2DrawIOLib/__init__.py +21 -0
- SVG2DrawIOLib/cli/__init__.py +158 -0
- SVG2DrawIOLib/cli/add.py +241 -0
- SVG2DrawIOLib/cli/create.py +242 -0
- SVG2DrawIOLib/cli/helpers.py +29 -0
- SVG2DrawIOLib/cli/list.py +70 -0
- SVG2DrawIOLib/cli/remove.py +71 -0
- SVG2DrawIOLib/library_manager.py +269 -0
- SVG2DrawIOLib/models.py +129 -0
- SVG2DrawIOLib/svg_processor.py +317 -0
- svg2drawiolib-1.0.0.dist-info/METADATA +220 -0
- svg2drawiolib-1.0.0.dist-info/RECORD +16 -0
- svg2drawiolib-1.0.0.dist-info/WHEEL +4 -0
- svg2drawiolib-1.0.0.dist-info/entry_points.txt +2 -0
- svg2drawiolib-1.0.0.dist-info/licenses/LICENSE.txt +9 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""List command - List all icons in a DrawIO library."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import rich_click as rc
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from SVG2DrawIOLib.cli.helpers import console, setup_logging
|
|
11
|
+
from SVG2DrawIOLib.library_manager import LibraryManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@rc.command(name="list")
|
|
15
|
+
@rc.argument(
|
|
16
|
+
"library_file",
|
|
17
|
+
type=rc.Path(exists=True, dir_okay=False, path_type=Path),
|
|
18
|
+
)
|
|
19
|
+
@rc.option(
|
|
20
|
+
"--verbose",
|
|
21
|
+
"-v",
|
|
22
|
+
is_flag=True,
|
|
23
|
+
help="Enable verbose debug logging.",
|
|
24
|
+
)
|
|
25
|
+
@rc.option(
|
|
26
|
+
"--quiet",
|
|
27
|
+
"-q",
|
|
28
|
+
is_flag=True,
|
|
29
|
+
help="Suppress all output except errors.",
|
|
30
|
+
)
|
|
31
|
+
def list(
|
|
32
|
+
library_file: Path,
|
|
33
|
+
verbose: bool,
|
|
34
|
+
quiet: bool,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""List all icons in a DrawIO library.
|
|
37
|
+
|
|
38
|
+
\b
|
|
39
|
+
Displays all icon names in the specified library file.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
List all icons:
|
|
43
|
+
$ SVG2DrawIOLib list my-library.xml
|
|
44
|
+
"""
|
|
45
|
+
setup_logging(verbose, quiet)
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
manager = LibraryManager()
|
|
50
|
+
icon_names = manager.list_icons(library_file)
|
|
51
|
+
|
|
52
|
+
if not icon_names:
|
|
53
|
+
console.print(f"[yellow]Library is empty:[/yellow] {library_file}")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# Create a nice table
|
|
57
|
+
table = Table(title=f"Icons in {library_file.name}", show_header=True)
|
|
58
|
+
table.add_column("Icon Name", style="cyan")
|
|
59
|
+
|
|
60
|
+
for name in sorted(icon_names):
|
|
61
|
+
table.add_row(name)
|
|
62
|
+
|
|
63
|
+
console.print(table)
|
|
64
|
+
console.print(f"\n[green]Total:[/green] {len(icon_names)} icon(s)")
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.error(f"Error: {e}")
|
|
68
|
+
if verbose:
|
|
69
|
+
raise
|
|
70
|
+
sys.exit(1)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Remove command - Remove icons from a DrawIO library by name."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import rich_click as rc
|
|
8
|
+
|
|
9
|
+
from SVG2DrawIOLib.cli.helpers import console, setup_logging
|
|
10
|
+
from SVG2DrawIOLib.library_manager import LibraryManager
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@rc.command()
|
|
14
|
+
@rc.argument(
|
|
15
|
+
"library_file",
|
|
16
|
+
type=rc.Path(exists=True, dir_okay=False, path_type=Path),
|
|
17
|
+
)
|
|
18
|
+
@rc.argument("icon_names", nargs=-1, required=True, metavar="ICON_NAMES...")
|
|
19
|
+
@rc.option(
|
|
20
|
+
"--verbose",
|
|
21
|
+
"-v",
|
|
22
|
+
is_flag=True,
|
|
23
|
+
help="Enable verbose debug logging.",
|
|
24
|
+
)
|
|
25
|
+
@rc.option(
|
|
26
|
+
"--quiet",
|
|
27
|
+
"-q",
|
|
28
|
+
is_flag=True,
|
|
29
|
+
help="Suppress all output except errors.",
|
|
30
|
+
)
|
|
31
|
+
def remove(
|
|
32
|
+
library_file: Path,
|
|
33
|
+
icon_names: tuple[str, ...],
|
|
34
|
+
verbose: bool,
|
|
35
|
+
quiet: bool,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Remove icons from a DrawIO library by name.
|
|
38
|
+
|
|
39
|
+
Removes one or more icons from an existing library file.
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
Remove single icon:
|
|
43
|
+
$ SVG2DrawIOLib remove my-library.xml old-icon
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
Remove multiple icons:
|
|
47
|
+
$ SVG2DrawIOLib remove my-library.xml icon1 icon2 icon3
|
|
48
|
+
"""
|
|
49
|
+
setup_logging(verbose, quiet)
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
manager = LibraryManager()
|
|
54
|
+
metadata, removed_count = manager.remove_icons_from_library(library_file, list(icon_names))
|
|
55
|
+
|
|
56
|
+
if removed_count == 0:
|
|
57
|
+
console.print(
|
|
58
|
+
"[yellow]Warning:[/yellow] No icons were removed. "
|
|
59
|
+
"Requested icons not found in library."
|
|
60
|
+
)
|
|
61
|
+
else:
|
|
62
|
+
console.print(
|
|
63
|
+
f"[green]✓[/green] Removed {removed_count} icon(s). "
|
|
64
|
+
f"Library now has {metadata.icon_count} icon(s): [cyan]{library_file}[/cyan]"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"Error: {e}")
|
|
69
|
+
if verbose:
|
|
70
|
+
raise
|
|
71
|
+
sys.exit(1)
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""DrawIO library management functionality."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import xml.etree.ElementTree as ET
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from SVG2DrawIOLib.models import DrawIOIcon, LibraryMetadata
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LibraryManager:
|
|
14
|
+
"""Manages DrawIO library files."""
|
|
15
|
+
|
|
16
|
+
def __init__(self) -> None:
|
|
17
|
+
"""Initialize the library manager."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def create_library(self, icons: list[DrawIOIcon], output_path: Path) -> LibraryMetadata:
|
|
21
|
+
"""Create a new DrawIO library from icons.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
icons: List of DrawIO icons to include.
|
|
25
|
+
output_path: Path where the library file will be saved.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
LibraryMetadata with information about the created library.
|
|
29
|
+
"""
|
|
30
|
+
logger.info(f"Creating library with {len(icons)} icon(s)")
|
|
31
|
+
|
|
32
|
+
# Sort icons alphabetically by name
|
|
33
|
+
sorted_icons = sorted(icons, key=lambda icon: icon.name)
|
|
34
|
+
|
|
35
|
+
# Convert to library JSON format
|
|
36
|
+
library_data = [icon.to_dict() for icon in sorted_icons]
|
|
37
|
+
library_json = json.dumps(library_data)
|
|
38
|
+
|
|
39
|
+
# Create XML structure
|
|
40
|
+
mxlibrary = ET.Element("mxlibrary")
|
|
41
|
+
mxlibrary.text = library_json
|
|
42
|
+
|
|
43
|
+
tree = ET.ElementTree(mxlibrary)
|
|
44
|
+
|
|
45
|
+
# Write to file
|
|
46
|
+
tree.write(output_path, encoding="utf-8", xml_declaration=True)
|
|
47
|
+
|
|
48
|
+
logger.info(f"Library created successfully: {output_path}")
|
|
49
|
+
|
|
50
|
+
return LibraryMetadata(
|
|
51
|
+
name=output_path.stem,
|
|
52
|
+
icon_count=len(icons),
|
|
53
|
+
source_files=[],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def load_library(self, library_path: Path) -> list[DrawIOIcon]:
|
|
57
|
+
"""Load icons from an existing DrawIO library.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
library_path: Path to the library file.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of DrawIO icons from the library.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
FileNotFoundError: If the library file does not exist.
|
|
67
|
+
ValueError: If the library file is invalid.
|
|
68
|
+
"""
|
|
69
|
+
if not library_path.exists():
|
|
70
|
+
logger.error(f"Library file not found: {library_path}")
|
|
71
|
+
raise FileNotFoundError(f"Library file not found: {library_path}")
|
|
72
|
+
|
|
73
|
+
logger.debug(f"Loading library: {library_path}")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
tree = ET.parse(library_path) # nosec B314 - User-provided library file, user controls input
|
|
77
|
+
root = tree.getroot()
|
|
78
|
+
|
|
79
|
+
if root.tag != "mxlibrary":
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Invalid library file: root element is {root.tag}, expected mxlibrary"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if not root.text:
|
|
85
|
+
logger.warning("Library file is empty")
|
|
86
|
+
return []
|
|
87
|
+
|
|
88
|
+
library_data = json.loads(root.text)
|
|
89
|
+
|
|
90
|
+
icons = []
|
|
91
|
+
for item in library_data:
|
|
92
|
+
# Convert from library format back to DrawIOIcon
|
|
93
|
+
# Note: We need to re-encode the XML data
|
|
94
|
+
from SVG2DrawIOLib.models import SVGDimensions
|
|
95
|
+
|
|
96
|
+
icon = DrawIOIcon(
|
|
97
|
+
name=item["title"],
|
|
98
|
+
xml_data=item["xml"].encode("ascii"),
|
|
99
|
+
dimensions=SVGDimensions(width=item["w"], height=item["h"]),
|
|
100
|
+
)
|
|
101
|
+
icons.append(icon)
|
|
102
|
+
|
|
103
|
+
logger.info(f"Loaded {len(icons)} icon(s) from library")
|
|
104
|
+
return icons
|
|
105
|
+
|
|
106
|
+
except ET.ParseError as e:
|
|
107
|
+
logger.error(f"Failed to parse library file: {e}")
|
|
108
|
+
raise ValueError(f"Invalid library file: {e}") from e
|
|
109
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
110
|
+
logger.error(f"Invalid library format: {e}")
|
|
111
|
+
raise ValueError(f"Invalid library format: {e}") from e
|
|
112
|
+
|
|
113
|
+
def add_icons_to_library(
|
|
114
|
+
self,
|
|
115
|
+
library_path: Path,
|
|
116
|
+
new_icons: list[DrawIOIcon],
|
|
117
|
+
replace_duplicates: bool = False,
|
|
118
|
+
add_duplicates: bool = False,
|
|
119
|
+
) -> LibraryMetadata:
|
|
120
|
+
"""Add icons to an existing library.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
library_path: Path to the existing library file.
|
|
124
|
+
new_icons: List of icons to add.
|
|
125
|
+
replace_duplicates: If True, replace icons with duplicate names.
|
|
126
|
+
add_duplicates: If True, add duplicates with modified names (e.g., icon_2, icon_3).
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
LibraryMetadata with updated library information.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
FileNotFoundError: If the library file does not exist.
|
|
133
|
+
ValueError: If the library file is invalid.
|
|
134
|
+
"""
|
|
135
|
+
logger.info(f"Adding {len(new_icons)} icon(s) to library: {library_path}")
|
|
136
|
+
|
|
137
|
+
# Load existing icons
|
|
138
|
+
existing_icons = self.load_library(library_path)
|
|
139
|
+
|
|
140
|
+
# Check for duplicate names in existing icons and make them unique
|
|
141
|
+
existing_names = [icon.name for icon in existing_icons]
|
|
142
|
+
if len(existing_names) != len(set(existing_names)):
|
|
143
|
+
# Find duplicates and rename them
|
|
144
|
+
name_counts: dict[str, int] = {}
|
|
145
|
+
renamed_count = 0
|
|
146
|
+
|
|
147
|
+
for i, icon in enumerate(existing_icons):
|
|
148
|
+
if icon.name in name_counts:
|
|
149
|
+
# This is a duplicate - create unique name
|
|
150
|
+
name_counts[icon.name] += 1
|
|
151
|
+
counter = name_counts[icon.name]
|
|
152
|
+
new_name = f"{icon.name}_{counter}"
|
|
153
|
+
|
|
154
|
+
# Ensure the new name is also unique
|
|
155
|
+
while new_name in existing_names or new_name in name_counts:
|
|
156
|
+
counter += 1
|
|
157
|
+
new_name = f"{icon.name}_{counter}"
|
|
158
|
+
|
|
159
|
+
# Update name_counts to reflect the actual counter used
|
|
160
|
+
name_counts[icon.name] = counter
|
|
161
|
+
|
|
162
|
+
# Create new icon with unique name
|
|
163
|
+
existing_icons[i] = DrawIOIcon(
|
|
164
|
+
name=new_name,
|
|
165
|
+
xml_data=icon.xml_data,
|
|
166
|
+
dimensions=icon.dimensions,
|
|
167
|
+
)
|
|
168
|
+
renamed_count += 1
|
|
169
|
+
logger.debug(f"Renamed duplicate icon: {icon.name} -> {new_name}")
|
|
170
|
+
else:
|
|
171
|
+
name_counts[icon.name] = 1
|
|
172
|
+
|
|
173
|
+
logger.info(
|
|
174
|
+
f"Renamed {renamed_count} duplicate icon(s) to preserve all icons from library"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Create a map of existing icons by name (now all unique)
|
|
178
|
+
icon_map = {icon.name: icon for icon in existing_icons}
|
|
179
|
+
|
|
180
|
+
# Add or replace icons
|
|
181
|
+
added_count = 0
|
|
182
|
+
replaced_count = 0
|
|
183
|
+
skipped_count = 0
|
|
184
|
+
|
|
185
|
+
for new_icon in new_icons:
|
|
186
|
+
if new_icon.name in icon_map:
|
|
187
|
+
if replace_duplicates:
|
|
188
|
+
icon_map[new_icon.name] = new_icon
|
|
189
|
+
replaced_count += 1
|
|
190
|
+
logger.debug(f"Replaced icon: {new_icon.name}")
|
|
191
|
+
elif add_duplicates:
|
|
192
|
+
# Find a unique name by appending _2, _3, etc.
|
|
193
|
+
base_name = new_icon.name
|
|
194
|
+
counter = 2
|
|
195
|
+
unique_name = f"{base_name}_{counter}"
|
|
196
|
+
while unique_name in icon_map:
|
|
197
|
+
counter += 1
|
|
198
|
+
unique_name = f"{base_name}_{counter}"
|
|
199
|
+
|
|
200
|
+
# Create new icon with unique name
|
|
201
|
+
unique_icon = DrawIOIcon(
|
|
202
|
+
name=unique_name,
|
|
203
|
+
xml_data=new_icon.xml_data,
|
|
204
|
+
dimensions=new_icon.dimensions,
|
|
205
|
+
)
|
|
206
|
+
icon_map[unique_name] = unique_icon
|
|
207
|
+
added_count += 1
|
|
208
|
+
logger.debug(f"Added duplicate icon with modified name: {unique_name}")
|
|
209
|
+
else:
|
|
210
|
+
skipped_count += 1
|
|
211
|
+
logger.debug(f"Skipped duplicate icon: {new_icon.name}")
|
|
212
|
+
else:
|
|
213
|
+
icon_map[new_icon.name] = new_icon
|
|
214
|
+
added_count += 1
|
|
215
|
+
logger.debug(f"Added icon: {new_icon.name}")
|
|
216
|
+
|
|
217
|
+
logger.info(f"Added: {added_count}, Replaced: {replaced_count}, Skipped: {skipped_count}")
|
|
218
|
+
|
|
219
|
+
# Save updated library
|
|
220
|
+
all_icons = list(icon_map.values())
|
|
221
|
+
return self.create_library(all_icons, library_path)
|
|
222
|
+
|
|
223
|
+
def remove_icons_from_library(
|
|
224
|
+
self, library_path: Path, icon_names: list[str]
|
|
225
|
+
) -> tuple[LibraryMetadata, int]:
|
|
226
|
+
"""Remove icons from a library by name.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
library_path: Path to the library file.
|
|
230
|
+
icon_names: List of icon names to remove.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Tuple of (LibraryMetadata with updated library information, number of icons actually removed).
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
FileNotFoundError: If the library file does not exist.
|
|
237
|
+
ValueError: If the library file is invalid.
|
|
238
|
+
"""
|
|
239
|
+
logger.info(f"Removing {len(icon_names)} icon(s) from library: {library_path}")
|
|
240
|
+
|
|
241
|
+
# Load existing icons
|
|
242
|
+
existing_icons = self.load_library(library_path)
|
|
243
|
+
|
|
244
|
+
# Filter out icons to remove
|
|
245
|
+
names_to_remove = set(icon_names)
|
|
246
|
+
filtered_icons = [icon for icon in existing_icons if icon.name not in names_to_remove]
|
|
247
|
+
|
|
248
|
+
removed_count = len(existing_icons) - len(filtered_icons)
|
|
249
|
+
logger.info(f"Removed {removed_count} icon(s)")
|
|
250
|
+
|
|
251
|
+
# Save updated library
|
|
252
|
+
metadata = self.create_library(filtered_icons, library_path)
|
|
253
|
+
return metadata, removed_count
|
|
254
|
+
|
|
255
|
+
def list_icons(self, library_path: Path) -> list[str]:
|
|
256
|
+
"""List all icon names in a library.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
library_path: Path to the library file.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
List of icon names.
|
|
263
|
+
|
|
264
|
+
Raises:
|
|
265
|
+
FileNotFoundError: If the library file does not exist.
|
|
266
|
+
ValueError: If the library file is invalid.
|
|
267
|
+
"""
|
|
268
|
+
icons = self.load_library(library_path)
|
|
269
|
+
return [icon.name for icon in icons]
|
SVG2DrawIOLib/models.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Data models for SVG to DrawIO conversion."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class SVGDimensions:
|
|
9
|
+
"""Dimensions for an SVG shape in DrawIO.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
width: Width in pixels.
|
|
13
|
+
height: Height in pixels.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
width: float
|
|
17
|
+
height: float
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_max_dimension(cls, max_dimension: float, aspect_ratio: float) -> "SVGDimensions":
|
|
21
|
+
"""Create dimensions from a maximum dimension and aspect ratio.
|
|
22
|
+
|
|
23
|
+
Scales dimensions so the longest side equals max_dimension while
|
|
24
|
+
maintaining the aspect ratio.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
max_dimension: Maximum dimension (width or height) in pixels.
|
|
28
|
+
aspect_ratio: Width/height ratio.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
SVGDimensions with scaled width and height.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
>>> # For a 100x50 image with max_dimension=40
|
|
35
|
+
>>> dims = SVGDimensions.from_max_dimension(40, 2.0)
|
|
36
|
+
>>> dims.width, dims.height
|
|
37
|
+
(40.0, 20.0)
|
|
38
|
+
"""
|
|
39
|
+
if aspect_ratio >= 1.0:
|
|
40
|
+
# Width is longer
|
|
41
|
+
return cls(width=max_dimension, height=max_dimension / aspect_ratio)
|
|
42
|
+
else:
|
|
43
|
+
# Height is longer
|
|
44
|
+
return cls(width=max_dimension * aspect_ratio, height=max_dimension)
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_fixed_dimensions(cls, width: float, height: float) -> "SVGDimensions":
|
|
48
|
+
"""Create dimensions from fixed width and height.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
width: Width in pixels.
|
|
52
|
+
height: Height in pixels.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
SVGDimensions with specified dimensions.
|
|
56
|
+
"""
|
|
57
|
+
return cls(width=width, height=height)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class DrawIOIcon:
|
|
62
|
+
"""Represents a single icon in a DrawIO library.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
name: Icon name (typically filename without extension).
|
|
66
|
+
xml_data: Compressed and encoded XML data.
|
|
67
|
+
dimensions: Icon dimensions.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
name: str
|
|
71
|
+
xml_data: bytes
|
|
72
|
+
dimensions: SVGDimensions
|
|
73
|
+
|
|
74
|
+
def to_dict(self) -> dict[str, str | int | float]:
|
|
75
|
+
"""Convert to DrawIO library JSON format.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Dictionary with DrawIO library format fields.
|
|
79
|
+
"""
|
|
80
|
+
return {
|
|
81
|
+
"xml": self.xml_data.decode("ascii"),
|
|
82
|
+
"w": int(self.dimensions.width),
|
|
83
|
+
"h": int(self.dimensions.height),
|
|
84
|
+
"aspect": "fixed",
|
|
85
|
+
"title": self.name,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class SVGProcessingOptions:
|
|
91
|
+
"""Options for processing SVG files.
|
|
92
|
+
|
|
93
|
+
Attributes:
|
|
94
|
+
add_css: Whether to add CSS classes for color editing.
|
|
95
|
+
css_color: Default CSS fill color.
|
|
96
|
+
xml_namespace: XML namespace for SVG elements.
|
|
97
|
+
css_tag: XML tag to add CSS classes to.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
add_css: bool = False
|
|
101
|
+
css_color: str = "#000000"
|
|
102
|
+
xml_namespace: str = "http://www.w3.org/2000/svg"
|
|
103
|
+
css_tag: str = "path"
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def namespaced_tag(self) -> str:
|
|
107
|
+
"""Get the CSS tag with namespace prefix.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Tag name with namespace in Clark notation.
|
|
111
|
+
"""
|
|
112
|
+
if self.xml_namespace:
|
|
113
|
+
return f"{{{self.xml_namespace}}}{self.css_tag}"
|
|
114
|
+
return self.css_tag
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class LibraryMetadata:
|
|
119
|
+
"""Metadata for a DrawIO library.
|
|
120
|
+
|
|
121
|
+
Attributes:
|
|
122
|
+
name: Library name.
|
|
123
|
+
icon_count: Number of icons in the library.
|
|
124
|
+
source_files: List of source SVG files.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
name: str
|
|
128
|
+
icon_count: int
|
|
129
|
+
source_files: list[Path] = field(default_factory=list)
|