From 58de3deb64298c6d562a930f81a993d4e7303ce2 Mon Sep 17 00:00:00 2001 From: Harold Martin Date: Sat, 6 Jul 2024 13:41:06 -0700 Subject: [PATCH] added MissingTitleError, general improvements to SVG parsing, additional typing --- graphviz2drawio/models/Errors.py | 9 +++-- graphviz2drawio/models/SVG.py | 6 ++- graphviz2drawio/models/SvgParser.py | 3 ++ graphviz2drawio/mx/Edge.py | 2 +- graphviz2drawio/mx/EdgeFactory.py | 8 +++- graphviz2drawio/mx/MxGraph.py | 61 +++++++++++++++-------------- graphviz2drawio/mx/RectFactory.py | 7 ++-- 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/graphviz2drawio/models/Errors.py b/graphviz2drawio/models/Errors.py index b64848b..d1ff0d4 100644 --- a/graphviz2drawio/models/Errors.py +++ b/graphviz2drawio/models/Errors.py @@ -5,8 +5,9 @@ def __init__(self, message: str) -> None: super().__init__(message) -class InvalidCbError(GdValueError): - """Invalid cubic Bézier value.""" +class MissingTitleError(GdValueError): + """Title missing from SVG element.""" - def __init__(self) -> None: - super().__init__("Invalid cubic Bézier value.") + def __init__(self, g) -> None: + super().__init__("Title missing from SVG element.") + self.g = g diff --git a/graphviz2drawio/models/SVG.py b/graphviz2drawio/models/SVG.py index f9a54d0..17ba519 100644 --- a/graphviz2drawio/models/SVG.py +++ b/graphviz2drawio/models/SVG.py @@ -11,8 +11,10 @@ def get_first(g: Element, tag: str) -> Element | None: return g.find(f"./{NS_SVG}{tag}") -def get_title(g: Element) -> str: - return get_first(g, "title").text # pytype: disable=attribute-error +def get_title(g: Element) -> str | None: + if (title_el := get_first(g, "title")) is not None: + return title_el.text # pytype: disable=attribute-error + return None def get_text(g: Element) -> str | None: diff --git a/graphviz2drawio/models/SvgParser.py b/graphviz2drawio/models/SvgParser.py index 4748441..3509f21 100644 --- a/graphviz2drawio/models/SvgParser.py +++ b/graphviz2drawio/models/SvgParser.py @@ -8,6 +8,7 @@ from . import SVG from .CoordsTranslate import CoordsTranslate +from .Errors import MissingTitleError def parse_nodes_edges_clusters( @@ -26,6 +27,8 @@ def parse_nodes_edges_clusters( for g in root: if SVG.is_tag(g, "g"): title = SVG.get_title(g) + if title is None: + raise MissingTitleError(g) if g.attrib["class"] == "node": nodes[title] = node_factory.from_svg(g) elif g.attrib["class"] == "edge": diff --git a/graphviz2drawio/mx/Edge.py b/graphviz2drawio/mx/Edge.py index 9f419e5..d887523 100644 --- a/graphviz2drawio/mx/Edge.py +++ b/graphviz2drawio/mx/Edge.py @@ -12,7 +12,7 @@ def __init__( sid: str, fr: str, to: str, - curve: Curve, + curve: Curve | None, label: str, ) -> None: super().__init__(sid=sid, gid=f"{fr}->{to}") diff --git a/graphviz2drawio/mx/EdgeFactory.py b/graphviz2drawio/mx/EdgeFactory.py index 9f87783..ce8ddeb 100644 --- a/graphviz2drawio/mx/EdgeFactory.py +++ b/graphviz2drawio/mx/EdgeFactory.py @@ -2,6 +2,7 @@ from .CurveFactory import CurveFactory from .Edge import Edge +from ..models.Errors import MissingTitleError class EdgeFactory: @@ -10,11 +11,14 @@ def __init__(self, coords) -> None: self.curve_factory = CurveFactory(coords) def from_svg(self, g) -> Edge: - fr, to = SVG.get_title(g).replace("--", "->").split("->") + title = SVG.get_title(g) + if title is None: + raise MissingTitleError(g) + fr, to = title.replace("--", "->").split("->") fr = fr.split(":")[0] to = to.split(":")[0] curve = None - label = SVG.get_text(g) + label = SVG.get_text(g) or "" if (path := SVG.get_first(g, "path")) is not None: if "d" in path.attrib: curve = self.curve_factory.from_svg(path.attrib["d"]) diff --git a/graphviz2drawio/mx/MxGraph.py b/graphviz2drawio/mx/MxGraph.py index d25bd5d..d3d421a 100644 --- a/graphviz2drawio/mx/MxGraph.py +++ b/graphviz2drawio/mx/MxGraph.py @@ -112,42 +112,43 @@ def add_mx_geo(element, rect=None) -> None: SubElement(element, MxConst.GEO, attributes) @staticmethod - def add_mx_geo_with_points(element: Element, curve: Curve) -> None: + def add_mx_geo_with_points(element: Element, curve: Curve | None) -> None: geo = SubElement( element, MxConst.GEO, attrib={"as": "geometry", "relative": "1"}, ) - SubElement( - geo, - MxConst.POINT, - attrib={ - "x": str(curve.start.real), - "y": str(curve.start.imag), - "as": "sourcePoint", - }, - ) - SubElement( - geo, - MxConst.POINT, - attrib={ - "x": str(curve.end.real), - "y": str(curve.end.imag), - "as": "targetPoint", - }, - ) + if curve is not None: + SubElement( + geo, + MxConst.POINT, + attrib={ + "x": str(curve.start.real), + "y": str(curve.start.imag), + "as": "sourcePoint", + }, + ) + SubElement( + geo, + MxConst.POINT, + attrib={ + "x": str(curve.end.real), + "y": str(curve.end.imag), + "as": "targetPoint", + }, + ) - if len(curve.points) != 0: - array = SubElement(geo, MxConst.ARRAY, {"as": "points"}) - for point in curve.points: - SubElement( - array, - MxConst.POINT, - attrib={ - "x": str(point.real), - "y": str(point.imag), - }, - ) + if len(curve.points) != 0: + array = SubElement(geo, MxConst.ARRAY, {"as": "points"}) + for point in curve.points: + SubElement( + array, + MxConst.POINT, + attrib={ + "x": str(point.real), + "y": str(point.imag), + }, + ) @staticmethod def x_y_strs(point: complex) -> tuple[str, str]: diff --git a/graphviz2drawio/mx/RectFactory.py b/graphviz2drawio/mx/RectFactory.py index b7ab98b..7019aab 100644 --- a/graphviz2drawio/mx/RectFactory.py +++ b/graphviz2drawio/mx/RectFactory.py @@ -3,11 +3,10 @@ def rect_from_svg_points(coords: CoordsTranslate, svg: str) -> Rect: - points = svg.split(" ") - points = [coords.translate(*p.split(",")) for p in points] + points: list[tuple[float, float]] = [coords.translate(*p.split(",")) for p in svg.split(" ")] min_x, min_y = points[0] - width = 0 - height = 0 + width = 0.0 + height = 0.0 for p in points: if p[0] < min_x: min_x = p[0]