Skip to content

Commit

Permalink
add support for graphviz builtin shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
hbmartin committed Sep 5, 2018
1 parent f507870 commit 13490c6
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 25 deletions.
1 change: 1 addition & 0 deletions graphviz2drawio/mx/MxConst.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

DEFAULT_TEXT_SIZE = 10
DEFAULT_FONT_FAMILY = "Helvetica"
DEFAULT_FONT_COLOR = "#000000"

DEFAUT_STROKE = "#000000"
DEFAUT_FILL = "#ffffff"
Expand Down
15 changes: 8 additions & 7 deletions graphviz2drawio/mx/MxGraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from graphviz2drawio.models import DotAttr
from graphviz2drawio.mx import MxConst
from graphviz2drawio.mx.Shape import Shape
from graphviz2drawio.mx.Styles import Styles


Expand All @@ -22,11 +21,10 @@ def __init__(self, nodes, edges):
self.add_edge(edge)

def add_edge(self, edge):
end_arrow = MxConst.NONE
end_arrow = MxConst.BLOCK
end_fill = 1
dashed = 1 if edge.style == DotAttr.DASHED else 0
if edge.arrowtail is not None:
end_arrow = MxConst.BLOCK
tail = edge.arrowtail
if edge.arrowtail[0] == DotAttr.NO_FILL:
end_fill = 0
Expand Down Expand Up @@ -68,7 +66,7 @@ def add_edge(self, edge):
self.add_mx_geo(edge_element)

def edge_reposition(self, edges):
# TODO: this needs to be smarter
# TODO: https://github.com/hbmartin/graphviz2drawio/issues/7
edge_to = {}
for edge in edges:
if edge.to not in edge_to:
Expand Down Expand Up @@ -96,9 +94,12 @@ def add_node(self, node):
else MxConst.DEFAUT_FILL
)
stroke = node.stroke if node.stroke is not None else MxConst.DEFAUT_STROKE
style = Styles.NODE.format(fill=fill, stroke=stroke)
if node.shape == Shape.ELLIPSE:
style = Shape.ELLIPSE.value + ";" + style

if node.shape is not None:
style = Styles.get_for_shape(node.shape).format(fill=fill, stroke=stroke)
else:
style = Styles.NODE.format(fill=fill, stroke=stroke)

node_element = ET.SubElement(
self.root,
MxConst.CELL,
Expand Down
4 changes: 2 additions & 2 deletions graphviz2drawio/mx/Node.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@


class Node(GraphObj):
def __init__(self, sid, gid, rect, texts, fill, stroke, shape):
def __init__(self, sid, gid, rect, texts, fill, stroke):
super(Node, self).__init__(sid, gid)
self.rect = rect
self.texts = texts
self.fill = fill
self.stroke = stroke
self.label = None
self.shape = shape
self.shape = None

def text_to_mx_value(self):
value = ""
Expand Down
11 changes: 4 additions & 7 deletions graphviz2drawio/mx/NodeFactory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from graphviz2drawio.models import SVG
from graphviz2drawio.models.Rect import Rect
from graphviz2drawio.mx.Shape import Shape
from .Node import Node
from .Text import Text

Expand All @@ -11,7 +10,6 @@ def __init__(self, coords):

def rect_from_svg_points(self, svg):
points = svg.split(" ")
assert len(points) == 5
points = [self.coords.translate(*p.split(",")) for p in points]
min_x, min_y = points[0]
width = 0
Expand Down Expand Up @@ -57,14 +55,14 @@ def from_svg(self, g):
rect = self.rect_from_svg_points(
SVG.get_first(g, "polygon").attrib["points"]
)
shape = Shape.RECT
else:
rect = self.rect_from_ellipse_svg(SVG.get_first(g, "ellipse").attrib)
shape = Shape.ELLIPSE

stroke = None
if "stroke" in g.attrib:
stroke = g.attrib["stroke"]
if SVG.has(g, "polygon"):
polygon = SVG.get_first(g, "polygon")
if "stroke" in polygon.attrib:
stroke = polygon.attrib["stroke"]
fill = None
if "fill" in g.attrib:
fill = g.attrib["fill"]
Expand All @@ -75,5 +73,4 @@ def from_svg(self, g):
texts=texts,
fill=fill,
stroke=stroke,
shape=shape,
)
43 changes: 37 additions & 6 deletions graphviz2drawio/mx/Shape.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,37 @@
from enum import Enum


