Skip to content

Commit eb228b0

Browse files
authored
add path handling so that clusters/groups will display correctly (#116)
* add path handling so that clusters/groups will display correctly * PR comments, bump version to 1.1 * include path in CouldNotParsePathError * update table test * install diagrams on CI * simplify diagrams test
1 parent eb55e9b commit eb228b0

File tree

15 files changed

+147
-168
lines changed

15 files changed

+147
-168
lines changed

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ jobs:
4141
- uses: psf/black@stable
4242
with:
4343
options: "--check --verbose"
44-
- run: pytest --cov
44+
- run: pip install diagrams && pytest --cov

.idea/misc.xml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

graphviz2drawio/graphviz2drawio.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def convert(
1414
graph_to_convert: AGraph | str | TextIOBase | Path | TextIO,
1515
layout_prog: str = "dot",
1616
) -> str:
17-
graph = _load_pygraphviz_graph(graph_to_convert)
17+
graph = _load_pygraphviz_agraph(graph_to_convert)
1818

1919
graph_edges: dict[str, dict] = {
2020
f"{e[0]}->{e[1]}-"
@@ -49,12 +49,16 @@ def convert(
4949
return mx_graph.value()
5050

5151

52-
def _load_pygraphviz_graph(
52+
def _load_pygraphviz_agraph( # noqa: PLR0911
5353
graph_to_convert: AGraph | str | TextIOBase | Path | TextIO,
5454
) -> AGraph:
5555
if isinstance(graph_to_convert, AGraph):
5656
return graph_to_convert
5757
if isinstance(graph_to_convert, str):
58+
if graph_to_convert.endswith((".dot", ".gv", ".txt")):
59+
return AGraph(filename=graph_to_convert)
60+
if graph_to_convert.endswith(("}", "}\n")):
61+
return AGraph(string=graph_to_convert)
5862
# This fixes a pygraphviz bug where a string beginning with a comment
5963
# is mistakenly identified as a filename.
6064
# https://github.com/pygraphviz/pygraphviz/issues/536
@@ -63,6 +67,7 @@ def _load_pygraphviz_graph(
6367
flags=re.MULTILINE,
6468
)
6569
if pattern.search(graph_to_convert):
70+
# graph_to_convert was a graph / dot string
6671
return AGraph(string=graph_to_convert)
6772
return AGraph(filename=graph_to_convert)
6873
# pyrefly: ignore # missing-attribute

graphviz2drawio/models/Errors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ def __init__(self, message: str) -> None:
1010
super().__init__(message)
1111

1212

13+
class CouldNotParsePathError(GdValueError):
14+
"""Could not parse path from SVG element."""
15+
16+
def __init__(self, path: str) -> None:
17+
super().__init__(
18+
f"Could not parse path from SVG element: {path}",
19+
)
20+
21+
1322
class MissingTitleError(GdValueError):
1423
"""Title missing from SVG element."""
1524

graphviz2drawio/mx/NodeFactory.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
from . import MxConst, Shape
88
from .MxConst import DEFAULT_STROKE_WIDTH
99
from .Node import Gradient, Node
10-
from .RectFactory import rect_from_ellipse_svg, rect_from_image, rect_from_svg_points
10+
from .RectFactory import (
11+
rect_from_ellipse_svg,
12+
rect_from_image,
13+
rect_from_svg_path,
14+
rect_from_svg_points,
15+
)
1116
from .Text import Text
1217
from .utils import adjust_color_opacity
1318

@@ -64,6 +69,11 @@ def from_svg(
6469
stroke_width = ellipse.attrib.get("stroke-width", DEFAULT_STROKE_WIDTH)
6570
if "stroke-dasharray" in ellipse.attrib:
6671
dashed = True
72+
elif (path := SVG.get_first(g, "path")) is not None:
73+
rect = rect_from_svg_path(self.coords, path.attrib["d"])
74+
shape = Shape.RECT
75+
fill = self._extract_fill(path, gradients)
76+
stroke = self._extract_stroke(path)
6777
else:
6878
shape = Shape.ELLIPSE
6979

graphviz2drawio/mx/RectFactory.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
1+
from svg import path as svg_path
2+
13
from ..models.CoordsTranslate import CoordsTranslate
4+
from ..models.Errors import CouldNotParsePathError
25
from ..models.Rect import Rect
36

47

8+
def rect_from_svg_path(coords: CoordsTranslate, path_d: str) -> Rect:
9+
parsed_path: svg_path.Path = svg_path.parse_path(path_d)
10+
if len(parsed_path) == 0 or not isinstance(parsed_path[0], svg_path.Move):
11+
raise CouldNotParsePathError(path_d)
12+
start: svg_path.Move = parsed_path.pop(0)
13+
min_x = start.start.real
14+
min_y = start.start.imag
15+
max_x = start.start.real
16+
max_y = start.start.imag
17+
18+
# Note that this loop may not be accurate since it does not calculate
19+
# the Bezier curve based on the control points
20+
for e in parsed_path:
21+
min_x = min(min_x, e.start.real, e.end.real)
22+
min_y = min(min_y, e.start.imag, e.end.imag)
23+
max_x = max(max_x, e.start.real, e.end.real)
24+
max_y = max(max_y, e.start.imag, e.end.imag)
25+
26+
(x, y) = coords.translate(min_x, min_y)
27+
28+
return Rect(x=x, y=y, width=max_x - min_x, height=max_y - min_y)
29+
30+
531
def rect_from_svg_points(coords: CoordsTranslate, svg: str) -> Rect:
632
"""Don't use this function.
733

graphviz2drawio/mx/Styles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class Styles(Enum):
8181
)
8282
LARROW = "flipH=1;" + RARROW
8383
IMAGE = (
84-
"shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;"
84+
"shape=image;verticalLabelPosition=top;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;"
8585
"imageAspect=0;image={image};" + NODE
8686
)
8787

graphviz2drawio/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.0.0"
1+
__version__ = "1.1.0"
22

33
if __name__ == "__main__":
44
print(__version__)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ line-length = 88
1010
indent-width = 4
1111

1212
lint.select = ["ALL"]
13-
lint.ignore = ["D100", "D101", "D102", "D103", "D104", "D105", "D107", "D203", "D213", "ERA001", "ICN001", "PLR0913", "PT009", "S314", "SIM102", "TD002", "TID252", "N999"]
13+
lint.ignore = ["D100", "D101", "D102", "D103", "D104", "D105", "D107", "D203", "D213", "ERA001", "ICN001", "PLR0913", "PT009", "RSE102", "S314", "SIM102", "TD002", "TID252", "N999"]
1414
target-version = "py310"
1515

1616
[tool.ruff.format]

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@
4141
"Programming Language :: Python :: 3.14",
4242
],
4343
license="GPLv3",
44-
keywords="graphviz graph agraph dot convert conversion draw drawio mxgraph maxgraph",
44+
keywords="graphviz graph agraph dot convert conversion diagrams drawio mxgraph maxgraph Lucidchart",
4545
packages=find_packages(exclude=["doc", "test"]),
4646
python_requires=">=3.10",
4747
install_requires=["puremagic", "pygraphviz", "svg.path"],
48-
tests_require=["pytest"],
48+
tests_require=["diagrams", "pytest"],
4949
entry_points={"console_scripts": ["graphviz2drawio=graphviz2drawio.__main__:main"]},
5050
project_urls={
5151
"Bug Reports": "https://github.com/hbmartin/graphviz2drawio/issues",

0 commit comments

Comments
 (0)