class Shape(Enum):
RECT = "rect"
ELLIPSE = "ellipse"
ELLIPSE = "ellipse"
OVAL = "oval"
BOX = "box"
RECT = "rect"
RECTANGLE = "rectangle"
HEXAGON = "hexagon"
POLYGON = "polygon"
CIRCLE = "circle"
EGG = "egg"
TRIANGLE = "triangle"
PLAIN = "plain"
DIAMOND = "diamond"
TRAPEZIUM = "trapezium"
PARALLELOGRAM = "parallelogram"
HOUSE = "house"
PENTAGON = "pentagon"
OCTAGON = "octagon"
DOUBLE_CIRCLE = "doublecircle"
DOUBLE_OCTAGON = "doubleoctagon"
INV_TRIANGLE = "invtriangle"
INV_TRAPEZIUM = "invtrapezium"
INV_HOUSE = "invhouse"
SQUARE = "square"
STAR = "star"
UNDERLINE = "underline"
CYLINDER = "cylinder"
NOTE = "note"
TAB = "tab"
FOLDER = "folder"
BOX_3D = "box3d"
COMPONENT = "component"
PROMOTER = "promoter"
RPROMOTER = "rpromoter"
LPROMOTER = "lpromoter"
CDS = "cds"
RARROW = "rarrow"
LARROW = "larrow"
102 changes: 102 additions & 0 deletions graphviz2drawio/mx/Styles.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,112 @@
from enum import Enum
from . import Shape


class Styles(Enum):
NODE = "verticalAlign=top;align=left;overflow=fill;html=1;rounded=0;shadow=0;comic=0;labelBackgroundColor=none;strokeColor={stroke};strokeWidth=1;fillColor={fill};"
EDGE = "edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;exitX={exit_x:.3g};exitY={exit_y:.3g};entryX={entry_x:.3g};entryY={entry_y:.3g};jettySize=auto;orthogonalLoop=1;endArrow={end_arrow};dashed={dashed};endFill={end_fill};"
TEXT = "margin:0px;text-align:{align};{margin};font-size:{size}px;font-family:{family};color:{color};"

ELLIPSE = "ellipse;" + NODE
CIRCLE = "ellipse;aspect=fixed;" + NODE
HEXAGON = "shape=hexagon;perimeter=hexagonPerimeter2;" + NODE
EGG = "shape=mxgraph.flowchart.display;direction=south;" + NODE
TRIANGLE = "triangle;direction=north;" + NODE
LINE = "line;strokeWidth=2;verticalAlign=bottom;labelPosition=center;verticalLabelPosition=top;align=center;" + NODE
DIAMOND = "rhombus;" + NODE
TRAPEZOID = "shape=trapezoid;perimeter=trapezoidPerimeter;" + NODE
PARALLELOGRAM = "shape=parallelogram;perimeter=parallelogramPerimeter;" + NODE
HOUSE = "shape=offPageConnector;direction=west;" + NODE
PENTAGON = "shape=mxgraph.basic.pentagon;" + NODE
OCTAGON = "shape=mxgraph.basic.octagon2;align=center;verticalAlign=middle;dx=15;" + NODE
DOUBLE_CIRCLE = "ellipse;shape=doubleEllipse;aspect=fixed;" + NODE
DOUBLE_OCTAGON = "shape=image;html=1;verticalAlign=middle;verticalLabelPosition=middle;imageAspect=0;aspect=fixed;image=https://cdn4.iconfinder.com/data/icons/feather/24/octagon-128.png;labelPosition=center;align=center;" + NODE
INV_TRIANGLE = "triangle;direction=south;" + NODE
INV_TRAPEZOID = "shape=trapezoid;perimeter=trapezoidPerimeter;direction=west;" + NODE
INV_HOUSE = "shape=offPageConnector;direction=east;" + NODE
SQUARE = "aspect=fixed;" + NODE
STAR = "shape=mxgraph.basic.star;labelPosition=center;align=center;verticalLabelPosition=middle;verticalAlign=middle;" + NODE
UNDERLINE = "line;strokeWidth=2;verticalAlign=bottom;labelPosition=center;verticalLabelPosition=top;align=center;"
CYLINDER = "shape=cylinder;boundedLbl=1;backgroundOutline=1;" + NODE
NOTE = "shape=note;backgroundOutline=1;" + NODE
TAB = "shape=folder;tabWidth=40;tabHeight=14;tabPosition=left;" + NODE
FOLDER = "shape=mxgraph.office.concepts.folder;outlineConnect=0;align=center;verticalLabelPosition=middle;verticalAlign=middle;labelPosition=center;shadow=0;dashed=0;" + NODE
CUBE = "shape=cube;boundedLbl=1;backgroundOutline=1;" + NODE
COMPONENT = "shape=component;align=center;spacingLeft=36;verticalAlign=bottom;" + NODE
RPROMOTER = "shape=mxgraph.arrows2.bendArrow;dy=15;dx=38;notch=0;arrowHead=55;rounded=0;shadow=0;dashed=0;align=center;verticalAlign=middle;" + NODE
LPROMOTER = "flipH=1;" + RPROMOTER
CDS = "shape=mxgraph.arrows2.arrow;dy=0;dx=10;notch=0;shadow=0;dashed=0;align=center;verticalAlign=middle;" + NODE
RARROW = "shape=mxgraph.arrows2.arrow;dy=0.6;dx=40;align=center;labelPosition=center;notch=0;strokeWidth=2;verticalLabelPosition=middle;verticalAlign=middle;" + NODE
LARROW = "flipH=1;" + RARROW

@staticmethod
def get_for_shape(dot_shape):
if dot_shape == Shape.ELLIPSE or dot_shape == Shape.OVAL:
return Styles.ELLIPSE
elif dot_shape == Shape.BOX or dot_shape == Shape.RECT or dot_shape == Shape.RECTANGLE:
return Styles.NODE
elif dot_shape == Shape.HEXAGON or dot_shape == Shape.POLYGON:
return Styles.HEXAGON
elif dot_shape == Shape.CIRCLE:
return Styles.CIRCLE
elif dot_shape == Shape.EGG:
return Styles.EGG
elif dot_shape == Shape.TRIANGLE:
return Styles.TRIANGLE
elif dot_shape == Shape.PLAIN:
return Styles.LINE
elif dot_shape == Shape.DIAMOND:
return Styles.DIAMOND
elif dot_shape == Shape.TRAPEZIUM:
return Styles.TRAPEZOID
elif dot_shape == Shape.PARALLELOGRAM:
return Styles.PARALLELOGRAM
elif dot_shape == Shape.HOUSE:
return Styles.HOUSE
elif dot_shape == Shape.PENTAGON:
return Styles.PENTAGON
elif dot_shape == Shape.OCTAGON:
return Styles.OCTAGON
elif dot_shape == Shape.DOUBLE_CIRCLE:
return Styles.DOUBLE_CIRCLE
elif dot_shape == Shape.DOUBLE_OCTAGON:
return Styles.DOUBLE_OCTAGON
elif dot_shape == Shape.INV_TRIANGLE:
return Styles.INV_TRIANGLE
elif dot_shape == Shape.INV_TRAPEZIUM:
return Styles.INV_TRAPEZOID
elif dot_shape == Shape.INV_HOUSE:
return Styles.INV_HOUSE
elif dot_shape == Shape.SQUARE:
return Styles.SQUARE
elif dot_shape == Shape.STAR:
return Styles.STAR
elif dot_shape == Shape.UNDERLINE:
return Styles.UNDERLINE
elif dot_shape == Shape.CYLINDER:
return Styles.CYLINDER
elif dot_shape == Shape.NOTE:
return Styles.NODE
elif dot_shape == Shape.TAB:
return Styles.TAB
elif dot_shape == Shape.FOLDER:
return Styles.FOLDER
elif dot_shape == Shape.BOX_3D:
return Styles.CUBE
elif dot_shape == Shape.COMPONENT:
return Styles.COMPONENT
elif dot_shape == Shape.PROMOTER or dot_shape == Shape.RPROMOTER:
return Styles.RPROMOTER
elif dot_shape == Shape.LPROMOTER:
return Styles.LPROMOTER
elif dot_shape == Shape.CDS:
return Styles.CDS
elif dot_shape == Shape.RARROW:
return Styles.RARROW
elif dot_shape == Shape.LARROW:
return Styles.LARROW
else:
return Styles.NODE

def format(self, **values):
return self.value.format(**values)
4 changes: 2 additions & 2 deletions graphviz2drawio/mx/Text.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_mx_style(self):
margin=margin,
size=rescaled_size,
family=self.family or MxConst.DEFAULT_FONT_FAMILY,
color=self.color,
color=self.color or MxConst.DEFAULT_FONT_COLOR,
)

@staticmethod
Expand All @@ -32,5 +32,5 @@ def from_svg(t):
anchor=t.attrib["text-anchor"],
family=t.attrib["font-family"],
size=float(t.attrib["font-size"]),
color=t.attrib["fill"],
color=t.attrib["fill"] or None,

This comment has been minimized.

Copy link
@jcaplan

jcaplan Sep 5, 2018

Contributor

I think this is still broken. You need to do t.attrib.get('fill'). You still get a key error with t.attrib['fill'] or None since fill is not in the dict. I'm not sure why this is happening, possibly different versions of graphviz. It's not happening on travis either but still happening for me.

)
2 changes: 1 addition & 1 deletion graphviz2drawio/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.0.4"
__version__ = "0.0.5"

0 comments on commit 13490c6

Please sign in to comment.